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 "DefaultVehicleHal_v2_0"
18#include <android/log.h>
19
20#include "EmulatedVehicleHal.h"
21
22namespace android {
23namespace hardware {
24namespace automotive {
25namespace vehicle {
26namespace V2_0 {
27
28namespace impl {
29
30enum class FakeDataCommand : int32_t {
31    Stop = 0,
32    Start = 1,
33};
34
35EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore)
36    : mPropStore(propStore),
37      mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)),
38      mRecurrentTimer(std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer,
39                                  this, std::placeholders::_1)),
40      mFakeValueGenerator(std::bind(&EmulatedVehicleHal::onFakeValueGenerated,
41                                    this, std::placeholders::_1, std::placeholders::_2)) {
42
43    for (size_t i = 0; i < arraysize(kVehicleProperties); i++) {
44        mPropStore->registerProperty(kVehicleProperties[i].config);
45    }
46}
47
48VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::get(
49        const VehiclePropValue& requestedPropValue, StatusCode* outStatus) {
50    VehiclePropValuePtr v = nullptr;
51
52    auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue);
53    if (internalPropValue != nullptr) {
54        v = getValuePool()->obtain(*internalPropValue);
55    }
56
57    *outStatus = v != nullptr ? StatusCode::OK : StatusCode::INVALID_ARG;
58    return v;
59}
60
61StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {
62    if (propValue.prop == kGenerateFakeDataControllingProperty) {
63        return handleGenerateFakeDataRequest(propValue);
64    };
65
66    if (mHvacPowerProps.count(propValue.prop)) {
67        auto hvacPowerOn = mPropStore->readValueOrNull(toInt(VehicleProperty::HVAC_POWER_ON),
68                                                      toInt(VehicleAreaZone::ROW_1));
69
70        if (hvacPowerOn && hvacPowerOn->value.int32Values.size() == 1
71                && hvacPowerOn->value.int32Values[0] == 0) {
72            return StatusCode::NOT_AVAILABLE;
73        }
74    }
75
76    if (!mPropStore->writeValue(propValue)) {
77        return StatusCode::INVALID_ARG;
78    }
79
80    getEmulatorOrDie()->doSetValueFromClient(propValue);
81
82    return StatusCode::OK;
83}
84
85// Parse supported properties list and generate vector of property values to hold current values.
86void EmulatedVehicleHal::onCreate() {
87    for (auto& it : kVehicleProperties) {
88        VehiclePropConfig cfg = it.config;
89        int32_t supportedAreas = cfg.supportedAreas;
90
91        //  A global property will have supportedAreas = 0
92        if (isGlobalProp(cfg.prop)) {
93            supportedAreas = 0;
94        }
95
96        // This loop is a do-while so it executes at least once to handle global properties
97        do {
98            int32_t curArea = supportedAreas;
99            supportedAreas &= supportedAreas - 1;  // Clear the right-most bit of supportedAreas.
100            curArea ^= supportedAreas;  // Set curArea to the previously cleared bit.
101
102            // Create a separate instance for each individual zone
103            VehiclePropValue prop = {
104                .prop = cfg.prop,
105                .areaId = curArea,
106            };
107            if (it.initialAreaValues.size() > 0) {
108                auto valueForAreaIt = it.initialAreaValues.find(curArea);
109                if (valueForAreaIt != it.initialAreaValues.end()) {
110                    prop.value = valueForAreaIt->second;
111                } else {
112                    ALOGW("%s failed to get default value for prop 0x%x area 0x%x",
113                            __func__, cfg.prop, curArea);
114                }
115            } else {
116                prop.value = it.initialValue;
117            }
118            mPropStore->writeValue(prop);
119
120        } while (supportedAreas != 0);
121    }
122}
123
124std::vector<VehiclePropConfig> EmulatedVehicleHal::listProperties()  {
125    return mPropStore->getAllConfigs();
126}
127
128void EmulatedVehicleHal::onContinuousPropertyTimer(const std::vector<int32_t>& properties) {
129    VehiclePropValuePtr v;
130
131    auto& pool = *getValuePool();
132
133    for (int32_t property : properties) {
134        if (isContinuousProperty(property)) {
135            auto internalPropValue = mPropStore->readValueOrNull(property);
136            if (internalPropValue != nullptr) {
137                v = pool.obtain(*internalPropValue);
138            }
139        } else {
140            ALOGE("Unexpected onContinuousPropertyTimer for property: 0x%x", property);
141        }
142
143        if (v.get()) {
144            v->timestamp = elapsedRealtimeNano();
145            doHalEvent(std::move(v));
146        }
147    }
148}
149
150StatusCode EmulatedVehicleHal::subscribe(int32_t property, int32_t,
151                                        float sampleRate) {
152    ALOGI("%s propId: 0x%x, sampleRate: %f", __func__, property, sampleRate);
153
154    if (isContinuousProperty(property)) {
155        mRecurrentTimer.registerRecurrentEvent(hertzToNanoseconds(sampleRate), property);
156    }
157    return StatusCode::OK;
158}
159
160StatusCode EmulatedVehicleHal::unsubscribe(int32_t property) {
161    ALOGI("%s propId: 0x%x", __func__, property);
162    if (isContinuousProperty(property)) {
163        mRecurrentTimer.unregisterRecurrentEvent(property);
164    }
165    return StatusCode::OK;
166}
167
168bool EmulatedVehicleHal::isContinuousProperty(int32_t propId) const {
169    const VehiclePropConfig* config = mPropStore->getConfigOrNull(propId);
170    if (config == nullptr) {
171        ALOGW("Config not found for property: 0x%x", propId);
172        return false;
173    }
174    return config->changeMode == VehiclePropertyChangeMode::CONTINUOUS;
175}
176
177bool EmulatedVehicleHal::setPropertyFromVehicle(const VehiclePropValue& propValue) {
178    if (mPropStore->writeValue(propValue)) {
179        doHalEvent(getValuePool()->obtain(propValue));
180        return true;
181    } else {
182        return false;
183    }
184}
185
186std::vector<VehiclePropValue> EmulatedVehicleHal::getAllProperties() const  {
187    return mPropStore->readAllValues();
188}
189
190StatusCode EmulatedVehicleHal::handleGenerateFakeDataRequest(const VehiclePropValue& request) {
191    ALOGI("%s", __func__);
192    const auto& v = request.value;
193    if (v.int32Values.size() < 2) {
194        ALOGE("%s: expected at least 2 elements in int32Values, got: %zu", __func__,
195                v.int32Values.size());
196        return StatusCode::INVALID_ARG;
197    }
198
199    FakeDataCommand command = static_cast<FakeDataCommand>(v.int32Values[0]);
200    int32_t propId = v.int32Values[1];
201
202    switch (command) {
203        case FakeDataCommand::Start: {
204            if (!v.int64Values.size()) {
205                ALOGE("%s: interval is not provided in int64Values", __func__);
206                return StatusCode::INVALID_ARG;
207            }
208            auto interval = std::chrono::nanoseconds(v.int64Values[0]);
209
210            if (v.floatValues.size() < 3) {
211                ALOGE("%s: expected at least 3 element sin floatValues, got: %zu", __func__,
212                        v.floatValues.size());
213                return StatusCode::INVALID_ARG;
214            }
215            float initialValue = v.floatValues[0];
216            float dispersion = v.floatValues[1];
217            float increment = v.floatValues[2];
218
219            ALOGI("%s, propId: %d, initalValue: %f", __func__, propId, initialValue);
220            mFakeValueGenerator.startGeneratingHalEvents(
221                interval, propId, initialValue, dispersion, increment);
222
223            break;
224        }
225        case FakeDataCommand::Stop: {
226            ALOGI("%s, FakeDataCommandStop", __func__);
227            mFakeValueGenerator.stopGeneratingHalEvents(propId);
228            break;
229        }
230        default: {
231            ALOGE("%s: unexpected command: %d", __func__, command);
232            return StatusCode::INVALID_ARG;
233        }
234    }
235    return StatusCode::OK;
236}
237
238void EmulatedVehicleHal::onFakeValueGenerated(int32_t propId, float value) {
239    VehiclePropValuePtr updatedPropValue {};
240    switch (getPropType(propId)) {
241        case VehiclePropertyType::FLOAT:
242            updatedPropValue = getValuePool()->obtainFloat(value);
243            break;
244        case VehiclePropertyType::INT32:
245            updatedPropValue = getValuePool()->obtainInt32(static_cast<int32_t>(value));
246            break;
247        default:
248            ALOGE("%s: data type for property: 0x%x not supported", __func__, propId);
249            return;
250
251    }
252
253    if (updatedPropValue) {
254        updatedPropValue->prop = propId;
255        updatedPropValue->areaId = 0;  // Add area support if necessary.
256        updatedPropValue->timestamp = elapsedRealtimeNano();
257        mPropStore->writeValue(*updatedPropValue);
258        auto changeMode = mPropStore->getConfigOrDie(propId)->changeMode;
259        if (VehiclePropertyChangeMode::ON_CHANGE == changeMode) {
260            doHalEvent(move(updatedPropValue));
261        }
262    }
263}
264
265}  // impl
266
267}  // namespace V2_0
268}  // namespace vehicle
269}  // namespace automotive
270}  // namespace hardware
271}  // namespace android
272