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 #include "EvsStateControl.h"
17 #include "RenderDirectView.h"
18 #include "RenderTopView.h"
19
20 #include <stdio.h>
21 #include <string.h>
22
23 #include <log/log.h>
24
25
26 // TODO: Seems like it'd be nice if the Vehicle HAL provided such helpers (but how & where?)
getPropType(VehicleProperty prop)27 inline constexpr VehiclePropertyType getPropType(VehicleProperty prop) {
28 return static_cast<VehiclePropertyType>(
29 static_cast<int32_t>(prop)
30 & static_cast<int32_t>(VehiclePropertyType::MASK));
31 }
32
33
EvsStateControl(android::sp<IVehicle> pVnet,android::sp<IEvsEnumerator> pEvs,android::sp<IEvsDisplay> pDisplay,const ConfigManager & config)34 EvsStateControl::EvsStateControl(android::sp <IVehicle> pVnet,
35 android::sp <IEvsEnumerator> pEvs,
36 android::sp <IEvsDisplay> pDisplay,
37 const ConfigManager& config) :
38 mVehicle(pVnet),
39 mEvs(pEvs),
40 mDisplay(pDisplay),
41 mConfig(config),
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 #if 0 // This way we only ever deal with cameras which exist in the system
54 // Build our set of cameras for the states we support
55 ALOGD("Requesting camera list");
56 mEvs->getCameraList([this, &config](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 // If more than one camera is listed for a given function, we'll
67 // list all of them and let the UX/rendering logic use one, some
68 // or all of them as appropriate.
69 for (auto&& info: config.getCameras()) {
70 if (cam.cameraId == info.cameraId) {
71 // We found a match!
72 if (info.function.find("reverse") != std::string::npos) {
73 mCameraList[State::REVERSE].push_back(info);
74 }
75 if (info.function.find("right") != std::string::npos) {
76 mCameraList[State::RIGHT].push_back(info);
77 }
78 if (info.function.find("left") != std::string::npos) {
79 mCameraList[State::LEFT].push_back(info);
80 }
81 if (info.function.find("park") != std::string::npos) {
82 mCameraList[State::PARKING].push_back(info);
83 }
84 cameraConfigFound = true;
85 break;
86 }
87 }
88 if (!cameraConfigFound) {
89 ALOGW("No config information for hardware camera %s",
90 cam.cameraId.c_str());
91 }
92 }
93 }
94 );
95 #else // This way we use placeholders for cameras in the configuration but not reported by EVS
96 // Build our set of cameras for the states we support
97 ALOGD("Requesting camera list");
98 for (auto&& info: config.getCameras()) {
99 if (info.function.find("reverse") != std::string::npos) {
100 mCameraList[State::REVERSE].push_back(info);
101 }
102 if (info.function.find("right") != std::string::npos) {
103 mCameraList[State::RIGHT].push_back(info);
104 }
105 if (info.function.find("left") != std::string::npos) {
106 mCameraList[State::LEFT].push_back(info);
107 }
108 if (info.function.find("park") != std::string::npos) {
109 mCameraList[State::PARKING].push_back(info);
110 }
111 }
112 #endif
113
114 ALOGD("State controller ready");
115 }
116
117
startUpdateLoop()118 bool EvsStateControl::startUpdateLoop() {
119 // Create the thread and report success if it gets started
120 mRenderThread = std::thread([this](){ updateLoop(); });
121 return mRenderThread.joinable();
122 }
123
124
postCommand(const Command & cmd)125 void EvsStateControl::postCommand(const Command& cmd) {
126 // Push the command onto the queue watched by updateLoop
127 mLock.lock();
128 mCommandQueue.push(cmd);
129 mLock.unlock();
130
131 // Send a signal to wake updateLoop in case it is asleep
132 mWakeSignal.notify_all();
133 }
134
135
updateLoop()136 void EvsStateControl::updateLoop() {
137 ALOGD("Starting EvsStateControl update loop");
138
139 bool run = true;
140 while (run) {
141 // Process incoming commands
142 {
143 std::lock_guard <std::mutex> lock(mLock);
144 while (!mCommandQueue.empty()) {
145 const Command& cmd = mCommandQueue.front();
146 switch (cmd.operation) {
147 case Op::EXIT:
148 run = false;
149 break;
150 case Op::CHECK_VEHICLE_STATE:
151 // Just running selectStateForCurrentConditions below will take care of this
152 break;
153 case Op::TOUCH_EVENT:
154 // TODO: Implement this given the x/y location of the touch event
155 // Ignore for now
156 break;
157 }
158 mCommandQueue.pop();
159 }
160 }
161
162 // Review vehicle state and choose an appropriate renderer
163 if (!selectStateForCurrentConditions()) {
164 ALOGE("selectStateForCurrentConditions failed so we're going to die");
165 break;
166 }
167
168 // If we have an active renderer, give it a chance to draw
169 if (mCurrentRenderer) {
170 // Get the output buffer we'll use to display the imagery
171 BufferDesc tgtBuffer = {};
172 mDisplay->getTargetBuffer([&tgtBuffer](const BufferDesc& buff) {
173 tgtBuffer = buff;
174 }
175 );
176
177 if (tgtBuffer.memHandle == nullptr) {
178 ALOGE("Didn't get requested output buffer -- skipping this frame.");
179 } else {
180 // Generate our output image
181 if (!mCurrentRenderer->drawFrame(tgtBuffer)) {
182 // If drawing failed, we want to exit quickly so an app restart can happen
183 run = false;
184 }
185
186 // Send the finished image back for display
187 mDisplay->returnTargetBufferForDisplay(tgtBuffer);
188 }
189 } else {
190 // No active renderer, so sleep until somebody wakes us with another command
191 std::unique_lock<std::mutex> lock(mLock);
192 mWakeSignal.wait(lock);
193 }
194 }
195
196 ALOGW("EvsStateControl update loop ending");
197
198 // TODO: Fix it so we can exit cleanly from the main thread instead
199 printf("Shutting down app due to state control loop ending\n");
200 ALOGE("KILLING THE APP FROM THE EvsStateControl LOOP ON DRAW FAILURE!!!");
201 exit(1);
202 }
203
204
selectStateForCurrentConditions()205 bool EvsStateControl::selectStateForCurrentConditions() {
206 static int32_t sDummyGear = int32_t(VehicleGear::GEAR_REVERSE);
207 static int32_t sDummySignal = int32_t(VehicleTurnSignal::NONE);
208
209 if (mVehicle != nullptr) {
210 // Query the car state
211 if (invokeGet(&mGearValue) != StatusCode::OK) {
212 ALOGE("GEAR_SELECTION not available from vehicle. Exiting.");
213 return false;
214 }
215 if ((mTurnSignalValue.prop == 0) || (invokeGet(&mTurnSignalValue) != StatusCode::OK)) {
216 // Silently treat missing turn signal state as no turn signal active
217 mTurnSignalValue.value.int32Values.setToExternal(&sDummySignal, 1);
218 mTurnSignalValue.prop = 0;
219 }
220 } else {
221 // While testing without a vehicle, behave as if we're in reverse for the first 20 seconds
222 static const int kShowTime = 20; // seconds
223
224 // See if it's time to turn off the default reverse camera
225 static std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
226 std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
227 if (std::chrono::duration_cast<std::chrono::seconds>(now - start).count() > kShowTime) {
228 // Switch to drive (which should turn off the reverse camera)
229 sDummyGear = int32_t(VehicleGear::GEAR_DRIVE);
230 }
231
232 // Build the dummy vehicle state values (treating single values as 1 element vectors)
233 mGearValue.value.int32Values.setToExternal(&sDummyGear, 1);
234 mTurnSignalValue.value.int32Values.setToExternal(&sDummySignal, 1);
235 }
236
237 // Choose our desired EVS state based on the current car state
238 // TODO: Update this logic, and consider user input when choosing if a view should be presented
239 State desiredState = OFF;
240 if (mGearValue.value.int32Values[0] == int32_t(VehicleGear::GEAR_REVERSE)) {
241 desiredState = REVERSE;
242 } else if (mTurnSignalValue.value.int32Values[0] == int32_t(VehicleTurnSignal::RIGHT)) {
243 desiredState = RIGHT;
244 } else if (mTurnSignalValue.value.int32Values[0] == int32_t(VehicleTurnSignal::LEFT)) {
245 desiredState = LEFT;
246 } else if (mGearValue.value.int32Values[0] == int32_t(VehicleGear::GEAR_PARK)) {
247 desiredState = PARKING;
248 }
249
250 // Apply the desire state
251 return configureEvsPipeline(desiredState);
252 }
253
254
invokeGet(VehiclePropValue * pRequestedPropValue)255 StatusCode EvsStateControl::invokeGet(VehiclePropValue *pRequestedPropValue) {
256 StatusCode status = StatusCode::TRY_AGAIN;
257
258 // Call the Vehicle HAL, which will block until the callback is complete
259 mVehicle->get(*pRequestedPropValue,
260 [pRequestedPropValue, &status]
261 (StatusCode s, const VehiclePropValue& v) {
262 status = s;
263 if (s == StatusCode::OK) {
264 *pRequestedPropValue = v;
265 }
266 }
267 );
268
269 return status;
270 }
271
272
configureEvsPipeline(State desiredState)273 bool EvsStateControl::configureEvsPipeline(State desiredState) {
274 if (mCurrentState == desiredState) {
275 // Nothing to do here...
276 return true;
277 }
278
279 ALOGD("Switching to state %d.", desiredState);
280 ALOGD(" Current state %d has %zu cameras", mCurrentState,
281 mCameraList[mCurrentState].size());
282 ALOGD(" Desired state %d has %zu cameras", desiredState,
283 mCameraList[desiredState].size());
284
285 // Since we're changing states, shut down the current renderer
286 if (mCurrentRenderer != nullptr) {
287 mCurrentRenderer->deactivate();
288 mCurrentRenderer = nullptr; // It's a smart pointer, so destructs on assignment to null
289 }
290
291 // Do we need a new direct view renderer?
292 if (mCameraList[desiredState].size() > 1 || desiredState == PARKING) {
293 // TODO: DO we want other kinds of compound view or else sequentially selected views?
294 mCurrentRenderer = std::make_unique<RenderTopView>(mEvs,
295 mCameraList[desiredState],
296 mConfig);
297 if (!mCurrentRenderer) {
298 ALOGE("Failed to construct top view renderer. Skipping state change.");
299 return false;
300 }
301 } else if (mCameraList[desiredState].size() == 1) {
302 // We have a camera assigned to this state for direct view
303 mCurrentRenderer = std::make_unique<RenderDirectView>(mEvs,
304 mCameraList[desiredState][0]);
305 if (!mCurrentRenderer) {
306 ALOGE("Failed to construct direct renderer. Skipping state change.");
307 return false;
308 }
309 }
310
311 // Now set the display state based on whether we have a video feed to show
312 if (mCurrentRenderer == nullptr) {
313 ALOGD("Turning off the display");
314 mDisplay->setDisplayState(DisplayState::NOT_VISIBLE);
315 } else {
316 // Start the camera stream
317 ALOGD("Starting camera stream");
318 if (!mCurrentRenderer->activate()) {
319 ALOGE("New renderer failed to activate");
320 return false;
321 }
322
323 // Activate the display
324 ALOGD("Arming the display");
325 Return<EvsResult> result = mDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME);
326 if (result != EvsResult::OK) {
327 ALOGE("setDisplayState returned an error (%d)", (EvsResult)result);
328 return false;
329 }
330 }
331
332 // Record our current state
333 ALOGI("Activated state %d.", desiredState);
334 mCurrentState = desiredState;
335
336 return true;
337 }
338