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