Android车载应用之EvsCameraPreview源码分析(三)

555 阅读10分钟

0 引言

Android车载应用之EvsCameraPreview源码分析(二)中重点分析了startActivity过程,指名了自定义Evs应用的重点修改位置。接下来这篇文章想要介绍一下整个Evs应用的启动流程。

分析Evs的启动过程首先从查看Evs的日志开始,这里我们可以看到Evs的启动有几个关键的组件:evsmanagerd、EvsApp、android.hardware.automotive.evs-aidl-default-service和CAR.EVS这三个部分,下面将详细介绍它们。

image.png

1 evsmanagerd

evsmanagerd 是 EVS 系统中的核心管理进程(服务),负责初始化硬件服务、配置线程池并处理与硬件服务的通信。它通过命令行参数支持灵活的配置,包括连接模拟硬件或指定目标硬件服务。启动过程中,evsmanagerd 启动一个独立线程与硬件服务进行连接,并将主线程加入到线程池中,确保硬件交互的高效性和稳定性。整个过程确保了 EVS 系统能够在多线程环境下顺畅运行,并为后续的功能提供基础。

与evsmanagerd相关的代码位置在packages/services/Car/cpp/evs/manager中。

1.1 evsmanagerd.rc和init.evs.rc

evsmanagerd.rc的配置代码如下:

service evsmanagerd /system/bin/evsmanagerd
    class early_hal
    priority -20
    user automotive_evs
    group automotive_evs system
    disabled # will not automatically start with its class; must be explicitly started.

这段配置文件描述了 evsmanagerd 服务的启动方式。它将服务设置为在早期硬件抽象层阶段启动,并指定了较高的优先级以确保尽早运行。服务会以 automotive_evs 用户身份运行,并且默认是禁用的,需要显式启动。这种配置可以确保 evsmanagerd 在系统启动时按需启动,并提供与硬件交互的支持。

init.evs.rc的配置代码如下:

on late-init
    start evsmanagerd

这段配置通过 on late-init 确保在系统初始化的后期阶段启动 evsmanagerd 服务。这使得 evsmanagerd 在所有早期系统服务和硬件服务准备完毕后启动,从而避免干扰系统的初期启动过程,并确保其依赖的硬件服务已就绪。

1.2 Android.bp

重点找到与evsmanagerd服务进程相关的部分:

cc_binary {
    name: "evsmanagerd",
    defaults: ["evsmanagerd_defaults"],
    static_libs: ["libevsmanagerd"],
    srcs: ["src/service.cpp"],
    init_rc: ["evsmanagerd.rc"],
    vintf_fragments: ["manifest_evsmanagerd.xml"],
}

可以看到service.cpp文件就是实现evsmanagerd服务进程的源代码。

1.3 service.cpp

main函数的代码如下:

int main(int argc, char** argv) {
    LOG(INFO) << "EVS manager starting";

    // 命令行参数解析
    bool printHelp = false;
    std::string_view evsHardwareServiceName = kHardwareEnumeratorName;
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "--mock") == 0) {
            evsHardwareServiceName = kMockEnumeratorName;
        } else if (strcmp(argv[i], "--target") == 0) {
            i++;
            if (i >= argc) {
                LOG(ERROR) << "--target <service> was not provided with a service name";
            } else {
                evsHardwareServiceName = argv[i];
            }
        } else if (strcmp(argv[i], "--help") == 0) {
            printHelp = true;
        } else {
            printf("Ignoring unrecognized command line arg '%s'\n", argv[i]);
            printHelp = true;
        }
    }

    // 打印帮助信息
    if (printHelp) {
        printf("Options include:\n");
        printf("  --mock                   Connect to the mock driver at EvsEnumeratorHw-Mock\n");
        printf("  --target <service_name>  Connect to the named IEvsEnumerator service");
        return EXIT_SUCCESS;
    }

    // 设置线程池的最大线程数为 1
    if (!ABinderProcess_setThreadPoolMaxThreadCount(/* numThreads = */ 1)) {
        LOG(ERROR) << "Failed to set thread pool";
        return EXIT_FAILURE;
    }

    // 创建一个新的线程来启动硬件服务连接(startService函数),确保硬件服务不会阻塞主线程
    std::thread registrationThread(startService, evsHardwareServiceName, kManagedEnumeratorName);

    // 主线程加入线程池,开始等待和处理来自硬件服务的 RPC 请求
    ABinderProcess_startThreadPool();
    LOG(INFO) << "Main thread entering thread pool";

    ABinderProcess_joinThreadPool();
    LOG(ERROR) << "EVS Hardware Enumerator is shutting down";

    return EXIT_SUCCESS;
}

