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 #include "ConfigManager.h"
18 #include "EvsStateControl.h"
19 #include "EvsVehicleListener.h"
20
21 #include <signal.h>
22 #include <stdio.h>
23
24 #include <android/hardware/automotive/evs/1.1/IEvsDisplay.h>
25 #include <android/hardware/automotive/evs/1.1/IEvsEnumerator.h>
26 #include <android-base/logging.h>
27 #include <android-base/macros.h> // arraysize
28 #include <android-base/strings.h>
29 #include <hidl/HidlTransportSupport.h>
30 #include <hwbinder/IPCThreadState.h>
31 #include <hwbinder/ProcessState.h>
32 #include <utils/Errors.h>
33 #include <utils/StrongPointer.h>
34 #include <utils/Log.h>
35
36
37 using android::base::EqualsIgnoreCase;
38
39 // libhidl:
40 using android::hardware::configureRpcThreadpool;
41 using android::hardware::joinRpcThreadpool;
42
43 namespace {
44
45 const char* CONFIG_DEFAULT_PATH = "/system/etc/automotive/evs/config.json";
46 const char* CONFIG_OVERRIDE_PATH = "/system/etc/automotive/evs/config_override.json";
47
48 android::sp<IEvsEnumerator> pEvs;
49 android::sp<IEvsDisplay> pDisplay;
50 EvsStateControl *pStateController;
51
sigHandler(int sig)52 void sigHandler(int sig) {
53 LOG(WARNING) << "evs_app is being terminated on receiving a signal " << sig;
54 if (pEvs != nullptr) {
55 // Attempt to clean up the resources
56 pStateController->postCommand({EvsStateControl::Op::EXIT, 0, 0}, true);
57 pStateController->terminateUpdateLoop();
58 pDisplay = nullptr;
59 pEvs = nullptr;
60 }
61
62 android::hardware::IPCThreadState::self()->stopProcess();
63 exit(EXIT_FAILURE);
64 }
65
registerSigHandler()66 void registerSigHandler() {
67 struct sigaction sa;
68 sigemptyset(&sa.sa_mask);
69 sa.sa_flags = 0;
70 sa.sa_handler = sigHandler;
71 sigaction(SIGABRT, &sa, nullptr);
72 sigaction(SIGTERM, &sa, nullptr);
73 sigaction(SIGINT, &sa, nullptr);
74 }
75
76 } // namespace
77
78
79 // Helper to subscribe to VHal notifications
subscribeToVHal(sp<IVehicle> pVnet,sp<IVehicleCallback> listener,VehicleProperty propertyId)80 static bool subscribeToVHal(sp<IVehicle> pVnet,
81 sp<IVehicleCallback> listener,
82 VehicleProperty propertyId) {
83 assert(pVnet != nullptr);
84 assert(listener != nullptr);
85
86 // Register for vehicle state change callbacks we care about
87 // Changes in these values are what will trigger a reconfiguration of the EVS pipeline
88 SubscribeOptions optionsData[] = {
89 {
90 .propId = static_cast<int32_t>(propertyId),
91 .flags = SubscribeFlags::EVENTS_FROM_CAR
92 },
93 };
94 hidl_vec <SubscribeOptions> options;
95 options.setToExternal(optionsData, arraysize(optionsData));
96 StatusCode status = pVnet->subscribe(listener, options);
97 if (status != StatusCode::OK) {
98 LOG(WARNING) << "VHAL subscription for property " << static_cast<int32_t>(propertyId)
99 << " failed with code " << static_cast<int32_t>(status);
100 return false;
101 }
102
103 return true;
104 }
105
106
convertStringToFormat(const char * str,android_pixel_format_t * output)107 static bool convertStringToFormat(const char* str, android_pixel_format_t* output) {
108 bool result = true;
109 if (EqualsIgnoreCase(str, "RGBA8888")) {
110 *output = HAL_PIXEL_FORMAT_RGBA_8888;
111 } else if (EqualsIgnoreCase(str, "YV12")) {
112 *output = HAL_PIXEL_FORMAT_YV12;
113 } else if (EqualsIgnoreCase(str, "NV21")) {
114 *output = HAL_PIXEL_FORMAT_YCrCb_420_SP;
115 } else if (EqualsIgnoreCase(str, "YUYV")) {
116 *output = HAL_PIXEL_FORMAT_YCBCR_422_I;
117 } else {
118 result = false;
119 }
120
121 return result;
122 }
123
124
125 // Main entry point
main(int argc,char ** argv)126 int main(int argc, char** argv)
127 {
128 LOG(INFO) << "EVS app starting";
129
130 // Register a signal handler
131 registerSigHandler();
132
133 // Set up default behavior, then check for command line options
134 bool useVehicleHal = true;
135 bool printHelp = false;
136 const char* evsServiceName = "default";
137 int displayId = -1;
138 bool useExternalMemory = false;
139 android_pixel_format_t extMemoryFormat = HAL_PIXEL_FORMAT_RGBA_8888;
140 int32_t mockGearSignal = static_cast<int32_t>(VehicleGear::GEAR_REVERSE);
141 for (int i=1; i< argc; i++) {
142 if (strcmp(argv[i], "--test") == 0) {
143 useVehicleHal = false;
144 } else if (strcmp(argv[i], "--hw") == 0) {
145 evsServiceName = "EvsEnumeratorHw";
146 } else if (strcmp(argv[i], "--mock") == 0) {
147 evsServiceName = "EvsEnumeratorHw-Mock";
148 } else if (strcmp(argv[i], "--help") == 0) {
149 printHelp = true;
150 } else if (strcmp(argv[i], "--display") == 0) {
151 displayId = std::stoi(argv[++i]);
152 } else if (strcmp(argv[i], "--extmem") == 0) {
153 useExternalMemory = true;
154 if (i + 1 >= argc) {
155 // use RGBA8888 by default
156 LOG(INFO) << "External buffer format is not set. "
157 << "RGBA8888 will be used.";
158 } else {
159 if (!convertStringToFormat(argv[i + 1], &extMemoryFormat)) {
160 LOG(WARNING) << "Color format string " << argv[i + 1]
161 << " is unknown or not supported. RGBA8888 will be used.";
162 } else {
163 // move the index
164 ++i;
165 }
166 }
167 } else if (strcmp(argv[i], "--gear") == 0) {
168 // Gear signal to simulate
169 i += 1; // increase an index to next argument
170 if (strcasecmp(argv[i], "Park") == 0) {
171 mockGearSignal = static_cast<int32_t>(VehicleGear::GEAR_PARK);
172 } else if (strcasecmp(argv[i], "Reverse") != 0) {
173 LOG(WARNING) << "Unknown gear signal, " << argv[i] << ", is ignored "
174 << "and the reverse signal will be used instead";
175 }
176 } else {
177 printf("Ignoring unrecognized command line arg '%s'\n", argv[i]);
178 printHelp = true;
179 }
180 }
181 if (printHelp) {
182 printf("Options include:\n");
183 printf(" --test\n\tDo not talk to Vehicle Hal, "
184 "but simulate a given mock gear signal instead\n");
185 printf(" --gear\n\tMock gear signal for the test mode.");
186 printf(" Available options are Reverse and Park (case insensitive)\n");
187 printf(" --hw\n\tBypass EvsManager by connecting directly to EvsEnumeratorHw\n");
188 printf(" --mock\n\tConnect directly to EvsEnumeratorHw-Mock\n");
189 printf(" --display\n\tSpecify the display to use. If this is not set, the first"
190 "display in config.json's list will be used.\n");
191 printf(" --extmem <format>\n\t"
192 "Application allocates buffers to capture camera frames. "
193 "Available format strings are (case insensitive):\n");
194 printf("\t\tRGBA8888: 4x8-bit RGBA format. This is the default format to be used "
195 "when no format is specified.\n");
196 printf("\t\tYV12: YUV420 planar format with a full resolution Y plane "
197 "followed by a V values, with U values last.\n");
198 printf("\t\tNV21: A biplanar format with a full resolution Y plane "
199 "followed by a single chrome plane with weaved V and U values.\n");
200 printf("\t\tYUYV: Packed format with a half horizontal chrome resolution. "
201 "Known as YUV4:2:2.\n");
202
203 return EXIT_FAILURE;
204 }
205
206 // Load our configuration information
207 ConfigManager config;
208 if (!config.initialize(CONFIG_OVERRIDE_PATH)) {
209 if (!config.initialize(CONFIG_DEFAULT_PATH)) {
210 LOG(ERROR) << "Missing or improper configuration for the EVS application. Exiting.";
211 return EXIT_FAILURE;
212 }
213 }
214
215 // Set thread pool size to one to avoid concurrent events from the HAL.
216 // This pool will handle the EvsCameraStream callbacks.
217 // Note: This _will_ run in parallel with the EvsListener run() loop below which
218 // runs the application logic that reacts to the async events.
219 configureRpcThreadpool(1, false /* callerWillJoin */);
220
221 // Construct our async helper object
222 sp<EvsVehicleListener> pEvsListener = new EvsVehicleListener();
223
224 // Get the EVS manager service
225 LOG(INFO) << "Acquiring EVS Enumerator";
226 pEvs = IEvsEnumerator::getService(evsServiceName);
227 if (pEvs.get() == nullptr) {
228 LOG(ERROR) << "getService(" << evsServiceName
229 << ") returned NULL. Exiting.";
230 return EXIT_FAILURE;
231 }
232
233 // Request exclusive access to the EVS display
234 LOG(INFO) << "Acquiring EVS Display";
235
236 // We'll use an available display device.
237 displayId = config.setActiveDisplayId(displayId);
238 if (displayId < 0) {
239 PLOG(ERROR) << "EVS Display is unknown. Exiting.";
240 return EXIT_FAILURE;
241 }
242
243 pDisplay = pEvs->openDisplay_1_1(displayId);
244 if (pDisplay.get() == nullptr) {
245 LOG(ERROR) << "EVS Display unavailable. Exiting.";
246 return EXIT_FAILURE;
247 }
248
249 config.useExternalMemory(useExternalMemory);
250 config.setExternalMemoryFormat(extMemoryFormat);
251
252 // Set a mock gear signal for the test mode
253 config.setMockGearSignal(mockGearSignal);
254
255 // Connect to the Vehicle HAL so we can monitor state
256 sp<IVehicle> pVnet;
257 if (useVehicleHal) {
258 LOG(INFO) << "Connecting to Vehicle HAL";
259 pVnet = IVehicle::getService();
260 if (pVnet.get() == nullptr) {
261 LOG(ERROR) << "Vehicle HAL getService returned NULL. Exiting.";
262 return EXIT_FAILURE;
263 } else {
264 // Register for vehicle state change callbacks we care about
265 // Changes in these values are what will trigger a reconfiguration of the EVS pipeline
266 if (!subscribeToVHal(pVnet, pEvsListener, VehicleProperty::GEAR_SELECTION)) {
267 LOG(ERROR) << "Without gear notification, we can't support EVS. Exiting.";
268 return EXIT_FAILURE;
269 }
270 if (!subscribeToVHal(pVnet, pEvsListener, VehicleProperty::TURN_SIGNAL_STATE)) {
271 LOG(WARNING) << "Didn't get turn signal notifications, so we'll ignore those.";
272 }
273 }
274 } else {
275 LOG(WARNING) << "Test mode selected, so not talking to Vehicle HAL";
276 }
277
278 // Configure ourselves for the current vehicle state at startup
279 LOG(INFO) << "Constructing state controller";
280 pStateController = new EvsStateControl(pVnet, pEvs, pDisplay, config);
281 if (!pStateController->startUpdateLoop()) {
282 LOG(ERROR) << "Initial configuration failed. Exiting.";
283 return EXIT_FAILURE;
284 }
285
286 // Run forever, reacting to events as necessary
287 LOG(INFO) << "Entering running state";
288 pEvsListener->run(pStateController);
289
290 // In normal operation, we expect to run forever, but in some error conditions we'll quit.
291 // One known example is if another process preempts our registration for our service name.
292 LOG(ERROR) << "EVS Listener stopped. Exiting.";
293
294 return EXIT_SUCCESS;
295 }
296