android13/packages/services/Car/cpp/telemetry/cartelemetryd/src/TelemetryServer.cpp

180 lines
6.7 KiB
C++

/*
* 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.
*/
#include "TelemetryServer.h"
#include "CarTelemetryImpl.h"
#include "RingBuffer.h"
#include <aidl/android/automotive/telemetry/internal/CarDataInternal.h>
#include <android-base/logging.h>
#include <inttypes.h> // for PRIu64 and friends
#include <cstdint>
#include <memory>
namespace android {
namespace automotive {
namespace telemetry {
namespace {
using ::aidl::android::automotive::telemetry::internal::CarDataInternal;
using ::aidl::android::automotive::telemetry::internal::ICarDataListener;
using ::aidl::android::frameworks::automotive::telemetry::CarData;
using ::android::base::Error;
using ::android::base::Result;
constexpr int kMsgPushCarDataToListener = 1;
// If ICarDataListener cannot accept data, the next push should be delayed little bit to allow
// the listener to recover.
constexpr const std::chrono::seconds kPushCarDataFailureDelaySeconds = 1s;
} // namespace
TelemetryServer::TelemetryServer(LooperWrapper* looper,
const std::chrono::nanoseconds& pushCarDataDelayNs,
const int maxBufferSize) :
mLooper(looper),
mPushCarDataDelayNs(pushCarDataDelayNs),
mRingBuffer(maxBufferSize),
mMessageHandler(new MessageHandlerImpl(this)) {}
void TelemetryServer::setListener(const std::shared_ptr<ICarDataListener>& listener) {
const std::scoped_lock<std::mutex> lock(mMutex);
mCarDataListener = listener;
mLooper->sendMessageDelayed(mPushCarDataDelayNs.count(), mMessageHandler,
kMsgPushCarDataToListener);
}
void TelemetryServer::clearListener() {
const std::scoped_lock<std::mutex> lock(mMutex);
if (mCarDataListener == nullptr) {
return;
}
mCarDataListener = nullptr;
mLooper->removeMessages(mMessageHandler, kMsgPushCarDataToListener);
}
void TelemetryServer::addCarDataIds(const std::vector<int32_t>& ids) {
const std::scoped_lock<std::mutex> lock(mMutex);
mCarDataIds.insert(ids.cbegin(), ids.cend());
}
void TelemetryServer::removeCarDataIds(const std::vector<int32_t>& ids) {
const std::scoped_lock<std::mutex> lock(mMutex);
for (int32_t id : ids) {
mCarDataIds.erase(id);
}
}
std::shared_ptr<ICarDataListener> TelemetryServer::getListener() {
const std::scoped_lock<std::mutex> lock(mMutex);
return mCarDataListener;
}
void TelemetryServer::dump(int fd) {
const std::scoped_lock<std::mutex> lock(mMutex);
dprintf(fd, " TelemetryServer:\n");
mRingBuffer.dump(fd);
}
// TODO(b/174608802): Add 10kb size check for the `dataList`, see the AIDL for the limits
void TelemetryServer::writeCarData(const std::vector<CarData>& dataList, uid_t publisherUid) {
const std::scoped_lock<std::mutex> lock(mMutex);
bool bufferWasEmptyBefore = mRingBuffer.size() == 0;
for (auto&& data : dataList) {
// ignore data that has no subscribers in CarTelemetryService
if (mCarDataIds.find(data.id) == mCarDataIds.end()) {
LOG(VERBOSE) << "Ignoring CarData with ID=" << data.id;
continue;
}
mRingBuffer.push({.mId = data.id,
.mContent = std::move(data.content),
.mPublisherUid = publisherUid});
}
// If the mRingBuffer was not empty, the message is already scheduled. It prevents scheduling
// too many unnecessary idendical messages in the looper.
if (mCarDataListener != nullptr && bufferWasEmptyBefore && mRingBuffer.size() > 0) {
mLooper->sendMessageDelayed(mPushCarDataDelayNs.count(), mMessageHandler,
kMsgPushCarDataToListener);
}
}
// Runs on the main thread.
void TelemetryServer::pushCarDataToListeners() {
std::vector<CarDataInternal> pendingCarDataInternals;
{
const std::scoped_lock<std::mutex> lock(mMutex);
// Remove extra messages.
mLooper->removeMessages(mMessageHandler, kMsgPushCarDataToListener);
if (mCarDataListener == nullptr || mRingBuffer.size() == 0) {
return;
}
// Push elements to pendingCarDataInternals in reverse order so we can send data
// from the back of the pendingCarDataInternals vector.
while (mRingBuffer.size() > 0) {
auto carData = std::move(mRingBuffer.popBack());
CarDataInternal data;
data.id = carData.mId;
data.content = std::move(carData.mContent);
pendingCarDataInternals.push_back(data);
}
}
// TODO(b/186477983): send data in batch to improve performance, but careful sending too
// many data at once, as it could clog the Binder - it has <1MB limit.
while (!pendingCarDataInternals.empty()) {
ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
{
const std::scoped_lock<std::mutex> lock(mMutex);
if (mCarDataListener != nullptr) {
status = mCarDataListener->onCarDataReceived({pendingCarDataInternals.back()});
} else {
status = ndk::ScopedAStatus::
fromServiceSpecificErrorWithMessage(EX_NULL_POINTER,
"mCarDataListener is currently set to "
"null, will try again.");
}
}
if (!status.isOk()) {
LOG(WARNING) << "Failed to push CarDataInternal, will try again: "
<< status.getMessage();
sleep(kPushCarDataFailureDelaySeconds.count());
} else {
pendingCarDataInternals.pop_back();
}
}
}
TelemetryServer::MessageHandlerImpl::MessageHandlerImpl(TelemetryServer* server) :
mTelemetryServer(server) {}
void TelemetryServer::MessageHandlerImpl::handleMessage(const Message& message) {
switch (message.what) {
case kMsgPushCarDataToListener:
mTelemetryServer->pushCarDataToListeners();
break;
default:
LOG(WARNING) << "Unknown message: " << message.what;
}
}
} // namespace telemetry
} // namespace automotive
} // namespace android