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