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#include <unordered_map>
18#include <iostream>
19
20#include <android-base/macros.h>
21#include <utils/SystemClock.h>
22
23#include <gtest/gtest.h>
24
25#include "vhal_v2_0/VehicleHalManager.h"
26
27#include "VehicleHalTestUtils.h"
28
29namespace android {
30namespace hardware {
31namespace automotive {
32namespace vehicle {
33namespace V2_0 {
34
35namespace {
36
37using namespace std::placeholders;
38
39constexpr char kCarMake[] = "Default Car";
40constexpr int kRetriablePropMockedAttempts = 3;
41
42class MockedVehicleHal : public VehicleHal {
43public:
44    MockedVehicleHal() {
45        mConfigs.assign(std::begin(kVehicleProperties),
46                        std::end(kVehicleProperties));
47    }
48
49    std::vector<VehiclePropConfig> listProperties() override {
50        return mConfigs;
51    }
52
53    VehiclePropValuePtr get(const VehiclePropValue& requestedPropValue,
54             StatusCode* outStatus) override {
55        *outStatus = StatusCode::OK;
56        VehiclePropValuePtr pValue;
57        auto property = static_cast<VehicleProperty>(requestedPropValue.prop);
58        int32_t areaId = requestedPropValue.areaId;
59
60        switch (property) {
61            case VehicleProperty::INFO_MAKE:
62                pValue = getValuePool()->obtainString(kCarMake);
63                break;
64            case VehicleProperty::INFO_FUEL_CAPACITY:
65                if (fuelCapacityAttemptsLeft-- > 0) {
66                    // Emulate property not ready yet.
67                    *outStatus = StatusCode::TRY_AGAIN;
68                } else {
69                    pValue = getValuePool()->obtainFloat(42.42);
70                }
71                break;
72            default:
73                if (requestedPropValue.prop == kCustomComplexProperty) {
74                    pValue = getValuePool()->obtainComplex();
75                    pValue->value.int32Values = hidl_vec<int32_t> { 10, 20 };
76                    pValue->value.int64Values = hidl_vec<int64_t> { 30, 40 };
77                    pValue->value.floatValues = hidl_vec<float_t> { 1.1, 2.2 };
78                    pValue->value.bytes = hidl_vec<uint8_t> { 1, 2, 3 };
79                    pValue->value.stringValue = kCarMake;
80                    break;
81                }
82                auto key = makeKey(toInt(property), areaId);
83                if (mValues.count(key) == 0) {
84                    ALOGW("");
85                }
86                pValue = getValuePool()->obtain(mValues[key]);
87        }
88
89        if (*outStatus == StatusCode::OK && pValue.get() != nullptr) {
90            pValue->prop = toInt(property);
91            pValue->areaId = areaId;
92            pValue->timestamp = elapsedRealtimeNano();
93        }
94
95        return pValue;
96    }
97
98    StatusCode set(const VehiclePropValue& propValue) override {
99        if (toInt(VehicleProperty::MIRROR_FOLD) == propValue.prop
100                && mirrorFoldAttemptsLeft-- > 0) {
101            return StatusCode::TRY_AGAIN;
102        }
103
104        mValues[makeKey(propValue)] = propValue;
105        return StatusCode::OK;
106    }
107
108    StatusCode subscribe(int32_t /* property */,
109                         float /* sampleRate */) override {
110        return StatusCode::OK;
111    }
112
113    StatusCode unsubscribe(int32_t /* property */) override {
114        return StatusCode::OK;
115    }
116
117    void sendPropEvent(recyclable_ptr<VehiclePropValue> value) {
118        doHalEvent(std::move(value));
119    }
120
121    void sendHalError(StatusCode error, int32_t property, int32_t areaId) {
122        doHalPropertySetError(error, property, areaId);
123    }
124
125public:
126    int fuelCapacityAttemptsLeft = kRetriablePropMockedAttempts;
127    int mirrorFoldAttemptsLeft = kRetriablePropMockedAttempts;
128
129private:
130    int64_t makeKey(const VehiclePropValue& v) const {
131        return makeKey(v.prop, v.areaId);
132    }
133
134    int64_t makeKey(int32_t prop, int32_t area) const {
135        return (static_cast<int64_t>(prop) << 32) | area;
136    }
137
138private:
139    std::vector<VehiclePropConfig> mConfigs;
140    std::unordered_map<int64_t, VehiclePropValue> mValues;
141};
142
143class VehicleHalManagerTest : public ::testing::Test {
144protected:
145    void SetUp() override {
146        hal.reset(new MockedVehicleHal);
147        manager.reset(new VehicleHalManager(hal.get()));
148
149        objectPool = hal->getValuePool();
150    }
151
152    void TearDown() override {
153        manager.reset(nullptr);
154        hal.reset(nullptr);
155    }
156public:
157    void invokeGet(int32_t property, int32_t areaId) {
158        VehiclePropValue requestedValue {};
159        requestedValue.prop = property;
160        requestedValue.areaId = areaId;
161
162        invokeGet(requestedValue);
163    }
164
165    void invokeGet(const VehiclePropValue& requestedPropValue) {
166        actualValue = VehiclePropValue {};  // reset previous values
167
168        StatusCode refStatus;
169        VehiclePropValue refValue;
170        bool called = false;
171        manager->get(requestedPropValue, [&refStatus, &refValue, &called]
172            (StatusCode status, const VehiclePropValue& value) {
173            refStatus = status;
174            refValue = value;
175            called = true;
176        });
177        ASSERT_TRUE(called) << "callback wasn't called for prop: "
178                            << hexString(requestedPropValue.prop);
179
180        actualValue = refValue;
181        actualStatusCode = refStatus;
182    }
183
184public:
185    VehiclePropValue actualValue;
186    StatusCode actualStatusCode;
187
188    VehiclePropValuePool* objectPool;
189    std::unique_ptr<MockedVehicleHal> hal;
190    std::unique_ptr<VehicleHalManager> manager;
191};
192
193TEST_F(VehicleHalManagerTest, getPropConfigs) {
194    hidl_vec<int32_t> properties =
195        { toInt(VehicleProperty::HVAC_FAN_SPEED),
196          toInt(VehicleProperty::INFO_MAKE) };
197    bool called = false;
198
199    manager->getPropConfigs(properties,
200            [&called] (StatusCode status,
201                       const hidl_vec<VehiclePropConfig>& c) {
202        ASSERT_EQ(StatusCode::OK, status);
203        ASSERT_EQ(2u, c.size());
204        called = true;
205    });
206
207    ASSERT_TRUE(called);  // Verify callback received.
208
209    called = false;
210    manager->getPropConfigs({ toInt(VehicleProperty::HVAC_FAN_SPEED) },
211            [&called] (StatusCode status,
212                       const hidl_vec<VehiclePropConfig>& c) {
213        ASSERT_EQ(StatusCode::OK, status);
214        ASSERT_EQ(1u, c.size());
215        ASSERT_EQ(toString(kVehicleProperties[1]), toString(c[0]));
216        called = true;
217    });
218    ASSERT_TRUE(called);  // Verify callback received.
219
220    // TODO(pavelm): add case case when property was not declared.
221}
222
223TEST_F(VehicleHalManagerTest, getAllPropConfigs) {
224    bool called = false;
225    manager->getAllPropConfigs(
226            [&called] (const hidl_vec<VehiclePropConfig>& propConfigs) {
227        ASSERT_EQ(arraysize(kVehicleProperties), propConfigs.size());
228
229        for (size_t i = 0; i < propConfigs.size(); i++) {
230            ASSERT_EQ(toString(kVehicleProperties[i]),
231                      toString(propConfigs[i]));
232        }
233        called = true;
234    });
235    ASSERT_TRUE(called);  // Verify callback received.
236}
237
238TEST_F(VehicleHalManagerTest, halErrorEvent) {
239    const auto PROP = toInt(VehicleProperty::DISPLAY_BRIGHTNESS);
240
241    sp<MockedVehicleCallback> cb = new MockedVehicleCallback();
242
243    hidl_vec<SubscribeOptions> options = {
244        SubscribeOptions{.propId = PROP, .flags = SubscribeFlags::EVENTS_FROM_CAR},
245    };
246
247    StatusCode res = manager->subscribe(cb, options);
248    ASSERT_EQ(StatusCode::OK, res);
249
250    hal->sendHalError(StatusCode::TRY_AGAIN, PROP, 0 /* area id*/);
251}
252
253TEST_F(VehicleHalManagerTest, subscribe) {
254    const auto PROP = toInt(VehicleProperty::DISPLAY_BRIGHTNESS);
255
256    sp<MockedVehicleCallback> cb = new MockedVehicleCallback();
257
258    hidl_vec<SubscribeOptions> options = {
259        SubscribeOptions{.propId = PROP, .flags = SubscribeFlags::EVENTS_FROM_CAR}};
260
261    StatusCode res = manager->subscribe(cb, options);
262    ASSERT_EQ(StatusCode::OK, res);
263
264    auto unsubscribedValue = objectPool->obtain(VehiclePropertyType::INT32);
265    unsubscribedValue->prop = toInt(VehicleProperty::HVAC_FAN_SPEED);
266
267    hal->sendPropEvent(std::move(unsubscribedValue));
268    auto& receivedEnvents = cb->getReceivedEvents();
269
270    ASSERT_TRUE(cb->waitForExpectedEvents(0)) << " Unexpected events received: "
271                                              << receivedEnvents.size()
272                                              << (receivedEnvents.size() > 0
273                                                  ? toString(receivedEnvents.front()[0]) : "");
274
275    auto subscribedValue = objectPool->obtain(VehiclePropertyType::INT32);
276    subscribedValue->prop = PROP;
277    subscribedValue->value.int32Values[0] = 42;
278
279    cb->reset();
280    VehiclePropValue actualValue(*subscribedValue.get());
281    actualValue.status = VehiclePropertyStatus::AVAILABLE;
282    hal->sendPropEvent(std::move(subscribedValue));
283
284    ASSERT_TRUE(cb->waitForExpectedEvents(1)) << "Events received: "
285                                              << receivedEnvents.size();
286
287    ASSERT_EQ(toString(actualValue),
288              toString(cb->getReceivedEvents().front()[0]));
289}
290
291TEST_F(VehicleHalManagerTest, subscribe_WriteOnly) {
292    const auto PROP = toInt(VehicleProperty::HVAC_SEAT_TEMPERATURE);
293
294    sp<MockedVehicleCallback> cb = new MockedVehicleCallback();
295
296    hidl_vec<SubscribeOptions> options = {
297        SubscribeOptions{.propId = PROP, .flags = SubscribeFlags::EVENTS_FROM_CAR},
298    };
299
300    StatusCode res = manager->subscribe(cb, options);
301    // Unable to subscribe on Hal Events for write-only properties.
302    ASSERT_EQ(StatusCode::INVALID_ARG, res);
303
304    options[0].flags = SubscribeFlags::EVENTS_FROM_ANDROID;
305
306    res = manager->subscribe(cb, options);
307    // OK to subscribe on SET method call for write-only properties.
308    ASSERT_EQ(StatusCode::OK, res);
309}
310
311TEST_F(VehicleHalManagerTest, get_Complex) {
312    invokeGet(kCustomComplexProperty, 0);
313
314    ASSERT_EQ(StatusCode::OK, actualStatusCode);
315    ASSERT_EQ(kCustomComplexProperty, actualValue.prop);
316
317    ASSERT_EQ(3u, actualValue.value.bytes.size());
318    ASSERT_EQ(1, actualValue.value.bytes[0]);
319    ASSERT_EQ(2, actualValue.value.bytes[1]);
320    ASSERT_EQ(3, actualValue.value.bytes[2]);
321
322    ASSERT_EQ(2u, actualValue.value.int32Values.size());
323    ASSERT_EQ(10, actualValue.value.int32Values[0]);
324    ASSERT_EQ(20, actualValue.value.int32Values[1]);
325
326    ASSERT_EQ(2u, actualValue.value.floatValues.size());
327    ASSERT_FLOAT_EQ(1.1, actualValue.value.floatValues[0]);
328    ASSERT_FLOAT_EQ(2.2, actualValue.value.floatValues[1]);
329
330    ASSERT_EQ(2u, actualValue.value.int64Values.size());
331    ASSERT_FLOAT_EQ(30, actualValue.value.int64Values[0]);
332    ASSERT_FLOAT_EQ(40, actualValue.value.int64Values[1]);
333
334    ASSERT_STREQ(kCarMake, actualValue.value.stringValue.c_str());
335}
336
337TEST_F(VehicleHalManagerTest, get_StaticString) {
338    invokeGet(toInt(VehicleProperty::INFO_MAKE), 0);
339
340    ASSERT_EQ(StatusCode::OK, actualStatusCode);
341    ASSERT_EQ(toInt(VehicleProperty::INFO_MAKE), actualValue.prop);
342    ASSERT_STREQ(kCarMake, actualValue.value.stringValue.c_str());
343}
344
345TEST_F(VehicleHalManagerTest, get_NegativeCases) {
346    // Write-only property must fail.
347    invokeGet(toInt(VehicleProperty::HVAC_SEAT_TEMPERATURE), 0);
348    ASSERT_EQ(StatusCode::ACCESS_DENIED, actualStatusCode);
349
350    // Unknown property must fail.
351    invokeGet(toInt(VehicleProperty::MIRROR_Z_MOVE), 0);
352    ASSERT_EQ(StatusCode::INVALID_ARG, actualStatusCode);
353}
354
355TEST_F(VehicleHalManagerTest, get_Retriable) {
356    actualStatusCode = StatusCode::TRY_AGAIN;
357    int attempts = 0;
358    while (StatusCode::TRY_AGAIN == actualStatusCode && ++attempts < 10) {
359        invokeGet(toInt(VehicleProperty::INFO_FUEL_CAPACITY), 0);
360
361    }
362    ASSERT_EQ(StatusCode::OK, actualStatusCode);
363    ASSERT_EQ(kRetriablePropMockedAttempts + 1, attempts);
364    ASSERT_FLOAT_EQ(42.42, actualValue.value.floatValues[0]);
365}
366
367TEST_F(VehicleHalManagerTest, set_Basic) {
368    const auto PROP = toInt(VehicleProperty::DISPLAY_BRIGHTNESS);
369    const auto VAL = 7;
370
371    auto expectedValue = hal->getValuePool()->obtainInt32(VAL);
372    expectedValue->prop = PROP;
373    expectedValue->areaId = 0;
374
375    actualStatusCode = manager->set(*expectedValue.get());
376    ASSERT_EQ(StatusCode::OK, actualStatusCode);
377
378    invokeGet(PROP, 0);
379    ASSERT_EQ(StatusCode::OK, actualStatusCode);
380    ASSERT_EQ(PROP, actualValue.prop);
381    ASSERT_EQ(VAL, actualValue.value.int32Values[0]);
382}
383
384TEST_F(VehicleHalManagerTest, set_DifferentAreas) {
385    const auto PROP = toInt(VehicleProperty::HVAC_FAN_SPEED);
386    const auto VAL1 = 1;
387    const auto VAL2 = 2;
388    const auto AREA1 = toInt(VehicleAreaSeat::ROW_1_LEFT);
389    const auto AREA2 = toInt(VehicleAreaSeat::ROW_1_RIGHT);
390
391    {
392        auto expectedValue1 = hal->getValuePool()->obtainInt32(VAL1);
393        expectedValue1->prop = PROP;
394        expectedValue1->areaId = AREA1;
395        actualStatusCode = manager->set(*expectedValue1.get());
396        ASSERT_EQ(StatusCode::OK, actualStatusCode);
397
398        auto expectedValue2 = hal->getValuePool()->obtainInt32(VAL2);
399        expectedValue2->prop = PROP;
400        expectedValue2->areaId = AREA2;
401        actualStatusCode = manager->set(*expectedValue2.get());
402        ASSERT_EQ(StatusCode::OK, actualStatusCode);
403    }
404
405    {
406        invokeGet(PROP, AREA1);
407        ASSERT_EQ(StatusCode::OK, actualStatusCode);
408        ASSERT_EQ(PROP, actualValue.prop);
409        ASSERT_EQ(AREA1, actualValue.areaId);
410        ASSERT_EQ(VAL1, actualValue.value.int32Values[0]);
411
412        invokeGet(PROP, AREA2);
413        ASSERT_EQ(StatusCode::OK, actualStatusCode);
414        ASSERT_EQ(PROP, actualValue.prop);
415        ASSERT_EQ(AREA2, actualValue.areaId);
416        ASSERT_EQ(VAL2, actualValue.value.int32Values[0]);
417    }
418}
419
420TEST_F(VehicleHalManagerTest, set_Retriable) {
421    const auto PROP = toInt(VehicleProperty::MIRROR_FOLD);
422
423    auto v = hal->getValuePool()->obtainBoolean(true);
424    v->prop = PROP;
425    v->areaId = 0;
426
427    actualStatusCode = StatusCode::TRY_AGAIN;
428    int attempts = 0;
429    while (StatusCode::TRY_AGAIN == actualStatusCode && ++attempts < 10) {
430        actualStatusCode = manager->set(*v.get());
431    }
432
433    ASSERT_EQ(StatusCode::OK, actualStatusCode);
434    ASSERT_EQ(kRetriablePropMockedAttempts + 1, attempts);
435
436    invokeGet(PROP, 0);
437    ASSERT_EQ(StatusCode::OK, actualStatusCode);
438    ASSERT_TRUE(actualValue.value.int32Values[0]);
439}
440
441TEST(HalClientVectorTest, basic) {
442    HalClientVector clients;
443    sp<IVehicleCallback> callback1 = new MockedVehicleCallback();
444
445    sp<HalClient> c1 = new HalClient(callback1);
446    sp<HalClient> c2 = new HalClient(callback1);
447
448    clients.addOrUpdate(c1);
449    clients.addOrUpdate(c1);
450    clients.addOrUpdate(c2);
451    ASSERT_EQ(2u, clients.size());
452    ASSERT_FALSE(clients.isEmpty());
453    ASSERT_LE(0, clients.indexOf(c1));
454    ASSERT_LE(0, clients.remove(c1));
455    ASSERT_GT(0, clients.indexOf(c1));  // c1 was already removed
456    ASSERT_GT(0, clients.remove(c1));   // attempt to remove c1 again
457    ASSERT_LE(0, clients.remove(c2));
458
459    ASSERT_TRUE(clients.isEmpty());
460}
461
462}  // namespace anonymous
463
464}  // namespace V2_0
465}  // namespace vehicle
466}  // namespace automotive
467}  // namespace hardware
468}  // namespace android
469