通过startService函数来看看具体是怎么连接硬件服务的:

void startService(const std::string_view& hardwareServiceName,
                  const std::string_view& managerServiceName) {
    // 创建Enumerator类型的aidlService对象,并调用init函数进行初始化
    LOG(INFO) << "EVS managed service connecting to hardware service at " << hardwareServiceName;
    std::shared_ptr<Enumerator> aidlService = ::ndk::SharedRefBase::make<Enumerator>();
    if (!aidlService->init(hardwareServiceName)) {
        LOG(FATAL) << "Error while connecting to the hardware service, " << hardwareServiceName;
    }

    // 注册aidl服务
    const std::string instanceName =
            std::string(Enumerator::descriptor) + kSeparator + std::string(managerServiceName);
    LOG(INFO) << "EVS managed service is starting as " << instanceName;
    auto aidlStatus =
            AServiceManager_addService(aidlService->asBinder().get(), instanceName.data());
    if (aidlStatus != EX_NONE) {
        LOG(FATAL) << "Error while registering EVS manager service: "
                   << ::android::statusToString(aidlStatus);
    }

    // 注册hidl服务(可选)
    configureRpcThreadpool(/* maxThreads = */ 1, /* callerWillJoin = */ false);
    ::android::sp<::android::hardware::automotive::evs::V1_1::IEvsEnumerator> hidlService =
            new (std::nothrow) HidlEnumerator(aidlService);
    if (!hidlService) {
        LOG(WARNING) << "Failed to initialize HIDL service";
    } else {
        auto hidlStatus = hidlService->registerAsService(managerServiceName.data());
        if (hidlStatus != ::android::OK) {
            LOG(WARNING) << "Failed to register EVS manager service to the hwservice manager, "
                         << ::android::statusToString(hidlStatus);
        }
    }

    LOG(INFO) << "Registration complete";
}

startService函数的主要工作是通过Enumerator类的init函数不断地连接硬件服务,连接上硬件服务后将其注册到服务管理器中。init函数中不断连接并重启部分的代码如下:

while (!mHwEnumerator && retryCount < (kTimeoutMilliseconds / kSleepTimeMilliseconds)) {
        mHwEnumerator = connectToAidlHal(hardwareServiceName, /* blocking= */ false);
        if (!mHwEnumerator) {
            LOG(INFO) << "Failed to connect to AIDL EVS HAL implementation.  "
                      << "Trying to connect to HIDL EVS HAL implementation instead.";
            mHwEnumerator = connectToHidlHal(hardwareServiceName);
            if (!mHwEnumerator) {
                LOG(INFO) << "No EVS HAL implementation is available.  Retrying after "
                          << kSleepTimeMilliseconds << " ms";
                std::this_thread::sleep_for(std::chrono::milliseconds(kSleepTimeMilliseconds));
                ++retryCount;
            }
        }
    }

它会首先尝试AIDL接口的EVS HAL实现,然后再尝试HIDL的EVS HAL实现,如果都无法连接则会进行重启。从log信息中也可以看出来。

image.png

可以看到,连接上的HAL实现名称为android.hardware.automotive.evs.IEvsEnumerator/default,咱们找到相关部分的代码详细分析。

2 android.hardware.automotive.evs-aidl-default-service

android.hardware.automotive.evs-aidl-default-service 是 Android 为车载系统提供的一项 AIDL 服务,用于与 EVS 硬件(如摄像头)进行交互。它主要负责为系统提供实时的视频流数据,供应用程序或系统组件使用,支持车载系统中的增强视觉功能。

和它相关的代码目录在hardware/interfaces/automotive/evs/aidl/impl/default/位置。

2.1 evs-default-service.rc

该服务的rc定义如下:

service vendor.evs-hal-default /vendor/bin/hw/android.hardware.automotive.evs-aidl-default-service
    class early_hal
    priority -20
    user graphics
    group automotive_evs camera
    onrestart restart cardisplayproxyd
    onrestart restart evsmanagerd
    disabled

