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