Monitoruj stan systemu

Watchdog monitoruje kondycję usług dostawcy i usługi VHAL oraz kończy wszelkie niezdrowe procesy. Po zakończeniu niezdrowego procesu funkcja Watchdog zrzuca stan procesu do /data/anr podobnie jak w przypadku innych zrzutów aplikacji nie odpowiadających (ANR). Ułatwia to proces debugowania.

Monitorowanie stanu usług dostawcy

Usługi dostawców są monitorowane zarówno po stronie natywnej, jak i Java. Aby usługa dostawcy mogła być monitorowana, usługa musi zarejestrować proces sprawdzania stanu w Watchdogu, określając wstępnie zdefiniowany limit czasu. Watchdog monitoruje kondycję zarejestrowanego procesu sprawdzania kondycji, wysyłając do niego polecenie ping z interwałem odpowiadającym limitowi czasu określonemu podczas rejestracji. Jeśli pingowany proces nie odpowiada w określonym czasie, proces jest uważany za niezdrowy.

Natywne monitorowanie stanu usług

Określ plik makefile AIDL Watchdog

  1. Dołącz carwatchdog_aidl_interface-ndk_platform do shared_libs .

    Android.bp

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

Dodaj politykę SELinux

  1. Aby dodać politykę SELinux, zezwól domenie usługi dostawcy na używanie bindera (makro binder_use ) i dodaj domenę usługi dostawcy do domeny klienta carwatchdog (makro carwatchdog_client_domain ). Zobacz poniższy kod dla sample_client.te i 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
    

Zaimplementuj klasę klienta, dziedzicząc BnCarWatchdogClient

  1. W checkIfAlive przeprowadź kontrolę stanu. Jedną z opcji jest wysłanie wiadomości do procedury obsługi pętli wątku. Jeśli wszystko jest w porządku, zadzwoń do ICarWatchdog::tellClientAlive . Zobacz poniższy kod dla SampleNativeClient.h i 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);
    }
    

Rozpocznij wątek segregatora i zarejestruj klienta

Nazwa interfejsu demona watchdoga samochodu to android.automotive.watchdog.ICarWatchdog/default .

  1. Wyszukaj demona o nazwie i wywołaj ICarWatchdog::registerClient . Zobacz poniższy kod dla main.cpp i 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);
    }
    

Monitorowanie kondycji usług Java

Zaimplementuj klienta, dziedzicząc CarWatchdogClientCallback

  1. Edytuj nowy plik w następujący sposób:
    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() {}
    };
    

Zarejestruj klienta

  1. Wywołaj 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);
    }
    

Wyrejestruj klienta

  1. Wywołaj CarWatchdogManager.unregisterClient() po zakończeniu usługi:
    private void finishClient() {
        CarWatchdogManager manager =
            (CarWatchdogManager) car.getCarManager(
            Car.CAR_WATCHDOG_SERVICE);
        manager.unregisterClient(mClientCallback);
    }
    

Monitorowanie stanu VHAL

W odróżnieniu od monitorowania kondycji usług dostawcy, Watchdog monitoruje kondycję usług VHAL poprzez subskrybowanie właściwości pojazdu VHAL_HEARTBEAT . Watchdog oczekuje, że wartość tej właściwości będzie aktualizowana co N sekund. Jeśli puls nie zostanie zaktualizowany w tym czasie, Watchdog kończy usługę VHAL.

Uwaga: Watchdog monitoruje stan usługi VHAL tylko wtedy, gdy właściwość pojazdu VHAL_HEARTBEAT jest obsługiwana przez usługę VHAL.

Wewnętrzna implementacja VHAL może się różnić w zależności od dostawcy. Użyj poniższych przykładów kodu jako odwołań.

  1. Zarejestruj właściwość pojazdu VHAL_HEARTBEAT .

    Uruchamiając usługę VHAL, zarejestruj właściwość pojazdu VHAL_HEARTBEAT . W poniższym przykładzie unordered_map , który odwzorowuje identyfikator właściwości na config, służy do przechowywania wszystkich obsługiwanych konfiguracji. Konfiguracja dla VHAL_HEARTBEAT została dodana do mapy, więc po zapytaniu o VHAL_HEARTBEAT zwrócona zostanie odpowiednia konfiguracja.

    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. Zaktualizuj właściwość pojazdu VHAL_HEARTBEAT .

    W oparciu o częstotliwość sprawdzania stanu VHAL (wyjaśnione w sekcji Definiowanie częstotliwości sprawdzania stanu VHAL” ), aktualizuj właściwość pojazdu VHAL_HEARTBEAT co N sekund. Jednym ze sposobów osiągnięcia tego jest użycie RecurrentTimer do wywołania akcji sprawdzającej stan i stan VHAL aktualizuje właściwość pojazdu VHAL_HEARTBEAT w ramach limitu czasu.

    Poniżej pokazano przykładową implementację przy użyciu 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. ( Opcjonalnie ) Określ częstotliwość kontroli stanu VHAL.

    Właściwość produktu tylko do odczytu ro.carwatchdog.vhal_healthcheck.interval firmy Watchdog definiuje częstotliwość sprawdzania stanu VHAL. Domyślna częstotliwość sprawdzania stanu (jeśli ta właściwość nie jest zdefiniowana) wynosi trzy sekundy. Jeśli trzy sekundy nie wystarczą, aby usługa VHAL zaktualizowała właściwość pojazdu VHAL_HEARTBEAT , zdefiniuj częstotliwość sprawdzania stanu VHAL w zależności od czasu reakcji usługi.

Debuguj niezdrowe procesy zakończone przez Watchdog

Watchdog zrzuca stan procesu i kończy niezdrowe procesy. Podczas kończenia niezdrowego procesu Watchdog rejestruje w logcat tekst carwatchdog terminated <process name> (pid:<process id>) . Ta linia dziennika zawiera informacje o zakończonym procesie, takie jak nazwa procesu i identyfikator procesu.

  1. Logcat można przeszukać pod kątem powyższego tekstu, uruchamiając:
    $ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"
    

    Na przykład, gdy aplikacja KitchenSink jest zarejestrowanym klientem Watchdog i przestaje odpowiadać na pingi Watchdog, Watchdog rejestruje wiersz taki jak poniższy podczas kończenia zarejestrowanego procesu KitchenSink.

    05-01 09:50:19.683   578  5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)
    
  2. Aby zidentyfikować główną przyczynę braku odpowiedzi, użyj zrzutu procesu przechowywanego w /data/anr tak samo jak w przypadku przypadków ANR związanych z aktywnością. Aby pobrać plik zrzutu dla zakończonego procesu, użyj poniższych poleceń.
    $ adb root
    $ adb shell grep -Hn "pid process_pid" /data/anr/*
    

    Poniższe przykładowe dane wyjściowe dotyczą aplikacji 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 -----
    

    Plik zrzutu zakończonego procesu KitchenSink znajduje się pod adresem /data/anr/anr_2020-05-01-09-50-18-290 . Rozpocznij analizę, korzystając z pliku zrzutu ANR zakończonego procesu.