Memantau kesehatan sistem

Watchdog memantau kondisi layanan vendor dan layanan VHAL, serta menghentikan proses yang tidak responsif. Saat proses yang tidak sehat dihentikan, Watchdog akan mencatat status proses ke /data/anr seperti halnya pencatatan error Aplikasi Tidak Merespons (ANR) lainnya. Dengan demikian, proses debug akan lebih mudah.

Pemantauan kondisi layanan vendor

Layanan vendor dipantau di sisi native dan Java. Agar layanan Vendor dapat dipantau, layanan harus mendaftarkan proses pemeriksaan kondisi dengan Watchdog dengan menentukan waktu tunggu yang telah ditentukan sebelumnya. Watchdog memantau kesehatan proses pemeriksaan kesehatan yang terdaftar dengan melakukan ping pada interval relatif terhadap waktu tunggu yang ditentukan selama pendaftaran. Jika proses yang di-ping tidak merespons dalam waktu tunggu, proses tersebut dianggap tidak responsif.

Pemantauan kondisi layanan native

Tentukan makefile AIDL Watchdog

  1. Sertakan carwatchdog_aidl_interface-ndk_platform di shared_libs.

    Android.bp

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

Menambahkan kebijakan SELinux

  1. Untuk menambahkan kebijakan SELinux, izinkan domain layanan vendor menggunakan binder (makro binder_use) dan tambahkan domain layanan vendor ke domain klien carwatchdog (makro carwatchdog_client_domain). Lihat kode di bawah untuk sample_client.te dan file_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

Mengimplementasikan class klien dengan mewarisi BnCarWatchdogClient

  1. Di checkIfAlive, lakukan health check. Salah satu opsinya adalah memposting ke handler loop rangkaian pesan. Jika sehat, panggil ICarWatchdog::tellClientAlive. Lihat kode di bawah untuk SampleNativeClient.h dan SampleNativeClient.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);
    }

Mulai rangkaian pesan binder dan daftarkan klien

Nama antarmuka daemon watchdog mobil adalah android.automotive.watchdog.ICarWatchdog/default.

  1. Telusuri daemon dengan nama dan panggil ICarWatchdog::registerClient. Lihat kode di bawah untuk main.cpp dan SampleNativeClient.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);
    }

Pemantauan kondisi layanan Java

Mengimplementasikan klien dengan mewarisi CarWatchdogClientCallback

  1. Edit file baru sebagai berikut:
    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() {}
    };

Mendaftarkan klien

  1. Hubungi 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);
    }

Membatalkan pendaftaran klien

  1. Panggil CarWatchdogManager.unregisterClient() saat layanan selesai:
    private void finishClient() {
        CarWatchdogManager manager =
            (CarWatchdogManager) car.getCarManager(
            Car.CAR_WATCHDOG_SERVICE);
        manager.unregisterClient(mClientCallback);
    }

Pemantauan kondisi VHAL

Tidak seperti pemantauan kondisi layanan vendor, Watchdog memantau kondisi layanan VHAL dengan berlangganan properti kendaraan VHAL_HEARTBEAT. Watchdog mengharapkan nilai properti ini diperbarui sekali setiap N detik. Jika heartbeat tidak diperbarui dalam waktu tunggu ini, Watchdog akan menghentikan layanan VHAL.

Catatan: Watchdog memantau kondisi layanan VHAL hanya jika properti kendaraan VHAL_HEARTBEAT didukung oleh layanan VHAL.

