• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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