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