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 "EVSAPP"
18
19 #include "EvsStateControl.h"
20
21 #include <stdio.h>
22 #include <string.h>
23
24 #include <log/log.h>
25
26
27 // TODO: Seems like it'd be nice if the Vehicle HAL provided such helpers (but how & where?)
getPropType(VehicleProperty prop)28 inline constexpr VehiclePropertyType getPropType(VehicleProperty prop) {
29 return static_cast<VehiclePropertyType>(
30 static_cast<int32_t>(prop)
31 & static_cast<int32_t>(VehiclePropertyType::MASK));
32 }
33
34
EvsStateControl(android::sp<IVehicle> pVnet,android::sp<IEvsEnumerator> pEvs,android::sp<IEvsDisplay> pDisplay,const ConfigManager & config)35 EvsStateControl::EvsStateControl(android::sp <IVehicle> pVnet,
36 android::sp <IEvsEnumerator> pEvs,
37 android::sp <IEvsDisplay> pDisplay,
38 const ConfigManager& config) :
39 mVehicle(pVnet),
40 mEvs(pEvs),
41 mDisplay(pDisplay),
42 mCurrentState(OFF) {
43
44 // Initialize the property value containers we'll be updating (they'll be zeroed by default)
45 static_assert(getPropType(VehicleProperty::GEAR_SELECTION) == VehiclePropertyType::INT32,
46 "Unexpected type for GEAR_SELECTION property");
47 static_assert(getPropType(VehicleProperty::TURN_SIGNAL_STATE) == VehiclePropertyType::INT32,
48 "Unexpected type for TURN_SIGNAL_STATE property");
49
50 mGearValue.prop = static_cast<int32_t>(VehicleProperty::GEAR_SELECTION);
51 mTurnSignalValue.prop = static_cast<int32_t>(VehicleProperty::TURN_SIGNAL_STATE);
52
53 // Build our set of cameras for the states we support
54 ALOGD("Requesting camera list");
55 mEvs->getCameraList([this, &config]
56 (hidl_vec<CameraDesc> cameraList) {
57 ALOGI("Camera list callback received %zu cameras",
58 cameraList.size());
59 for (auto&& cam: cameraList) {
60 ALOGD("Found camera %s", cam.cameraId.c_str());
61 bool cameraConfigFound = false;
62
63 // Check our configuration for information about this camera
64 // Note that a camera can have a compound function string
65 // such that a camera can be "right/reverse" and be used for both.
66 for (auto&& info: config.getCameras()) {
67 if (cam.cameraId == info.cameraId) {
68 // We found a match!
69 if (info.function.find("reverse") != std::string::npos) {
70 mCameraInfo[State::REVERSE] = info;
71 }
72 if (info.function.find("right") != std::string::npos) {
73 mCameraInfo[State::RIGHT] = info;
74 }
75 if (info.function.find("left") != std::string::npos) {
76 mCameraInfo[State::LEFT] = info;
77 }
78 cameraConfigFound = true;
79 break;
80 }
81 }
82 if (!cameraConfigFound) {
83 ALOGW("No config information for hardware camera %s",
84 cam.cameraId.c_str());
85 }
86 }
87 }
88 );
89 ALOGD("State controller ready");
90 }
91
92
configureForVehicleState()93 bool EvsStateControl::configureForVehicleState() {
94 ALOGD("configureForVehicleState");
95
96 static int32_t sDummyGear = int32_t(VehicleGear::GEAR_REVERSE);
97 static int32_t sDummySignal = int32_t(VehicleTurnSignal::NONE);
98
99 if (mVehicle != nullptr) {
100 // Query the car state
101 if (invokeGet(&mGearValue) != StatusCode::OK) {
102 ALOGE("GEAR_SELECTION not available from vehicle. Exiting.");
103 return false;
104 }
105 if (invokeGet(&mTurnSignalValue) != StatusCode::OK) {
106 // Silently treat missing turn signal state as no turn signal active
107 mTurnSignalValue.value.int32Values.setToExternal(&sDummySignal, 1);
108 }
109 } else {
110 // While testing without a vehicle, behave as if we're in reverse for the first 20 seconds
111 static const int kShowTime = 20; // seconds
112
113 // See if it's time to turn off the default reverse camera
114 static std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
115 std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
116 if (std::chrono::duration_cast<std::chrono::seconds>(now - start).count() > kShowTime) {
117 // Switch to drive (which should turn off the reverse camera)
118 sDummyGear = int32_t(VehicleGear::GEAR_DRIVE);
119 }
120
121 // Build the dummy vehicle state values (treating single values as 1 element vectors)
122 mGearValue.value.int32Values.setToExternal(&sDummyGear, 1);
123 mTurnSignalValue.value.int32Values.setToExternal(&sDummySignal, 1);
124 }
125
126 // Choose our desired EVS state based on the current car state
127 State desiredState = OFF;
128 if (mGearValue.value.int32Values[0] == int32_t(VehicleGear::GEAR_REVERSE)) {
129 desiredState = REVERSE;
130 } else if (mTurnSignalValue.value.int32Values[0] == int32_t(VehicleTurnSignal::RIGHT)) {
131 desiredState = RIGHT;
132 } else if (mTurnSignalValue.value.int32Values[0] == int32_t(VehicleTurnSignal::LEFT)) {
133 desiredState = LEFT;
134 }
135
136 // Apply the desire state
137 ALOGV("Selected state %d.", desiredState);
138 configureEvsPipeline(desiredState);
139
140 // Operation was successful
141 return true;
142 }
143
144
invokeGet(VehiclePropValue * pRequestedPropValue)145 StatusCode EvsStateControl::invokeGet(VehiclePropValue *pRequestedPropValue) {
146 ALOGD("invokeGet");
147
148 StatusCode status = StatusCode::TRY_AGAIN;
149 bool called = false;
150
151 // Call the Vehicle HAL, which will block until the callback is complete
152 mVehicle->get(*pRequestedPropValue,
153 [pRequestedPropValue, &status, &called]
154 (StatusCode s, const VehiclePropValue& v) {
155 status = s;
156 *pRequestedPropValue = v;
157 called = true;
158 }
159 );
160 // This should be true as long as the get call is block as it should
161 // TODO: Once we've got some milage on this code and the underlying HIDL services,
162 // we should remove this belt-and-suspenders check for correct operation as unnecessary.
163 if (!called) {
164 ALOGE("VehicleNetwork query did not run as expected.");
165 }
166
167 return status;
168 }
169
170
configureEvsPipeline(State desiredState)171 bool EvsStateControl::configureEvsPipeline(State desiredState) {
172 ALOGD("configureEvsPipeline");
173
174 if (mCurrentState == desiredState) {
175 // Nothing to do here...
176 return true;
177 }
178
179 // See if we actually have to change cameras
180 if (mCameraInfo[mCurrentState].cameraId != mCameraInfo[desiredState].cameraId) {
181 ALOGI("Camera change required");
182 ALOGD(" Current cameraId (%d) = %s", mCurrentState,
183 mCameraInfo[mCurrentState].cameraId.c_str());
184 ALOGD(" Desired cameraId (%d) = %s", desiredState,
185 mCameraInfo[desiredState].cameraId.c_str());
186
187 // Yup, we need to change cameras, so close the previous one, if necessary.
188 if (mCurrentCamera != nullptr) {
189 mCurrentStreamHandler->blockingStopStream();
190 mCurrentStreamHandler = nullptr;
191 mCurrentCamera = nullptr;
192 }
193
194 // Now do we need a new camera?
195 if (!mCameraInfo[desiredState].cameraId.empty()) {
196 // Need a new camera, so open it
197 ALOGD("Open camera %s", mCameraInfo[desiredState].cameraId.c_str());
198 mCurrentCamera = mEvs->openCamera(mCameraInfo[desiredState].cameraId);
199
200 // If we didn't get the camera we asked for, we need to bail out and try again later
201 if (mCurrentCamera == nullptr) {
202 ALOGE("Failed to open EVS camera. Skipping state change.");
203 return false;
204 }
205 }
206
207 // Now set the display state based on whether we have a camera feed to show
208 if (mCurrentCamera == nullptr) {
209 ALOGD("Turning off the display");
210 mDisplay->setDisplayState(DisplayState::NOT_VISIBLE);
211 } else {
212 // Create the stream handler object to receive and forward the video frames
213 mCurrentStreamHandler = new StreamHandler(mCurrentCamera, mDisplay);
214
215 // Start the camera stream
216 ALOGD("Starting camera stream");
217 mCurrentStreamHandler->startStream();
218
219 // Activate the display
220 ALOGD("Arming the display");
221 mDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME);
222 }
223 }
224
225 // Record our current state
226 ALOGI("Activated state %d.", desiredState);
227 mCurrentState = desiredState;
228
229 return true;
230 }
231