1 /*
2  * Copyright (C) 2020 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 "VtsHalAutomotiveVehicle"
18 
19 #include <android/hardware/automotive/vehicle/2.0/IVehicle.h>
20 #include <utils/Log.h>
21 #include <unordered_set>
22 
23 #include <gtest/gtest.h>
24 #include <hidl/GtestPrinter.h>
25 #include <hidl/ServiceManagement.h>
26 
27 using namespace android::hardware::automotive::vehicle::V2_0;
28 using ::android::sp;
29 using ::android::hardware::hidl_vec;
30 using ::android::hardware::Return;
31 
32 constexpr auto kTimeout = std::chrono::milliseconds(500);
33 constexpr auto kInvalidProp = 0x31600207;
34 
35 class VtsVehicleCallback : public IVehicleCallback {
36   private:
37     using MutexGuard = std::lock_guard<std::mutex>;
38     using HidlVecOfValues = hidl_vec<VehiclePropValue>;
39     std::mutex mLock;
40     std::condition_variable mEventCond;
41     std::vector<HidlVecOfValues> mReceivedEvents;
42 
43   public:
onPropertyEvent(const hidl_vec<VehiclePropValue> & values)44     Return<void> onPropertyEvent(const hidl_vec<VehiclePropValue>& values) override {
45         {
46             MutexGuard guard(mLock);
47             mReceivedEvents.push_back(values);
48         }
49         mEventCond.notify_one();
50         return Return<void>();
51     }
52 
onPropertySet(const VehiclePropValue &)53     Return<void> onPropertySet(const VehiclePropValue& /* value */) override {
54         return Return<void>();
55     }
onPropertySetError(StatusCode,int32_t,int32_t)56     Return<void> onPropertySetError(StatusCode /* errorCode */, int32_t /* propId */,
57                                     int32_t /* areaId */) override {
58         return Return<void>();
59     }
60 
waitForExpectedEvents(size_t expectedEvents)61     bool waitForExpectedEvents(size_t expectedEvents) {
62         std::unique_lock<std::mutex> g(mLock);
63 
64         if (expectedEvents == 0 && mReceivedEvents.size() == 0) {
65             return mEventCond.wait_for(g, kTimeout) == std::cv_status::timeout;
66         }
67 
68         while (expectedEvents != mReceivedEvents.size()) {
69             if (mEventCond.wait_for(g, kTimeout) == std::cv_status::timeout) {
70                 return false;
71             }
72         }
73         return true;
74     }
75 
reset()76     void reset() { mReceivedEvents.clear(); }
77 };
78 
79 class VehicleHalHidlTest : public testing::TestWithParam<std::string> {
80   public:
SetUp()81     virtual void SetUp() override {
82         mVehicle = IVehicle::getService(GetParam());
83         ASSERT_NE(mVehicle.get(), nullptr);
84     }
TearDown()85     virtual void TearDown() override {}
86 
87     sp<IVehicle> mVehicle;
88 
isBooleanGlobalProp(int32_t property)89     bool isBooleanGlobalProp(int32_t property) {
90         return (property & (int)VehiclePropertyType::MASK) == (int)VehiclePropertyType::BOOLEAN &&
91                (property & (int)VehicleArea::MASK) == (int)VehicleArea::GLOBAL;
92     }
93 
invokeGet(int32_t property,int32_t areaId)94     void invokeGet(int32_t property, int32_t areaId) {
95         VehiclePropValue requestedValue{};
96         requestedValue.prop = property;
97         requestedValue.areaId = areaId;
98 
99         invokeGet(requestedValue);
100     }
101 
invokeGet(const VehiclePropValue & requestedPropValue)102     void invokeGet(const VehiclePropValue& requestedPropValue) {
103         mActualValue = VehiclePropValue{};  // reset previous values
104 
105         StatusCode refStatus;
106         VehiclePropValue refValue;
107         bool isCalled = false;
108         mVehicle->get(requestedPropValue,
109                       [&refStatus, &refValue, &isCalled](StatusCode status,
110                                                          const VehiclePropValue& value) {
111                           refStatus = status;
112                           refValue = value;
113                           isCalled = true;
114                       });
115         ASSERT_TRUE(isCalled) << "callback wasn't called for property: " << requestedPropValue.prop;
116 
117         mActualValue = refValue;
118         mActualStatusCode = refStatus;
119     }
120 
121     VehiclePropValue mActualValue;
122     StatusCode mActualStatusCode;
123 };
124 
125 // Test getAllPropConfig() returns at least 4 property configs.
TEST_P(VehicleHalHidlTest,getAllPropConfigs)126 TEST_P(VehicleHalHidlTest, getAllPropConfigs) {
127     ALOGD("VehicleHalHidlTest::getAllPropConfigs");
128     bool isCalled = false;
129     hidl_vec<VehiclePropConfig> propConfigs;
130     mVehicle->getAllPropConfigs([&isCalled, &propConfigs](const hidl_vec<VehiclePropConfig>& cfgs) {
131         propConfigs = cfgs;
132         isCalled = true;
133     });
134     ASSERT_TRUE(isCalled);
135     ASSERT_GE(propConfigs.size(), 4);
136 }
137 
138 // Test getPropConfig() can query all properties listed in CDD.
TEST_P(VehicleHalHidlTest,getPropConfigs)139 TEST_P(VehicleHalHidlTest, getPropConfigs) {
140     ALOGD("VehicleHalHidlTest::getPropConfigs");
141     // Check the properties listed in CDD
142     hidl_vec<int32_t> properties = {
143             (int)VehicleProperty::GEAR_SELECTION, (int)VehicleProperty::NIGHT_MODE,
144             (int)VehicleProperty::PARKING_BRAKE_ON, (int)VehicleProperty::PERF_VEHICLE_SPEED};
145     bool isCalled = false;
146     mVehicle->getPropConfigs(
147             properties, [&isCalled](StatusCode status, const hidl_vec<VehiclePropConfig>& cfgs) {
148                 ASSERT_EQ(StatusCode::OK, status);
149                 ASSERT_EQ(4u, cfgs.size());
150                 isCalled = true;
151             });
152     ASSERT_TRUE(isCalled);
153 }
154 
155 // Test getPropConfig() with an invalid propertyId returns an error code.
TEST_P(VehicleHalHidlTest,getPropConfigsWithInvalidProp)156 TEST_P(VehicleHalHidlTest, getPropConfigsWithInvalidProp) {
157     ALOGD("VehicleHalHidlTest::getPropConfigsWithInvalidProp");
158     hidl_vec<int32_t> properties = {kInvalidProp};
159     bool isCalled = false;
160     mVehicle->getPropConfigs(
161             properties, [&isCalled](StatusCode status, const hidl_vec<VehiclePropConfig>& cfgs) {
162                 ASSERT_NE(StatusCode::OK, status);
163                 ASSERT_EQ(0, cfgs.size());
164                 isCalled = true;
165             });
166     ASSERT_TRUE(isCalled);
167 }
168 
169 // Test get() return current value for properties.
TEST_P(VehicleHalHidlTest,get)170 TEST_P(VehicleHalHidlTest, get) {
171     ALOGD("VehicleHalHidlTest::get");
172     invokeGet((int)VehicleProperty::PERF_VEHICLE_SPEED, 0);
173     ASSERT_EQ(StatusCode::OK, mActualStatusCode);
174 }
175 
176 // Test get() with an invalid propertyId return an error codes.
TEST_P(VehicleHalHidlTest,getInvalidProp)177 TEST_P(VehicleHalHidlTest, getInvalidProp) {
178     ALOGD("VehicleHalHidlTest::getInvalidProp");
179 
180     invokeGet(kInvalidProp, 0);
181     ASSERT_NE(StatusCode::OK, mActualStatusCode);
182 }
183 
184 // Test set() on read_write properties.
TEST_P(VehicleHalHidlTest,setProp)185 TEST_P(VehicleHalHidlTest, setProp) {
186     ALOGD("VehicleHalHidlTest::setProp");
187     hidl_vec<VehiclePropConfig> propConfigs;
188     // skip hvac related properties
189     std::unordered_set<int32_t> hvacProps = {(int)VehicleProperty::HVAC_DEFROSTER,
190                                              (int)VehicleProperty::HVAC_AC_ON,
191                                              (int)VehicleProperty::HVAC_MAX_AC_ON,
192                                              (int)VehicleProperty::HVAC_MAX_DEFROST_ON,
193                                              (int)VehicleProperty::HVAC_RECIRC_ON,
194                                              (int)VehicleProperty::HVAC_DUAL_ON,
195                                              (int)VehicleProperty::HVAC_AUTO_ON,
196                                              (int)VehicleProperty::HVAC_POWER_ON,
197                                              (int)VehicleProperty::HVAC_AUTO_RECIRC_ON,
198                                              (int)VehicleProperty::HVAC_ELECTRIC_DEFROSTER_ON};
199     mVehicle->getAllPropConfigs(
200             [&propConfigs](const hidl_vec<VehiclePropConfig>& cfgs) { propConfigs = cfgs; });
201     for (const VehiclePropConfig& cfg : propConfigs) {
202         // test on boolean and writable property
203         if (cfg.access == VehiclePropertyAccess::READ_WRITE && isBooleanGlobalProp(cfg.prop) &&
204             !hvacProps.count(cfg.prop)) {
205             invokeGet(cfg.prop, 0);
206             int setValue = mActualValue.value.int32Values[0] == 1 ? 0 : 1;
207             VehiclePropValue propToSet = mActualValue;
208             propToSet.value.int32Values[0] = setValue;
209             ASSERT_EQ(StatusCode::OK, mVehicle->set(propToSet))
210                     << "Invalid status code for setting property: " << cfg.prop;
211             // check set success
212             invokeGet(cfg.prop, 0);
213             ASSERT_EQ(StatusCode::OK, mActualStatusCode);
214             ASSERT_EQ(setValue, mActualValue.value.int32Values[0])
215                     << "Failed to set value for property: " << cfg.prop;
216         }
217     }
218 }
219 
220 // Test set() on an read_only property.
TEST_P(VehicleHalHidlTest,setNotWritableProp)221 TEST_P(VehicleHalHidlTest, setNotWritableProp) {
222     ALOGD("VehicleHalHidlTest::setNotWritableProp");
223     invokeGet(static_cast<int>(VehicleProperty::PERF_VEHICLE_SPEED), 0);
224     ASSERT_EQ(StatusCode::OK, mActualStatusCode);
225     VehiclePropValue vehicleSpeed = mActualValue;
226 
227     ASSERT_EQ(StatusCode::ACCESS_DENIED, mVehicle->set(vehicleSpeed));
228 }
229 
230 // Test subscribe() and unsubscribe().
TEST_P(VehicleHalHidlTest,subscribeAndUnsubscribe)231 TEST_P(VehicleHalHidlTest, subscribeAndUnsubscribe) {
232     ALOGD("VehicleHalHidlTest::subscribeAndUnsubscribe");
233     const auto prop = static_cast<int>(VehicleProperty::PERF_VEHICLE_SPEED);
234     sp<VtsVehicleCallback> cb = new VtsVehicleCallback();
235 
236     hidl_vec<SubscribeOptions> options = {
237             SubscribeOptions{.propId = prop, 100.0, .flags = SubscribeFlags::EVENTS_FROM_CAR}};
238 
239     ASSERT_EQ(StatusCode::OK, mVehicle->subscribe(cb, options));
240     ASSERT_TRUE(cb->waitForExpectedEvents(10));
241 
242     ASSERT_EQ(StatusCode::OK, mVehicle->unsubscribe(cb, prop));
243     cb->reset();
244     ASSERT_FALSE(cb->waitForExpectedEvents(10));
245 }
246 
247 // Test subscribe() with an invalid property.
TEST_P(VehicleHalHidlTest,subscribeInvalidProp)248 TEST_P(VehicleHalHidlTest, subscribeInvalidProp) {
249     ALOGD("VehicleHalHidlTest::subscribeInvalidProp");
250 
251     sp<VtsVehicleCallback> cb = new VtsVehicleCallback();
252 
253     hidl_vec<SubscribeOptions> options = {SubscribeOptions{
254             .propId = kInvalidProp, 10.0, .flags = SubscribeFlags::EVENTS_FROM_CAR}};
255 
256     ASSERT_NE(StatusCode::OK, mVehicle->subscribe(cb, options));
257 }
258 
259 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VehicleHalHidlTest);
260 INSTANTIATE_TEST_SUITE_P(
261         PerInstance, VehicleHalHidlTest,
262         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IVehicle::descriptor)),
263         android::hardware::PrintInstanceNameToString);
264