• 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 
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