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