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