它将android.hardware.automotive.evs-aidl-default-service进程定义为vendor.evs-hal-default服务,并且在该服务崩溃重启时,同时会重启cardisplayproxyd和evsmanagerd服务。

2.2 service.cpp

service.cpp中包含了服务的具体实现代码:

int main() {
    LOG(INFO) << "EVS Hardware Enumerator service is starting";

    // 检测显示服务是否声明
    const std::string displayServiceInstanceName =
            std::string(ICarDisplayProxy::descriptor) + std::string(kDisplayServiceInstanceName);
    if (!AServiceManager_isDeclared(displayServiceInstanceName.data())) {
        // TODO: We may just want to disable EVS display.
        LOG(ERROR) << displayServiceInstanceName << " is required.";
        return EXIT_FAILURE;
    }

    // 获取显示服务
    std::shared_ptr<ICarDisplayProxy> displayService = ICarDisplayProxy::fromBinder(
            ::ndk::SpAIBinder(AServiceManager_waitForService(displayServiceInstanceName.data())));
    if (!displayService) {
        LOG(ERROR) << "Cannot use " << displayServiceInstanceName << ".  Exiting.";
        return EXIT_FAILURE;
    }

    // 创建 EVS 枚举器服务实例 `service`,并将 `displayService` 作为参数传入,表示与显示服务集成。
    std::shared_ptr<EvsEnumerator> service =
            ndk::SharedRefBase::make<EvsEnumerator>(displayService);
    if (!service) {
        LOG(ERROR) << "Failed to instantiate the service";
        return EXIT_FAILURE;
    }

    // 通过 AServiceManager_addService 注册 EVS 服务
    const std::string instanceName =
            std::string(EvsEnumerator::descriptor) + std::string(kHwInstanceName);
    auto err = AServiceManager_addService(service->asBinder().get(), instanceName.data());
    if (err != EX_NONE) {
        LOG(ERROR) << "Failed to register " << instanceName << ", exception = " << err;
        return EXIT_FAILURE;
    }

    // 设置线程池并加入线程循环,保证正常服务运行
    if (!ABinderProcess_setThreadPoolMaxThreadCount(kNumBinderThreads)) {
        LOG(ERROR) << "Failed to set thread pool";
        return EXIT_FAILURE;
    }

    ABinderProcess_startThreadPool();
    LOG(INFO) << "EVS Hardware Enumerator is ready";

    ABinderProcess_joinThreadPool();
    // In normal operation, we don't expect the thread pool to exit
    LOG(INFO) << "EVS Hardware Enumerator is shutting down";
    return EXIT_SUCCESS;
}

这段代码实现了 EVS 硬件枚举器服务的启动流程,关键步骤包括:

  • 检查并获取显示服务:首先验证并获取与显示相关的服务实例。
  • 实例化 EVS 枚举器服务:通过显示服务实例化 EvsEnumerator 服务。
  • 注册服务到服务管理器:将 EVS 服务注册到系统的服务管理器,使其他组件可以访问。
  • 设置并启动线程池:配置线程池并启动,确保可以处理并发的请求。

evsmanagerd获取的硬件服务就是该服务。

3 EvsApp

从引言中日志记录可以看到,EvsApp一直在尝试获取 EVS Enumerator,直到evsmanagerd获取到硬件服务后。 EvsApp负责初始化并管理 EVS 硬件、显示、车辆信号(例如:倒车档位信号),以及与 Vehicle HAL 的通信。 和它相关的代码在packages/services/Car/cpp/evs/apps/default中。

