Bộ đếm giờ phòng vệ của ô tô

Sử dụng dịch vụ theo dõi tình trạng xe để gỡ lỗi VHAL. Bộ đếm giờ phòng vệ của ô tô theo dõi tình trạng của và tiêu diệt các quy trình không lành mạnh. Để bộ đếm giờ phòng vệ ô tô giám sát một quy trình, bạn phải đăng ký quy trình đó với bộ đếm giờ phòng vệ ô tô. Khi trình giám sát ô tô loại bỏ các quy trình không lành mạnh, trình giám sát ô tô sẽ ghi trạng thái của các quy trình đó vào data/anr như với các tệp báo lỗi Ứng dụng không phản hồi (ANR) khác. Làm như vậy sẽ thiết lập cơ sở cho quá trình gỡ lỗi.

Bài viết này mô tả cách các dịch vụ và HAL của nhà cung cấp có thể đăng ký một quy trình với bộ đếm giờ phòng vệ của ô tô.

HAL của nhà cung cấp

Thông thường, HAL của nhà cung cấp sử dụng nhóm luồng cho hwbinder. Tuy nhiên, ứng dụng của bộ đếm giờ phòng vệ cho ô tô giao tiếp với trình nền của bộ đếm giờ phòng vệ cho ô tô thông qua binder, khác với hwbinder. Do đó, một nhóm luồng khác cho binder đang được sử dụng.

Chỉ định aidl của bộ đếm giờ phòng vệ của ô tô trong tệp makefile

  1. Đưa carwatchdog_aidl_interface-ndk_platform vào shared_libs:

    Android.bp:

    cc_defaults {
        name: "vhal_v2_0_defaults",
        shared_libs: [
            "libbinder_ndk",
            "libhidlbase",
            "liblog",
            "libutils",
            "android.hardware.automotive.vehicle@2.0",
            "carwatchdog_aidl_interface-ndk_platform",
        ],
        cflags: [
            "-Wall",
            "-Wextra",
            "-Werror",
        ],
    }
    

Thêm chính sách SELinux

  1. Cho phép system_server loại bỏ HAL của bạn. Nếu bạn chưa có system_server.te, hãy tạo một tài khoản. Bạn nên thêm chính sách SELinux vào từng thiết bị.
  2. Cho phép HAL của nhà cung cấp sử dụng trình liên kết (macro binder_use) và thêm HAL của nhà cung cấp vào miền ứng dụng carwatchdog (macro carwatchdog_client_domain). Xem mã bên dưới cho systemserver.tevehicle_default.te:

    system_server.te

    # Allow system_server to kill vehicle HAL
    allow system_server hal_vehicle_server:process sigkill;
    

    hal_vehicle_default.te

    # Configuration for register VHAL to car watchdog
    carwatchdog_client_domain(hal_vehicle_default)
    binder_use(hal_vehicle_default)
    

Triển khai một lớp ứng dụng bằng cách kế thừa BnCarWatchdogClient

  1. Trong checkIfAlive, hãy kiểm tra tình trạng. Ví dụ: đăng lên trình xử lý vòng lặp luồng. Nếu khỏe mạnh, hãy gọi ICarWatchdog::tellClientAlive. Xem mã bên dưới cho WatchogClient.hWatchogClient.cpp:

    WatchogClient.h

    class WatchdogClient : public aidl::android::automotive::watchdog::BnCarWatchdogClient {
      public:
        explicit WatchdogClient(const ::android::sp<::android::Looper>& handlerLooper, VehicleHalManager* vhalManager);
    
    ndk::ScopedAStatus checkIfAlive(int32_t sessionId, aidl::android::automotive::watchdog::TimeoutLength timeout) override; ndk::ScopedAStatus prepareProcessTermination() override; };

    WatchogClient.cpp

    ndk::ScopedAStatus WatchdogClient::checkIfAlive(int32_t sessionId, TimeoutLength /*timeout*/) {
        // Implement or call your health check logic here
        return ndk::ScopedAStatus::ok();
    }
    

