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 
17 #define LOG_TAG "EVSAPP"
18 
19 #include "EvsStateControl.h"
20 
21 #include <stdio.h>
22 #include <string.h>
23 
24 #include <log/log.h>
25 
26 
27 // TODO:  Seems like it'd be nice if the Vehicle HAL provided such helpers (but how & where?)
getPropType(VehicleProperty prop)28 inline constexpr VehiclePropertyType getPropType(VehicleProperty prop) {
29     return static_cast<VehiclePropertyType>(
30             static_cast<int32_t>(prop)
31             & static_cast<int32_t>(VehiclePropertyType::MASK));
32 }
33 
34 
EvsStateControl(android::sp<IVehicle> pVnet,android::sp<IEvsEnumerator> pEvs,android::sp<IEvsDisplay> pDisplay,const ConfigManager & config)35 EvsStateControl::EvsStateControl(android::sp <IVehicle>       pVnet,
36                                  android::sp <IEvsEnumerator> pEvs,
37                                  android::sp <IEvsDisplay>    pDisplay,
38                                  const ConfigManager&         config) :
39     mVehicle(pVnet),
40     mEvs(pEvs),
41     mDisplay(pDisplay),
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     // Build our set of cameras for the states we support
54     ALOGD("Requesting camera list");
55     mEvs->getCameraList([this, &config]
56                         (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                                 for (auto&& info: config.getCameras()) {
67                                     if (cam.cameraId == info.cameraId) {
68                                         // We found a match!
69                                         if (info.function.find("reverse") != std::string::npos) {
70                                             mCameraInfo[State::REVERSE] = info;
71                                         }
72                                         if (info.function.find("right") != std::string::npos) {
73                                             mCameraInfo[State::RIGHT] = info;
74                                         }
75                                         if (info.function.find("left") != std::string::npos) {
76                                             mCameraInfo[State::LEFT] = info;
77                                         }
78                                         cameraConfigFound = true;
79                                         break;
80                                     }
81                                 }
82                                 if (!cameraConfigFound) {
83                                     ALOGW("No config information for hardware camera %s",
84                                           cam.cameraId.c_str());
85                                 }
86                             }
87                         }
88     );
89     ALOGD("State controller ready");
90 }
91 
92 
configureForVehicleState()93 bool EvsStateControl::configureForVehicleState() {
94     ALOGD("configureForVehicleState");
95 
96     static int32_t sDummyGear   = int32_t(VehicleGear::GEAR_REVERSE);
97     static int32_t sDummySignal = int32_t(VehicleTurnSignal::NONE);
98 
99     if (mVehicle != nullptr) {
100         // Query the car state
101         if (invokeGet(&mGearValue) != StatusCode::OK) {
102             ALOGE("GEAR_SELECTION not available from vehicle.  Exiting.");
103             return false;
104         }
105         if (invokeGet(&mTurnSignalValue) != StatusCode::OK) {
106             // Silently treat missing turn signal state as no turn signal active
107             mTurnSignalValue.value.int32Values.setToExternal(&sDummySignal, 1);
108         }
109     } else {
110         // While testing without a vehicle, behave as if we're in reverse for the first 20 seconds
111         static const int kShowTime = 20;    // seconds
112 
113         // See if it's time to turn off the default reverse camera
114         static std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
115         std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
116         if (std::chrono::duration_cast<std::chrono::seconds>(now - start).count() > kShowTime) {
117             // Switch to drive (which should turn off the reverse camera)
118             sDummyGear = int32_t(VehicleGear::GEAR_DRIVE);
119         }
120 
121         // Build the dummy vehicle state values (treating single values as 1 element vectors)
122         mGearValue.value.int32Values.setToExternal(&sDummyGear, 1);
123         mTurnSignalValue.value.int32Values.setToExternal(&sDummySignal, 1);
124     }
125 
126     // Choose our desired EVS state based on the current car state
127     State desiredState = OFF;
128     if (mGearValue.value.int32Values[0] == int32_t(VehicleGear::GEAR_REVERSE)) {
129         desiredState = REVERSE;
130     } else if (mTurnSignalValue.value.int32Values[0] == int32_t(VehicleTurnSignal::RIGHT)) {
131         desiredState = RIGHT;
132     } else if (mTurnSignalValue.value.int32Values[0] == int32_t(VehicleTurnSignal::LEFT)) {
133         desiredState = LEFT;
134     }
135 
136     // Apply the desire state
137     ALOGV("Selected state %d.", desiredState);
138     configureEvsPipeline(desiredState);
139 
140     // Operation was successful
141     return true;
142 }
143 
144 
invokeGet(VehiclePropValue * pRequestedPropValue)145 StatusCode EvsStateControl::invokeGet(VehiclePropValue *pRequestedPropValue) {
146     ALOGD("invokeGet");
147 
148     StatusCode status = StatusCode::TRY_AGAIN;
149     bool called = false;
150 
151     // Call the Vehicle HAL, which will block until the callback is complete
152     mVehicle->get(*pRequestedPropValue,
153                   [pRequestedPropValue, &status, &called]
154                   (StatusCode s, const VehiclePropValue& v) {
155                        status = s;
156                        *pRequestedPropValue = v;
157                        called = true;
158                   }
159     );
160     // This should be true as long as the get call is block as it should
161     // TODO:  Once we've got some milage on this code and the underlying HIDL services,
162     // we should remove this belt-and-suspenders check for correct operation as unnecessary.
163     if (!called) {
164         ALOGE("VehicleNetwork query did not run as expected.");
165     }
166 
167     return status;
168 }
169 
170 
configureEvsPipeline(State desiredState)171 bool EvsStateControl::configureEvsPipeline(State desiredState) {
172     ALOGD("configureEvsPipeline");
173 
174     if (mCurrentState == desiredState) {
175         // Nothing to do here...
176         return true;
177     }
178 
179     // See if we actually have to change cameras
180     if (mCameraInfo[mCurrentState].cameraId != mCameraInfo[desiredState].cameraId) {
181         ALOGI("Camera change required");
182         ALOGD("  Current cameraId (%d) = %s", mCurrentState,
183               mCameraInfo[mCurrentState].cameraId.c_str());
184         ALOGD("  Desired cameraId (%d) = %s", desiredState,
185               mCameraInfo[desiredState].cameraId.c_str());
186 
187         // Yup, we need to change cameras, so close the previous one, if necessary.
188         if (mCurrentCamera != nullptr) {
189             mCurrentStreamHandler->blockingStopStream();
190             mCurrentStreamHandler = nullptr;
191             mCurrentCamera = nullptr;
192         }
193 
194         // Now do we need a new camera?
195         if (!mCameraInfo[desiredState].cameraId.empty()) {
196             // Need a new camera, so open it
197             ALOGD("Open camera %s", mCameraInfo[desiredState].cameraId.c_str());
198             mCurrentCamera = mEvs->openCamera(mCameraInfo[desiredState].cameraId);
199 
200             // If we didn't get the camera we asked for, we need to bail out and try again later
201             if (mCurrentCamera == nullptr) {
202                 ALOGE("Failed to open EVS camera.  Skipping state change.");
203                 return false;
204             }
205         }
206 
207         // Now set the display state based on whether we have a camera feed to show
208         if (mCurrentCamera == nullptr) {
209             ALOGD("Turning off the display");
210             mDisplay->setDisplayState(DisplayState::NOT_VISIBLE);
211         } else {
212             // Create the stream handler object to receive and forward the video frames
213             mCurrentStreamHandler = new StreamHandler(mCurrentCamera, mDisplay);
214 
215             // Start the camera stream
216             ALOGD("Starting camera stream");
217             mCurrentStreamHandler->startStream();
218 
219             // Activate the display
220             ALOGD("Arming the display");
221             mDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME);
222         }
223     }
224 
225     // Record our current state
226     ALOGI("Activated state %d.", desiredState);
227     mCurrentState = desiredState;
228 
229     return true;
230 }
231