int main(int argc, char** argv) {
    LOG(INFO) << "EVS app starting";

    // Set up default behavior, then check for command line options
    bool useVehicleHal = true;
    bool printHelp = false;
    const char* evsServiceName = "default";
    int displayId = -1;
    bool useExternalMemory = false;
    android_pixel_format_t extMemoryFormat = HAL_PIXEL_FORMAT_RGBA_8888;
    int32_t mockGearSignal = static_cast<int32_t>(VehicleGear::GEAR_REVERSE);
    // 命令行参数解析
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "--test") == 0) {
            useVehicleHal = false;
        } else if (strcmp(argv[i], "--hw") == 0) {
            evsServiceName = "EvsEnumeratorHw";
        } else if (strcmp(argv[i], "--mock") == 0) {
            evsServiceName = "EvsEnumeratorHw-Mock";
        } else if (strcmp(argv[i], "--help") == 0) {
            printHelp = true;
        } else if (strcmp(argv[i], "--display") == 0) {
            displayId = std::stoi(argv[++i]);
        } else if (strcmp(argv[i], "--extmem") == 0) {
            useExternalMemory = true;
            if (i + 1 >= argc) {
                // use RGBA8888 by default
                LOG(INFO) << "External buffer format is not set.  "
                          << "RGBA8888 will be used.";
            } else {
                if (!convertStringToFormat(argv[i + 1], &extMemoryFormat)) {
                    LOG(WARNING) << "Color format string " << argv[i + 1]
                                 << " is unknown or not supported.  RGBA8888 will be used.";
                } else {
                    // move the index
                    ++i;
                }
            }
        } else if (strcmp(argv[i], "--gear") == 0) {
            // Gear signal to simulate
            if (i + 1 >= argc) {
                LOG(INFO) << "Gear signal is not set.  "
                          << "Reverse signal will be used.";
                continue;
            }
            i += 1;  // increase an index to next argument
            if (strcasecmp(argv[i], "Park") == 0) {
                mockGearSignal = static_cast<int32_t>(VehicleGear::GEAR_PARK);
            } else if (strcasecmp(argv[i], "Reverse") != 0) {
                LOG(WARNING) << "Unknown gear signal, " << argv[i] << ", is ignored "
                             << "and the reverse signal will be used instead";
            }
        } else {
            printf("Ignoring unrecognized command line arg '%s'\n", argv[i]);
            printHelp = true;
        }
    }
    if (printHelp) {
        printf("Options include:\n");
        printf("  --test\n\tDo not talk to Vehicle Hal, "
               "but simulate a given mock gear signal instead\n");
        printf("  --gear\n\tMock gear signal for the test mode.");
        printf("  Available options are Reverse and Park (case insensitive)\n");
        printf("  --hw\n\tBypass EvsManager by connecting directly to EvsEnumeratorHw\n");
        printf("  --mock\n\tConnect directly to EvsEnumeratorHw-Mock\n");
        printf("  --display\n\tSpecify the display to use.  If this is not set, the first"
               "display in config.json's list will be used.\n");
        printf("  --extmem  <format>\n\t"
               "Application allocates buffers to capture camera frames.  "
               "Available format strings are (case insensitive):\n");
        printf("\t\tRGBA8888: 4x8-bit RGBA format.  This is the default format to be used "
               "when no format is specified.\n");
        printf("\t\tYV12: YUV420 planar format with a full resolution Y plane "
               "followed by a V values, with U values last.\n");
        printf("\t\tNV21: A biplanar format with a full resolution Y plane "
               "followed by a single chrome plane with weaved V and U values.\n");
        printf("\t\tYUYV: Packed format with a half horizontal chrome resolution.  "
               "Known as YUV4:2:2.\n");

        return EXIT_FAILURE;
    }

    // 加载配置信息(车辆、显示器和相机)
    ConfigManager config;
    if (!config.initialize(CONFIG_OVERRIDE_PATH)) {
        if (!config.initialize(CONFIG_DEFAULT_PATH)) {
            LOG(ERROR) << "Missing or improper configuration for the EVS application.  Exiting.";
            return EXIT_FAILURE;
        }
    }

    // 线程池配置
    if (!ABinderProcess_setThreadPoolMaxThreadCount(/* numThreads= */ 1)) {
        LOG(ERROR) << "Failed to confgiure the binder thread pool.";
        return EXIT_FAILURE;
    }
    ABinderProcess_startThreadPool();

    // Construct our async helper object
    std::shared_ptr<EvsVehicleListener> pEvsListener = std::make_shared<EvsVehicleListener>();

    // 获取EVS Enumerator服务(evsmanagerd中注册的服务)
    LOG(INFO) << "Acquiring EVS Enumerator";
    std::string serviceName =
            std::string(IEvsEnumerator::descriptor) + "/" + std::string(evsServiceName);
    if (!AServiceManager_isDeclared(serviceName.c_str())) {
        LOG(ERROR) << serviceName << " is not declared. Exiting.";
        return EXIT_FAILURE;
    }

    pEvsService = IEvsEnumerator::fromBinder(
            ndk::SpAIBinder(AServiceManager_checkService(serviceName.c_str())));
    if (!pEvsService) {
        LOG(ERROR) << "Failed to get " << serviceName << ". Exiting.";
        return EXIT_FAILURE;
    }

    // 获取EVS Display服务
    LOG(INFO) << "Acquiring EVS Display";

    // We'll use an available display device.
    displayId = config.setActiveDisplayId(displayId);
    if (displayId < 0) {
        PLOG(ERROR) << "EVS Display is unknown.  Exiting.";
        return EXIT_FAILURE;
    }

    if (auto status = pEvsService->openDisplay(displayId, &pDisplay); !status.isOk()) {
        LOG(ERROR) << "EVS Display unavailable.  Exiting.";
        return EXIT_FAILURE;
    }

    config.useExternalMemory(useExternalMemory);
    config.setExternalMemoryFormat(extMemoryFormat);

    // 设置模拟倒车信号
    config.setMockGearSignal(mockGearSignal);

    // 连接到Vehicl HAL
    std::shared_ptr<IVhalClient> pVnet;
    if (useVehicleHal) {
        LOG(INFO) << "Connecting to Vehicle HAL";
        pVnet = IVhalClient::create();
        if (pVnet == nullptr) {
            LOG(ERROR) << "Vehicle HAL getService returned NULL.  Exiting.";
            return EXIT_FAILURE;
        } else {
            auto subscriptionClient = pVnet->getSubscriptionClient(pEvsListener);
            // Register for vehicle state change callbacks we care about
            // Changes in these values are what will trigger a reconfiguration of the EVS pipeline
            if (!subscribeToVHal(subscriptionClient.get(), VehicleProperty::GEAR_SELECTION)) {
                LOG(ERROR) << "Without gear notification, we can't support EVS.  Exiting.";
                return EXIT_FAILURE;
            }
            if (!subscribeToVHal(subscriptionClient.get(), VehicleProperty::TURN_SIGNAL_STATE)) {
                LOG(WARNING) << "Didn't get turn signal notifications, so we'll ignore those.";
            }
        }
    } else {
        LOG(WARNING) << "Test mode selected, so not talking to Vehicle HAL";
    }

    // 创建并启动 EvsStateControl(状态控制器)来管理 EVS 服务的状态。
    LOG(INFO) << "Constructing state controller";
    pStateController = new EvsStateControl(pVnet, pEvsService, pDisplay, config);
    if (!pStateController->startUpdateLoop()) {
        LOG(ERROR) << "Initial configuration failed.  Exiting.";
        return EXIT_FAILURE;
    }

    // Run forever, reacting to events as necessary
    LOG(INFO) << "Entering running state";
    pEvsListener->run(pStateController);

    // In normal operation, we expect to run forever, but in some error conditions we'll quit.
    // One known example is if another process preempts our registration for our service name.
    LOG(ERROR) << "EVS Listener stopped.  Exiting.";

    return EXIT_SUCCESS;
}