Bắt đầu luồng liên kết và đăng ký ứng dụng

  1. Tạo một nhóm luồng để giao tiếp với liên kết. Nếu HAL của nhà cung cấp sử dụng hwbinder cho mục đích riêng, bạn phải tạo một nhóm luồng khác để giao tiếp với trình liên kết chó săn xe).
  2. Tìm trình nền có tên và gọi ICarWatchdog::registerClient. Tên giao diện trình nền theo dõi tình trạng xe là android.automotive.watchdog.ICarWatchdog/default.
  3. Dựa trên khả năng phản hồi của dịch vụ, hãy chọn một trong ba loại thời gian chờ sau đây mà trình giám sát xe hỗ trợ, sau đó truyền thời gian chờ trong lệnh gọi đến ICarWatchdog::registerClient:
    • critical(3s)
    • trung bình(5 giây)
    • bình thường(10 giây)
    Xem mã bên dưới cho VehicleService.cppWatchogClient.cpp:

    XeService.cpp

    int main(int /* argc */, char* /* argv */ []) {
        // Set up thread pool for hwbinder
        configureRpcThreadpool(4, false /* callerWillJoin */);
    
        ALOGI("Registering as service...");
        status_t status = service->registerAsService();
    
        if (status != OK) {
            ALOGE("Unable to register vehicle service (%d)", status);
            return 1;
        }
    
        // Setup a binder thread pool to be a car watchdog client.
        ABinderProcess_setThreadPoolMaxThreadCount(1);
        ABinderProcess_startThreadPool();
        sp<Looper> looper(Looper::prepare(0 /* opts */));
        std::shared_ptr<WatchdogClient> watchdogClient =
                ndk::SharedRefBase::make<WatchdogClient>(looper, service.get());
        // The current health check is done in the main thread, so it falls short of capturing the real
        // situation. Checking through HAL binder thread should be considered.
        if (!watchdogClient->initialize()) {
            ALOGE("Failed to initialize car watchdog client");
            return 1;
        }
        ALOGI("Ready");
        while (true) {
            looper->pollAll(-1 /* timeoutMillis */);
        }
    
        return 1;
    }
    

    WatchogClient.cpp

    bool WatchdogClient::initialize() {
        ndk::SpAIBinder binder(AServiceManager_getService("android.automotive.watchdog.ICarWatchdog/default"));
        if (binder.get() == nullptr) {
            ALOGE("Failed to get carwatchdog daemon");
            return false;
        }
        std::shared_ptr<ICarWatchdog> server = ICarWatchdog::fromBinder(binder);
        if (server == nullptr) {
            ALOGE("Failed to connect to carwatchdog daemon");
            return false;
        }
        mWatchdogServer = server;
    
        binder = this->asBinder();
        if (binder.get() == nullptr) {
            ALOGE("Failed to get car watchdog client binder object");
            return false;
        }
        std::shared_ptr<ICarWatchdogClient> client = ICarWatchdogClient::fromBinder(binder);
        if (client == nullptr) {
            ALOGE("Failed to get ICarWatchdogClient from binder");
            return false;
        }
        mTestClient = client;
        mWatchdogServer->registerClient(client, TimeoutLength::TIMEOUT_NORMAL);
        ALOGI("Successfully registered the client to car watchdog server");
        return true;
    }
    

Dịch vụ của nhà cung cấp (Gốc)

Chỉ định tệp makefile aidl của car watchdog

  1. Đưa carwatchdog_aidl_interface-ndk_platform vào shared_libs.

    Android.bp

    cc_binary {
        name: "sample_native_client",
        srcs: [
            "src/*.cpp"
        ],
        shared_libs: [
            "carwatchdog_aidl_interface-ndk_platform",
            "libbinder_ndk",
        ],
        vendor: true,
    }
    

Thêm chính sách SELinux

  1. Để thêm chính sách SELinux, hãy cho phép miền dịch vụ của nhà cung cấp sử dụng trình liên kết (macro binder_use) và thêm miền dịch vụ của nhà cung cấp vào miền ứng dụng carwatchdog (macro carwatchdog_client_domain). Xem mã bên dưới cho sample_client.tefile_contexts:

    sample_client.te

    type sample_client, domain;
    type sample_client_exec, exec_type, file_type, vendor_file_type;
    
    carwatchdog_client_domain(sample_client)
    
    init_daemon_domain(sample_client)
    binder_use(sample_client)
    

    file_contexts

    /vendor/bin/sample_native_client  u:object_r:sample_client_exec:s0
    

Triển khai lớp ứng dụng khách bằng cách kế thừa BnCarWatchdogClient

  1. Trong checkIfAlive, hãy kiểm tra tình trạng. Một cách là đăng lên trình xử lý vòng lặp luồng. Nếu khỏe mạnh, hãy gọi ICarWatchdog::tellClientAlive. Xem mã bên dưới cho SampleNativeClient.hSampleNativeClient.cpp:

    SampleNativeClient.h

    class SampleNativeClient : public BnCarWatchdogClient {
    public:
        ndk::ScopedAStatus checkIfAlive(int32_t sessionId, TimeoutLength
            timeout) override;
        ndk::ScopedAStatus prepareProcessTermination() override;
        void initialize();
    
    private:
        void respondToDaemon();
    private:
        ::android::sp<::android::Looper> mHandlerLooper;
        std::shared_ptr<ICarWatchdog> mWatchdogServer;
        std::shared_ptr<ICarWatchdogClient> mClient;
        int32_t mSessionId;
    };
    

    SampleNativeClient.cpp

    ndk::ScopedAStatus WatchdogClient::checkIfAlive(int32_t sessionId, TimeoutLength timeout) {
        mHandlerLooper->removeMessages(mMessageHandler,
            WHAT_CHECK_ALIVE);
        mSessionId = sessionId;
        mHandlerLooper->sendMessage(mMessageHandler,
            Message(WHAT_CHECK_ALIVE));
        return ndk::ScopedAStatus::ok();
    }
    // WHAT_CHECK_ALIVE triggers respondToDaemon from thread handler
    void WatchdogClient::respondToDaemon() {
      // your health checking method here
      ndk::ScopedAStatus status = mWatchdogServer->tellClientAlive(mClient,
            mSessionId);
    }
    

