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
17 #include "SubscriptionManager.h"
18
19 #include <android-base/stringprintf.h>
20 #include <utils/Log.h>
21 #include <utils/SystemClock.h>
22
23 #include <inttypes.h>
24
25 namespace android {
26 namespace hardware {
27 namespace automotive {
28 namespace vehicle {
29
30 namespace {
31
32 constexpr float ONE_SECOND_IN_NANO = 1'000'000'000.;
33
34 } // namespace
35
36 using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
37 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
38 using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
39 using ::aidl::android::hardware::automotive::vehicle::VehiclePropError;
40 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
41 using ::android::base::Error;
42 using ::android::base::Result;
43 using ::android::base::StringPrintf;
44 using ::ndk::ScopedAStatus;
45
SubscriptionManager(IVehicleHardware * vehicleHardware)46 SubscriptionManager::SubscriptionManager(IVehicleHardware* vehicleHardware)
47 : mVehicleHardware(vehicleHardware) {}
48
~SubscriptionManager()49 SubscriptionManager::~SubscriptionManager() {
50 std::scoped_lock<std::mutex> lockGuard(mLock);
51
52 mClientsByPropIdArea.clear();
53 mSubscribedPropsByClient.clear();
54 }
55
checkSampleRateHz(float sampleRateHz)56 bool SubscriptionManager::checkSampleRateHz(float sampleRateHz) {
57 return getIntervalNanos(sampleRateHz).ok();
58 }
59
getIntervalNanos(float sampleRateHz)60 Result<int64_t> SubscriptionManager::getIntervalNanos(float sampleRateHz) {
61 int64_t intervalNanos = 0;
62 if (sampleRateHz <= 0) {
63 return Error() << "invalid sample rate, must be a positive number";
64 }
65 if (sampleRateHz <= (ONE_SECOND_IN_NANO / static_cast<float>(INT64_MAX))) {
66 return Error() << "invalid sample rate: " << sampleRateHz << ", too small";
67 }
68 intervalNanos = static_cast<int64_t>(ONE_SECOND_IN_NANO / sampleRateHz);
69 return intervalNanos;
70 }
71
refreshMaxSampleRateHz()72 void ContSubConfigs::refreshMaxSampleRateHz() {
73 float maxSampleRateHz = 0.;
74 // This is not called frequently so a brute-focre is okay. More efficient way exists but this
75 // is simpler.
76 for (const auto& [_, sampleRateHz] : mSampleRateHzByClient) {
77 if (sampleRateHz > maxSampleRateHz) {
78 maxSampleRateHz = sampleRateHz;
79 }
80 }
81 mMaxSampleRateHz = maxSampleRateHz;
82 }
83
addClient(const ClientIdType & clientId,float sampleRateHz)84 void ContSubConfigs::addClient(const ClientIdType& clientId, float sampleRateHz) {
85 mSampleRateHzByClient[clientId] = sampleRateHz;
86 refreshMaxSampleRateHz();
87 }
88
removeClient(const ClientIdType & clientId)89 void ContSubConfigs::removeClient(const ClientIdType& clientId) {
90 mSampleRateHzByClient.erase(clientId);
91 refreshMaxSampleRateHz();
92 }
93
getMaxSampleRateHz() const94 float ContSubConfigs::getMaxSampleRateHz() const {
95 return mMaxSampleRateHz;
96 }
97
addContinuousSubscriberLocked(const ClientIdType & clientId,const PropIdAreaId & propIdAreaId,float sampleRateHz)98 VhalResult<void> SubscriptionManager::addContinuousSubscriberLocked(
99 const ClientIdType& clientId, const PropIdAreaId& propIdAreaId, float sampleRateHz) {
100 // Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases.
101 ContSubConfigs newConfig = mContSubConfigsByPropIdArea[propIdAreaId];
102 newConfig.addClient(clientId, sampleRateHz);
103 return updateContSubConfigs(propIdAreaId, newConfig);
104 }
105
removeContinuousSubscriberLocked(const ClientIdType & clientId,const PropIdAreaId & propIdAreaId)106 VhalResult<void> SubscriptionManager::removeContinuousSubscriberLocked(
107 const ClientIdType& clientId, const PropIdAreaId& propIdAreaId) {
108 // Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases.
109 ContSubConfigs newConfig = mContSubConfigsByPropIdArea[propIdAreaId];
110 newConfig.removeClient(clientId);
111 return updateContSubConfigs(propIdAreaId, newConfig);
112 }
113
updateContSubConfigs(const PropIdAreaId & propIdAreaId,const ContSubConfigs & newConfig)114 VhalResult<void> SubscriptionManager::updateContSubConfigs(const PropIdAreaId& propIdAreaId,
115 const ContSubConfigs& newConfig) {
116 if (newConfig.getMaxSampleRateHz() ==
117 mContSubConfigsByPropIdArea[propIdAreaId].getMaxSampleRateHz()) {
118 mContSubConfigsByPropIdArea[propIdAreaId] = newConfig;
119 return {};
120 }
121 float newRateHz = newConfig.getMaxSampleRateHz();
122 int32_t propId = propIdAreaId.propId;
123 int32_t areaId = propIdAreaId.areaId;
124 if (auto status = mVehicleHardware->updateSampleRate(propId, areaId, newRateHz);
125 status != StatusCode::OK) {
126 return StatusError(status) << StringPrintf("failed to update sample rate for prop: %" PRId32
127 ", area"
128 ": %" PRId32 ", sample rate: %f HZ",
129 propId, areaId, newRateHz);
130 }
131 mContSubConfigsByPropIdArea[propIdAreaId] = newConfig;
132 return {};
133 }
134
subscribe(const std::shared_ptr<IVehicleCallback> & callback,const std::vector<SubscribeOptions> & options,bool isContinuousProperty)135 VhalResult<void> SubscriptionManager::subscribe(const std::shared_ptr<IVehicleCallback>& callback,
136 const std::vector<SubscribeOptions>& options,
137 bool isContinuousProperty) {
138 std::scoped_lock<std::mutex> lockGuard(mLock);
139
140 for (const auto& option : options) {
141 float sampleRateHz = option.sampleRate;
142
143 if (isContinuousProperty) {
144 if (auto result = getIntervalNanos(sampleRateHz); !result.ok()) {
145 return StatusError(StatusCode::INVALID_ARG) << result.error().message();
146 }
147 }
148
149 if (option.areaIds.empty()) {
150 ALOGE("area IDs to subscribe must not be empty");
151 return StatusError(StatusCode::INVALID_ARG)
152 << "area IDs to subscribe must not be empty";
153 }
154 }
155
156 ClientIdType clientId = callback->asBinder().get();
157
158 for (const auto& option : options) {
159 int32_t propId = option.propId;
160 const std::vector<int32_t>& areaIds = option.areaIds;
161 for (int32_t areaId : areaIds) {
162 PropIdAreaId propIdAreaId = {
163 .propId = propId,
164 .areaId = areaId,
165 };
166 if (isContinuousProperty) {
167 if (auto result = addContinuousSubscriberLocked(clientId, propIdAreaId,
168 option.sampleRate);
169 !result.ok()) {
170 return result;
171 }
172 }
173
174 mSubscribedPropsByClient[clientId].insert(propIdAreaId);
175 mClientsByPropIdArea[propIdAreaId][clientId] = callback;
176 }
177 }
178 return {};
179 }
180
unsubscribe(SubscriptionManager::ClientIdType clientId,const std::vector<int32_t> & propIds)181 VhalResult<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId,
182 const std::vector<int32_t>& propIds) {
183 std::scoped_lock<std::mutex> lockGuard(mLock);
184
185 if (mSubscribedPropsByClient.find(clientId) == mSubscribedPropsByClient.end()) {
186 return StatusError(StatusCode::INVALID_ARG)
187 << "No property was subscribed for the callback";
188 }
189 std::unordered_set<int32_t> subscribedPropIds;
190 for (auto const& propIdAreaId : mSubscribedPropsByClient[clientId]) {
191 subscribedPropIds.insert(propIdAreaId.propId);
192 }
193
194 for (int32_t propId : propIds) {
195 if (subscribedPropIds.find(propId) == subscribedPropIds.end()) {
196 return StatusError(StatusCode::INVALID_ARG)
197 << "property ID: " << propId << " is not subscribed";
198 }
199 }
200
201 auto& propIdAreaIds = mSubscribedPropsByClient[clientId];
202 auto it = propIdAreaIds.begin();
203 while (it != propIdAreaIds.end()) {
204 int32_t propId = it->propId;
205 if (std::find(propIds.begin(), propIds.end(), propId) != propIds.end()) {
206 if (auto result = removeContinuousSubscriberLocked(clientId, *it); !result.ok()) {
207 return result;
208 }
209
210 auto& clients = mClientsByPropIdArea[*it];
211 clients.erase(clientId);
212 if (clients.empty()) {
213 mClientsByPropIdArea.erase(*it);
214 mContSubConfigsByPropIdArea.erase(*it);
215 }
216 it = propIdAreaIds.erase(it);
217 } else {
218 it++;
219 }
220 }
221 if (propIdAreaIds.empty()) {
222 mSubscribedPropsByClient.erase(clientId);
223 }
224 return {};
225 }
226
unsubscribe(SubscriptionManager::ClientIdType clientId)227 VhalResult<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId) {
228 std::scoped_lock<std::mutex> lockGuard(mLock);
229
230 if (mSubscribedPropsByClient.find(clientId) == mSubscribedPropsByClient.end()) {
231 return StatusError(StatusCode::INVALID_ARG) << "No property was subscribed for this client";
232 }
233
234 auto& subscriptions = mSubscribedPropsByClient[clientId];
235 for (auto const& propIdAreaId : subscriptions) {
236 if (auto result = removeContinuousSubscriberLocked(clientId, propIdAreaId); !result.ok()) {
237 return result;
238 }
239
240 auto& clients = mClientsByPropIdArea[propIdAreaId];
241 clients.erase(clientId);
242 if (clients.empty()) {
243 mClientsByPropIdArea.erase(propIdAreaId);
244 mContSubConfigsByPropIdArea.erase(propIdAreaId);
245 }
246 }
247 mSubscribedPropsByClient.erase(clientId);
248 return {};
249 }
250
251 std::unordered_map<std::shared_ptr<IVehicleCallback>, std::vector<const VehiclePropValue*>>
getSubscribedClients(const std::vector<VehiclePropValue> & updatedValues)252 SubscriptionManager::getSubscribedClients(const std::vector<VehiclePropValue>& updatedValues) {
253 std::scoped_lock<std::mutex> lockGuard(mLock);
254 std::unordered_map<std::shared_ptr<IVehicleCallback>, std::vector<const VehiclePropValue*>>
255 clients;
256
257 for (const auto& value : updatedValues) {
258 PropIdAreaId propIdAreaId{
259 .propId = value.prop,
260 .areaId = value.areaId,
261 };
262 if (mClientsByPropIdArea.find(propIdAreaId) == mClientsByPropIdArea.end()) {
263 continue;
264 }
265
266 for (const auto& [_, client] : mClientsByPropIdArea[propIdAreaId]) {
267 clients[client].push_back(&value);
268 }
269 }
270 return clients;
271 }
272
273 std::unordered_map<std::shared_ptr<IVehicleCallback>, std::vector<VehiclePropError>>
getSubscribedClientsForErrorEvents(const std::vector<SetValueErrorEvent> & errorEvents)274 SubscriptionManager::getSubscribedClientsForErrorEvents(
275 const std::vector<SetValueErrorEvent>& errorEvents) {
276 std::scoped_lock<std::mutex> lockGuard(mLock);
277 std::unordered_map<std::shared_ptr<IVehicleCallback>, std::vector<VehiclePropError>> clients;
278
279 for (const auto& errorEvent : errorEvents) {
280 PropIdAreaId propIdAreaId{
281 .propId = errorEvent.propId,
282 .areaId = errorEvent.areaId,
283 };
284 if (mClientsByPropIdArea.find(propIdAreaId) == mClientsByPropIdArea.end()) {
285 continue;
286 }
287
288 for (const auto& [_, client] : mClientsByPropIdArea[propIdAreaId]) {
289 clients[client].push_back({
290 .propId = errorEvent.propId,
291 .areaId = errorEvent.areaId,
292 .errorCode = errorEvent.errorCode,
293 });
294 }
295 }
296 return clients;
297 }
298
isEmpty()299 bool SubscriptionManager::isEmpty() {
300 std::scoped_lock<std::mutex> lockGuard(mLock);
301 return mSubscribedPropsByClient.empty() && mClientsByPropIdArea.empty();
302 }
303
304 } // namespace vehicle
305 } // namespace automotive
306 } // namespace hardware
307 } // namespace android
308