Implementasi internal VHAL dapat bervariasi menurut vendor. Gunakan contoh kode berikut sebagai referensi.

  1. Daftarkan properti kendaraan VHAL_HEARTBEAT.

    Saat memulai layanan VHAL, daftarkan properti kendaraan VHAL_HEARTBEAT. Pada contoh di bawah, unordered_map, yang memetakan ID properti ke konfigurasi, digunakan untuk menyimpan semua konfigurasi yang didukung. Konfigurasi untuk VHAL_HEARTBEAT ditambahkan ke peta, sehingga saat VHAL_HEARTBEAT dikueri, konfigurasi yang sesuai akan ditampilkan.

    void registerVhalHeartbeatProperty() {
            const VehiclePropConfig config = {
                    .prop = toInt(VehicleProperty::VHAL_HEARTBEAT),
                    .access = VehiclePropertyAccess::READ,
                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
            };
           // mConfigsById is declared as std::unordered_map<int32_t, VehiclePropConfig>.
           mConfigsById[config.prop] = config;
    }
  2. Perbarui properti kendaraan VHAL_HEARTBEAT.

    Berdasarkan frekuensi health check VHAL (dijelaskan dalam Tentukan frekuensi health check VHAL"), perbarui properti kendaraan VHAL_HEARTBEAT sekali setiap N detik. Salah satu cara untuk melakukannya adalah dengan menggunakan RecurrentTimer untuk memanggil tindakan yang memeriksa kondisi VHAL dan memperbarui properti kendaraan VHAL_HEARTBEAT dalam batas waktu.

    Di bawah ini adalah contoh implementasi menggunakan RecurrentTimer:

    int main(int argc, char** argv) {
            RecurrentTimer recurrentTimer(updateVhalHeartbeat);
            recurrentTimer.registerRecurrentEvent(kHeartBeatIntervalNs,
                                               static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT));
             Run service 
            recurrentTimer.unregisterRecurrentEvent(
                    static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT));
    }
    
    void updateVhalHeartbeat(const std::vector<int32_t>& cookies) {
           for (int32_t property : cookies) {
                  if (property != static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT)) {
                         continue;
                  }
    
                  // Perform internal health checking such as retrieving a vehicle property to ensure
                  // the service is responsive.
                  doHealthCheck();
    
                  // Construct the VHAL_HEARTBEAT property with system uptime.
                  VehiclePropValuePool valuePool;
                  VehicleHal::VehiclePropValuePtr propValuePtr = valuePool.obtainInt64(uptimeMillis());
                  propValuePtr->prop = static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT);
                  propValuePtr->areaId = 0;
                  propValuePtr->status = VehiclePropertyStatus::AVAILABLE;
                  propValuePtr->timestamp = elapsedRealtimeNano();
    
                  // Propagate the HAL event.
                  onHalEvent(std::move(propValuePtr));
           }
    }
  3. (Opsional) Tentukan frekuensi health check VHAL.

    Properti produk hanya baca ro.carwatchdog.vhal_healthcheck.interval Watchdog menentukan frekuensi health check VHAL. Frekuensi health check default (jika properti ini tidak ditentukan) adalah tiga detik. Jika tiga detik tidak cukup bagi layanan VHAL untuk memperbarui properti kendaraan VHAL_HEARTBEAT, tentukan frekuensi health check VHAL bergantung pada responsivitas layanan.

Men-debug proses tidak responsif yang dihentikan oleh Watchdog

Watchdog akan melakukan dump status proses dan menghentikan proses yang tidak responsif. Saat menghentikan proses yang tidak responsif, Watchdog mencatat teks carwatchdog terminated <process name> (pid:<process id>) ke logcat. Baris log ini memberikan informasi tentang proses yang dihentikan seperti nama proses dan ID proses.

  1. Logcat dapat ditelusuri untuk menemukan teks yang disebutkan di atas dengan menjalankan:
    $ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"

    Misalnya, saat aplikasi KitchenSink adalah klien Watchdog yang terdaftar dan tidak responsif terhadap ping Watchdog, Watchdog akan mencatat baris seperti di bawah saat menghentikan proses KitchenSink yang terdaftar.

    05-01 09:50:19.683   578  5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)
  2. Untuk mengidentifikasi penyebab utama tidak responsifnya aplikasi, gunakan dump proses yang disimpan di /data/anr seperti yang Anda lakukan untuk kasus ANR aktivitas. Untuk mengambil file dump untuk proses yang dihentikan, gunakan perintah di bawah.
    $ adb root
    $ adb shell grep -Hn "pid process_pid" /data/anr/*

    Contoh output berikut khusus untuk aplikasi 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 -----

    File dump untuk proses KitchenSink yang dihentikan terletak di /data/anr/anr_2020-05-01-09-50-18-290. Mulai analisis Anda menggunakan file dump ANR proses yang dihentikan.