1/*
2 * Copyright (C) 2016 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#define LOG_TAG "automotive.vehicle@2.0-impl"
18
19#include "SubscriptionManager.h"
20
21#include <cmath>
22#include <inttypes.h>
23
24#include <android/log.h>
25
26#include "VehicleUtils.h"
27
28namespace android {
29namespace hardware {
30namespace automotive {
31namespace vehicle {
32namespace V2_0 {
33
34bool mergeSubscribeOptions(const SubscribeOptions &oldOpts,
35                           const SubscribeOptions &newOpts,
36                           SubscribeOptions *outResult) {
37    float updatedRate = std::max(oldOpts.sampleRate, newOpts.sampleRate);
38    SubscribeFlags updatedFlags = SubscribeFlags(oldOpts.flags | newOpts.flags);
39
40    bool updated = (updatedRate > oldOpts.sampleRate) || (updatedFlags != oldOpts.flags);
41    if (updated) {
42        *outResult = oldOpts;
43        outResult->sampleRate = updatedRate;
44        outResult->flags = updatedFlags;
45    }
46
47    return updated;
48}
49
50void HalClient::addOrUpdateSubscription(const SubscribeOptions &opts)  {
51    ALOGI("%s opts.propId: 0x%x", __func__, opts.propId);
52
53    auto it = mSubscriptions.find(opts.propId);
54    if (it == mSubscriptions.end()) {
55        mSubscriptions.emplace(opts.propId, opts);
56    } else {
57        const SubscribeOptions& oldOpts = it->second;
58        SubscribeOptions updatedOptions;
59        if (mergeSubscribeOptions(oldOpts, opts, &updatedOptions)) {
60            mSubscriptions.erase(it);
61            mSubscriptions.emplace(opts.propId, updatedOptions);
62        }
63    }
64}
65
66bool HalClient::isSubscribed(int32_t propId,
67                             SubscribeFlags flags) {
68    auto it = mSubscriptions.find(propId);
69    if (it == mSubscriptions.end()) {
70        return false;
71    }
72    const SubscribeOptions& opts = it->second;
73    bool res = (opts.flags & flags);
74    return res;
75}
76
77std::vector<int32_t> HalClient::getSubscribedProperties() const {
78    std::vector<int32_t> props;
79    for (const auto& subscription : mSubscriptions) {
80        ALOGI("%s propId: 0x%x, propId: 0x%x", __func__, subscription.first, subscription.second.propId);
81        props.push_back(subscription.first);
82    }
83    return props;
84}
85
86StatusCode SubscriptionManager::addOrUpdateSubscription(
87        ClientId clientId,
88        const sp<IVehicleCallback> &callback,
89        const hidl_vec<SubscribeOptions> &optionList,
90        std::list<SubscribeOptions>* outUpdatedSubscriptions) {
91    outUpdatedSubscriptions->clear();
92
93    MuxGuard g(mLock);
94
95    ALOGI("SubscriptionManager::addOrUpdateSubscription, callback: %p", callback.get());
96
97    const sp<HalClient>& client = getOrCreateHalClientLocked(clientId, callback);
98    if (client.get() == nullptr) {
99        return StatusCode::INTERNAL_ERROR;
100    }
101
102    for (size_t i = 0; i < optionList.size(); i++) {
103        const SubscribeOptions& opts = optionList[i];
104        ALOGI("SubscriptionManager::addOrUpdateSubscription, prop: 0x%x", opts.propId);
105        client->addOrUpdateSubscription(opts);
106
107        addClientToPropMapLocked(opts.propId, client);
108
109        if (SubscribeFlags::EVENTS_FROM_CAR & opts.flags) {
110            SubscribeOptions updated;
111            if (updateHalEventSubscriptionLocked(opts, &updated)) {
112                outUpdatedSubscriptions->push_back(updated);
113            }
114        }
115    }
116
117    return StatusCode::OK;
118}
119
120std::list<HalClientValues> SubscriptionManager::distributeValuesToClients(
121        const std::vector<recyclable_ptr<VehiclePropValue>>& propValues,
122        SubscribeFlags flags) const {
123    std::map<sp<HalClient>, std::list<VehiclePropValue*>> clientValuesMap;
124
125    {
126        MuxGuard g(mLock);
127        for (const auto& propValue: propValues) {
128            VehiclePropValue* v = propValue.get();
129            auto clients = getSubscribedClientsLocked(v->prop, flags);
130            for (const auto& client : clients) {
131                clientValuesMap[client].push_back(v);
132            }
133        }
134    }
135
136    std::list<HalClientValues> clientValues;
137    for (const auto& entry : clientValuesMap) {
138        clientValues.push_back(HalClientValues {
139            .client = entry.first,
140            .values = entry.second
141        });
142    }
143
144    return clientValues;
145}
146
147std::list<sp<HalClient>> SubscriptionManager::getSubscribedClients(int32_t propId,
148                                                                   SubscribeFlags flags) const {
149    MuxGuard g(mLock);
150    return getSubscribedClientsLocked(propId, flags);
151}
152
153std::list<sp<HalClient>> SubscriptionManager::getSubscribedClientsLocked(
154    int32_t propId, SubscribeFlags flags) const {
155    std::list<sp<HalClient>> subscribedClients;
156
157    sp<HalClientVector> propClients = getClientsForPropertyLocked(propId);
158    if (propClients.get() != nullptr) {
159        for (size_t i = 0; i < propClients->size(); i++) {
160            const auto& client = propClients->itemAt(i);
161            if (client->isSubscribed(propId, flags)) {
162                subscribedClients.push_back(client);
163            }
164        }
165    }
166
167    return subscribedClients;
168}
169
170bool SubscriptionManager::updateHalEventSubscriptionLocked(
171        const SubscribeOptions &opts, SubscribeOptions *outUpdated) {
172    bool updated = false;
173    auto it = mHalEventSubscribeOptions.find(opts.propId);
174    if (it == mHalEventSubscribeOptions.end()) {
175        *outUpdated = opts;
176        mHalEventSubscribeOptions.emplace(opts.propId, opts);
177        updated = true;
178    } else {
179        const SubscribeOptions& oldOpts = it->second;
180
181        if (mergeSubscribeOptions(oldOpts, opts, outUpdated)) {
182            mHalEventSubscribeOptions.erase(opts.propId);
183            mHalEventSubscribeOptions.emplace(opts.propId, *outUpdated);
184            updated = true;
185        }
186    }
187
188    return updated;
189}
190
191void SubscriptionManager::addClientToPropMapLocked(
192        int32_t propId, const sp<HalClient> &client) {
193    auto it = mPropToClients.find(propId);
194    sp<HalClientVector> propClients;
195    if (it == mPropToClients.end()) {
196        propClients = new HalClientVector();
197        mPropToClients.insert(std::make_pair(propId, propClients));
198    } else {
199        propClients = it->second;
200    }
201    propClients->addOrUpdate(client);
202}
203
204sp<HalClientVector> SubscriptionManager::getClientsForPropertyLocked(
205        int32_t propId) const {
206    auto it = mPropToClients.find(propId);
207    return it == mPropToClients.end() ? nullptr : it->second;
208}
209
210sp<HalClient> SubscriptionManager::getOrCreateHalClientLocked(
211        ClientId clientId, const sp<IVehicleCallback>& callback) {
212    auto it = mClients.find(clientId);
213
214    if (it == mClients.end()) {
215        uint64_t cookie = reinterpret_cast<uint64_t>(clientId);
216        ALOGI("Creating new client and linking to death recipient, cookie: 0x%" PRIx64, cookie);
217        auto res = callback->linkToDeath(mCallbackDeathRecipient, cookie);
218        if (!res.isOk()) {  // Client is already dead?
219            ALOGW("%s failed to link to death, client %p, err: %s",
220                  __func__, callback.get(), res.description().c_str());
221            return nullptr;
222        }
223
224        sp<HalClient> client = new HalClient(callback);
225        mClients.insert({clientId, client});
226        return client;
227    } else {
228        return it->second;
229    }
230}
231
232void SubscriptionManager::unsubscribe(ClientId clientId,
233                                      int32_t propId) {
234    MuxGuard g(mLock);
235    auto propertyClients = getClientsForPropertyLocked(propId);
236    auto clientIter = mClients.find(clientId);
237    if (clientIter == mClients.end()) {
238        ALOGW("Unable to unsubscribe: no callback found, propId: 0x%x", propId);
239    } else {
240        auto client = clientIter->second;
241
242        if (propertyClients != nullptr) {
243            propertyClients->remove(client);
244
245            if (propertyClients->isEmpty()) {
246                mPropToClients.erase(propId);
247            }
248        }
249
250        bool isClientSubscribedToOtherProps = false;
251        for (const auto& propClient : mPropToClients) {
252            if (propClient.second->indexOf(client) >= 0) {
253                isClientSubscribedToOtherProps = true;
254                break;
255            }
256        }
257
258        if (!isClientSubscribedToOtherProps) {
259            auto res = client->getCallback()->unlinkToDeath(mCallbackDeathRecipient);
260            if (!res.isOk()) {
261                ALOGW("%s failed to unlink to death, client: %p, err: %s",
262                      __func__, client->getCallback().get(), res.description().c_str());
263            }
264            mClients.erase(clientIter);
265        }
266    }
267
268    if (propertyClients == nullptr || propertyClients->isEmpty()) {
269        mHalEventSubscribeOptions.erase(propId);
270        mOnPropertyUnsubscribed(propId);
271    }
272}
273
274void SubscriptionManager::onCallbackDead(uint64_t cookie) {
275    ALOGI("%s, cookie: 0x%" PRIx64, __func__, cookie);
276    ClientId clientId = cookie;
277
278    std::vector<int32_t> props;
279    {
280        MuxGuard g(mLock);
281        const auto& it = mClients.find(clientId);
282        if (it == mClients.end()) {
283            return;  // Nothing to do here, client wasn't subscribed to any properties.
284        }
285        const auto& halClient = it->second;
286        props = halClient->getSubscribedProperties();
287    }
288
289    for (int32_t propId : props) {
290        unsubscribe(clientId, propId);
291    }
292}
293
294
295}  // namespace V2_0
296}  // namespace vehicle
297}  // namespace automotive
298}  // namespace hardware
299}  // namespace android
300