• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "EvsStats.h"
17 
18 #include "packages/services/Car/cpp/telemetry/proto/evs.pb.h"
19 
20 #include <aidl/android/frameworks/automotive/telemetry/CarData.h>
21 #include <aidl/android/frameworks/automotive/telemetry/ICarTelemetry.h>
22 #include <android-base/logging.h>
23 #include <android/binder_manager.h>
24 #include <utils/SystemClock.h>
25 
26 #include <vector>
27 
28 namespace {
29 
30 using ::aidl::android::frameworks::automotive::telemetry::CarData;
31 using ::aidl::android::frameworks::automotive::telemetry::ICarTelemetry;
32 using ::android::automotive::telemetry::EvsFirstFrameLatency;
33 
34 // Name of ICarTelemetry service that consumes RVC latency metrics.
35 constexpr const char kCarTelemetryServiceName[] =
36         "android.frameworks.automotive.telemetry.ICarTelemetry/default";
37 
38 const int kCollectedDataSizeLimit = 200;  // arbitrary chosen
39 // 10kb limit is imposed by ICarTelemetry, the limit if only for
40 // CarData.content vector.
41 const int kCarTelemetryMaxDataSizePerWrite = 10 * 1024;
42 
43 // Defined in packages/services/Car/cpp/telemetry/proto/CarData.proto
44 const int kEvsFirstFrameLatencyId = 1;
45 }  // namespace
46 
47 // static
build()48 EvsStats EvsStats::build() {
49     // No need to enable stats if ICarTelemetry is not available.
50     bool enabled = ::AServiceManager_isDeclared(kCarTelemetryServiceName);
51     return EvsStats(enabled);
52 }
53 
startComputingFirstFrameLatency(int64_t startTimeMillis)54 void EvsStats::startComputingFirstFrameLatency(int64_t startTimeMillis) {
55     mFirstFrameLatencyStartTimeMillis = startTimeMillis;
56 }
57 
finishComputingFirstFrameLatency(int64_t finishTimeMillis)58 void EvsStats::finishComputingFirstFrameLatency(int64_t finishTimeMillis) {
59     if (!mEnabled) {
60         return;
61     }
62     if (mFirstFrameLatencyStartTimeMillis == EvsStatsState::NOT_STARTED) {
63         LOG(WARNING) << __func__ << "EvsStats received finishComputingFirstFrameLatency, but "
64                      << "startComputingFirstFrameLatency was not called before.";
65         return;
66     }
67     auto firstFrameLatencyMillis = finishTimeMillis - mFirstFrameLatencyStartTimeMillis;
68     mFirstFrameLatencyStartTimeMillis = EvsStatsState::NOT_STARTED;
69 
70     LOG(DEBUG) << __func__ << ": firstFrameLatencyMillis = " << firstFrameLatencyMillis;
71 
72     EvsFirstFrameLatency latency;
73     latency.set_start_timestamp_millis(mFirstFrameLatencyStartTimeMillis);
74     latency.set_latency_millis(firstFrameLatencyMillis);
75     std::vector<uint8_t> bytes(latency.ByteSize());
76     latency.SerializeToArray(&bytes[0], latency.ByteSize());
77     CarData msg;
78     msg.id = kEvsFirstFrameLatencyId;
79     msg.content = std::move(bytes);
80 
81     if (msg.content.size() > kCarTelemetryMaxDataSizePerWrite) {
82         LOG(WARNING) << __func__ << "finishComputingFirstFrameLatency is trying to store data "
83                      << "with size " << msg.content.size() << ", which is larger than allowed "
84                      << kCarTelemetryMaxDataSizePerWrite;
85         return;
86     }
87 
88     mCollectedData.push_back(msg);
89 
90     while (mCollectedData.size() > kCollectedDataSizeLimit) {
91         mCollectedData.pop_front();
92     }
93 
94     sendCollectedDataUnsafe(/* waitIfNotReady= */ false);
95 }
96 
sendCollectedDataBlocking()97 void EvsStats::sendCollectedDataBlocking() {
98     if (!mEnabled || mCollectedData.empty()) {
99         return;
100     }
101     sendCollectedDataUnsafe(/* waitIfNotReady= */ true);
102 }
103 
getCarTelemetry(bool waitIfNotReady)104 std::shared_ptr<ICarTelemetry> EvsStats::getCarTelemetry(bool waitIfNotReady) {
105     {
106         const std::scoped_lock<std::mutex> lock(mMutex);
107         if (mCarTelemetry != nullptr) {
108             return mCarTelemetry;
109         }
110     }
111 
112     AIBinder* binder;
113     if (waitIfNotReady) {
114         binder = ::AServiceManager_getService(kCarTelemetryServiceName);
115     } else {
116         binder = ::AServiceManager_checkService(kCarTelemetryServiceName);
117     }
118 
119     if (binder == nullptr) {
120         LOG(WARNING) << __func__ << ": ICarTelemetry is not ready";
121         return nullptr;
122     }
123 
124     const std::scoped_lock<std::mutex> lock(mMutex);  // locks until the end of the method
125     mCarTelemetry = ICarTelemetry::fromBinder(ndk::SpAIBinder(binder));
126     auto status = ndk::ScopedAStatus::fromStatus(
127             ::AIBinder_linkToDeath(mCarTelemetry->asBinder().get(), mBinderDeathRecipient.get(),
128                                    this));
129     if (!status.isOk()) {
130         LOG(WARNING) << __func__
131                      << ": Failed to linkToDeath, continuing anyway: " << status.getMessage();
132     }
133     return mCarTelemetry;
134 }
135 
sendCollectedDataUnsafe(bool waitIfNotReady)136 void EvsStats::sendCollectedDataUnsafe(bool waitIfNotReady) {
137     std::shared_ptr<ICarTelemetry> telemetry = getCarTelemetry(waitIfNotReady);
138     if (telemetry == nullptr) {
139         LOG(INFO) << __func__ << ": CarTelemetry is not ready, ignoring";
140         return;
141     }
142 
143     // Send data chunk by chnk, because Binder has transfer data size limit.
144     // Adds the oldest elements to `sendingData` and tries to push ICarTelemetryService.
145     // If successful, erases then from `mCollectedData`, otherwise leaves them there to try again
146     // later.
147     while (!mCollectedData.empty()) {
148         int sendingDataSizeBytes = 0;
149         std::vector<CarData> sendingData;
150         auto it = mCollectedData.begin();
151         while (it != mCollectedData.end()) {
152             sendingDataSizeBytes += it->content.size();
153             if (sendingDataSizeBytes > kCarTelemetryMaxDataSizePerWrite) {
154                 break;
155             }
156             sendingData.push_back(*it);
157             it++;
158         }
159         ndk::ScopedAStatus status = telemetry->write(sendingData);
160         if (!status.isOk()) {
161             LOG(WARNING) << __func__
162                          << "Failed to write data to ICarTelemetry: " << status.getMessage();
163             return;
164         }
165         mCollectedData.erase(mCollectedData.begin(), it);
166     }
167 }
168 
169 // Removes the listener if its binder dies.
telemetryBinderDiedImpl()170 void EvsStats::telemetryBinderDiedImpl() {
171     LOG(WARNING) << __func__ << "ICarTelemetry service died, resetting the state";
172     const std::scoped_lock<std::mutex> lock(mMutex);
173     mCarTelemetry = nullptr;
174 }
175 
176 // static
telemetryBinderDied(void * cookie)177 void EvsStats::telemetryBinderDied(void* cookie) {
178     // We expect the pointer to be alive as there is only a single instance of
179     // EvsStats and if EvsStats is destructed, then the whole evs_app should be dead too.
180     auto thiz = static_cast<EvsStats*>(cookie);
181     thiz->telemetryBinderDiedImpl();
182 }
183