Bắt đầu một luồng liên kết và đăng ký ứng dụng

Tên giao diện trình nền theo dõi tình trạng xe là android.automotive.watchdog.ICarWatchdog/default.

  1. Tìm trình nền có tên và gọi ICarWatchdog::registerClient. Xem mã bên dưới cho main.cppSampleNativeClient.cpp:

    main.cpp

    int main(int argc, char** argv) {
        sp<Looper> looper(Looper::prepare(/*opts=*/0));
    
        ABinderProcess_setThreadPoolMaxThreadCount(1);
        ABinderProcess_startThreadPool();
        std::shared_ptr<SampleNativeClient> client =
            ndk::SharedRefBase::make<SampleNatvieClient>(looper);
    
        // The client is registered in initialize()
        client->initialize();
        ...
    }
    

    SampleNativeClient.cpp

    void SampleNativeClient::initialize() {
        ndk::SpAIBinder binder(AServiceManager_getService(
            "android.automotive.watchdog.ICarWatchdog/default"));
        std::shared_ptr<ICarWatchdog> server =
            ICarWatchdog::fromBinder(binder);
        mWatchdogServer = server;
        ndk::SpAIBinder binder = this->asBinder();
        std::shared_ptr<ICarWatchdogClient> client =
            ICarWatchdogClient::fromBinder(binder)
        mClient = client;
        server->registerClient(client, TimeoutLength::TIMEOUT_NORMAL);
    }
    

Dịch vụ của nhà cung cấp (Android)

Triển khai ứng dụng bằng cách kế thừa CarWatchdogClientCallback

  1. Chỉnh sửa tệp mới như sau:
    private final CarWatchdogClientCallback mClientCallback = new CarWatchdogClientCallback() {
        @Override
        public boolean onCheckHealthStatus(int sessionId, int timeout) {
            // Your health check logic here
            // Returning true implies the client is healthy
            // If false is returned, the client should call
            // CarWatchdogManager.tellClientAlive after health check is
            // completed
        }
    
        @Override
        public void onPrepareProcessTermination() {}
    };
    

Đăng ký ứng dụng

  1. Gọi CarWatchdogManager.registerClient():
    private void startClient() {
        CarWatchdogManager manager =
            (CarWatchdogManager) car.getCarManager(
            Car.CAR_WATCHDOG_SERVICE);
        // Choose a proper executor according to your health check method
        ExecutorService executor = Executors.newFixedThreadPool(1);
        manager.registerClient(executor, mClientCallback,
            CarWatchdogManager.TIMEOUT_NORMAL);
    }
    

Huỷ đăng ký ứng dụng

  1. Gọi CarWatchdogManager.unregisterClient() khi dịch vụ hoàn tất:
    private void finishClient() {
        CarWatchdogManager manager =
            (CarWatchdogManager) car.getCarManager(
            Car.CAR_WATCHDOG_SERVICE);
        manager.unregisterClient(mClientCallback);
    }
    

Phát hiện các quy trình do bộ đếm giờ phòng vệ của ô tô chấm dứt

Quy trình kết xuất/xoá bộ đếm giờ phòng vệ của ô tô (HAL của nhà cung cấp, dịch vụ gốc của nhà cung cấp, dịch vụ Android của nhà cung cấp) được đăng ký với bộ đếm giờ phòng vệ của ô tô khi chúng bị lỗi và không phản hồi. Bạn có thể phát hiện hoạt động kết xuất như vậy bằng cách kiểm tra logcats. Bộ đếm giờ phòng vệ của ô tô sẽ xuất ra nhật ký carwatchdog killed process_name (pid:process_id) khi một quy trình có vấn đề bị kết xuất hoặc bị dừng. Do đó:

$ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"

Các nhật ký có liên quan sẽ được ghi lại. Ví dụ: nếu ứng dụng KitchenSink (một ứng dụng giám sát ô tô) bị treo, thì một dòng như dưới đây sẽ được ghi vào nhật ký:

05-01 09:50:19.683   578  5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)

Để xác định lý do hoặc vị trí ứng dụng KitchenSink gặp sự cố, hãy sử dụng tệp kết xuất quy trình được lưu trữ tại /data/anr giống như cách bạn sử dụng các trường hợp ANR hoạt động.

$ adb root
$ adb shell grep -Hn "pid process_pid" /data/anr/*

Kết quả mẫu sau đây dành riêng cho ứng dụng KitchenSink:

$ adb shell su root grep -Hn "pid 5574" /data/anr/*.
/data/anr/anr_2020-05-01-09-50-18-290:3:----- pid 5574 at 2020-05-01 09:50:18 -----
/data/anr/anr_2020-05-01-09-50-18-290:285:----- Waiting Channels: pid 5574 at 2020-05-01 09:50:18 -----

Tìm tệp kết xuất (ví dụ: /data/anr/anr_2020-05-01-09-50-18-290 trong ví dụ trên) và bắt đầu phân tích.