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

Sử dụng bộ đếm giờ phòng vệ của ô tô để giúp gỡ lỗi VHAL. Thiết bị giám sát bộ đếm giờ phòng vệ của ô tô sức khoẻ và giết chết những quy trình không lành mạnh. Để có quy trình được giám sát bộ đếm giờ phòng vệ ô tô phải đăng ký quy trình này với bộ đếm giờ phòng vệ ô tô. Thời gian bộ đếm giờ phòng vệ ô tô hủy các quy trình không lành mạnh, bộ đếm giờ phòng vệ ô tô ghi trạng thái của các quy trình xảy ra với data/anr cũng như với các ứng dụng khác không phản hồi Tệp kết xuất (ANR). 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 HAL và dịch vụ của nhà cung cấp có thể đăng ký quy trình với bộ đếm giờ phòng vệ của ô tô.

Lớp trừu tượng phần cứng (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 bộ đếm giờ phòng vệ của ô tô giao tiếp với trình nền của bộ đếm giờ phòng vệ của ô 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. Chắc chắn là 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 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 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. Ví dụ: đăng lên trình xử lý vòng lặp luồng. Nếu tình trạng hoạt động tốt, 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 liên kết. Nếu HAL của nhà cung cấp sử dụng hwbinder cho 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 của bộ đếm giờ phòng vệ cho ô tô).
  2. Tìm trình nền có tên và gọi ICarWatchdog::registerClient. Tên giao diện trình nền của bộ đếm giờ phòng vệ của ô tô 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 được bộ đếm giờ phòng vệ của ô tô hỗ trợ rồi chuyển thời gian chờ trong lệnh gọi đến ICarWatchdog::registerClient:
    • quan trọng(3)
    • 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 của bộ đếm giờ phòng vệ của ô tô

  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:

    mẫu_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 (ngữ cảnh tệp tin)

    /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 lựa chọn là đăng lên trình xử lý vòng lặp luồng. Nếu tình trạng hoạt động tốt, 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 của bộ đếm giờ phòng vệ của ô tô 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 ngừng hoạt động/xoá của 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 được bị nghẽn và không phản hồi. Việc kết xuất như vậy được phát hiện bằng cách kiểm tra logcat. Ô tô bộ đếm giờ phòng vệ 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 bộ đếm giờ phòng vệ của ô tô ứng dụng khách) bị lỗi, một dòng như bên dưới đượ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 nguyên nhân hoặc vị trí ứng dụng KitchenSink gặp sự cố, hãy 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.