• 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 #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