主要功能分析如下:

  • 连接 EVS 服务 Evs App 通过与 EVS 管理器(如 EvsEnumerator)进行交互,连接到车辆的视觉系统。EvsEnumerator 是一个服务,它为车辆提供硬件视觉数据流的管理,Evs App 会通过它来获取、显示或处理摄像头图像、视频流等。
  • 模拟车辆档位信号 Evs App 允许模拟车辆的档位信号(如倒车、停车等)。这对于在开发环境或测试环境中模拟不同车辆状态(比如倒车时显示倒车影像)非常重要。
  • 与 Vehicle HAL (硬件抽象层) 的集成 Evs App 可以选择是否与 Vehicle HAL 进行交互。如果与 Vehicle HAL 集成,它能够监控车辆的状态变化(如档位变化、转向信号等),并根据这些变化动态调整 EVS 流程。例如,在倒车时(GEAR_REVERSE),它可能会自动启动显示倒车影像。

4 CAR.EVS

CAR.EVS进程指的是 CarEvsService.java,它是 Framework 层中的 EVS 服务类。该服务由 CarEvsManager 类进行管理,且 CarEvsPreviewActivity 通过 CarEvsManager 直接调用 EVS 服务。该部分内容比较多且重要,将不在本篇博客进行分析。

5 总结

本篇博客通过日志信息分析了整个Evs系统的启动流程,并分别介绍了其重要的组成部分:直接与硬件打交道的# android.hardware.automotive.evs-aidl-default-service进程;管理和连接硬件服务的evsmanagerd;与 Vehicle HAL 的通信EvsApp;Framework层的CAR.EVS。