مراقبة حالة النظام

يراقب Watchdog سلامة خدمات المورّد وخدمة VHAL، ويوقف أي عملية غير سليمة. عند إنهاء عملية غير سليمة، يرسل Watchdog حالة العملية إلى /data/anr كما هو الحال مع عمليات تفريغ أخطاء "التطبيق لا يستجيب" (ANR) الأخرى. يؤدي ذلك إلى تسهيل عملية تصحيح الأخطاء.

مراقبة سلامة خدمة البائع

تتم مراقبة خدمات المورِّدين على كل من الجانب الأصلي وجانب Java. لكي تتم مراقبة خدمة المورّد، يجب أن تسجّل الخدمة عملية فحص السلامة لدى Watchdog من خلال تحديد مهلة زمنية محددة مسبقًا. يراقب Watchdog حالة عملية فحص الصحة المسجّلة من خلال إرسال طلبات ping إليها على فترات زمنية مرتبطة بمهلة انتهاء الوقت المحدّدة أثناء التسجيل. عندما لا تستجيب عملية تم إرسال إشارة إليها خلال المهلة المحدّدة، يتم اعتبار العملية غير سليمة.

مراقبة سلامة الخدمة الأصلية

تحديد ملف makefile الخاص بـ Watchdog AIDL

  1. أدرِج carwatchdog_aidl_interface-ndk_platform في shared_libs.

    Android.bp

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

إضافة سياسة SELinux

  1. لإضافة سياسة SELinux، اسمح لنطاق خدمة المورّد باستخدام binder (وحدة الماكرو binder_use) وأضِف نطاق خدمة المورّد إلى نطاق العميل carwatchdog (وحدة الماكرو carwatchdog_client_domain). اطّلِع على الرمز أدناه الخاص بـ sample_client.te و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

تنفيذ فئة عميل من خلال وراثة BnCarWatchdogClient

  1. في checkIfAlive، نفِّذ عملية تحقّق من الصحة. أحد الخيارات هو نشر المحتوى في معالج حلقة سلاسل المحادثات. إذا كنت بصحة جيدة، اتصِل بالرقم ICarWatchdog::tellClientAlive. اطّلِع على الرمز أدناه الخاص بـ SampleNativeClient.h و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);
    }

بدء سلسلة ربط وتسجيل العميل

اسم واجهة البرنامج الخفي لمراقب نظام السيارة هو android.automotive.watchdog.ICarWatchdog/default.

  1. ابحث عن البرنامج الخفي بالاسم واستدعِ ICarWatchdog::registerClient. اطّلِع على الرمز أدناه الخاص بـ main.cpp و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);
    }

مراقبة سلامة خدمة Java

تنفيذ عميل من خلال وراثة CarWatchdogClientCallback

  1. عدِّل الملف الجديد على النحو التالي:
    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() {}
    };

تسجيل العميل

  1. الاتصال بـ "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);
    }

إلغاء تسجيل العميل

  1. الاتصال بالرقم CarWatchdogManager.unregisterClient() عند الانتهاء من الخدمة:
    private void finishClient() {
        CarWatchdogManager manager =
            (CarWatchdogManager) car.getCarManager(
            Car.CAR_WATCHDOG_SERVICE);
        manager.unregisterClient(mClientCallback);
    }

مراقبة سلامة VHAL

على عكس مراقبة حالة خدمة المورّد، يراقب Watchdog حالة خدمة VHAL من خلال الاشتراك في سمة المركبة VHAL_HEARTBEAT. تتوقّع خدمة Watchdog تعديل قيمة هذه السمة مرة واحدة كل N ثانية. عندما لا يتم تعديل إشارة نبض القلب خلال مهلة الانتظار هذه، ينهي Watchdog خدمة VHAL.

ملاحظة: يراقب Watchdog حالة خدمة VHAL فقط عندما تكون السمة VHAL_HEARTBEAT للمركبة متوافقة مع خدمة VHAL.

يمكن أن تختلف عملية التنفيذ الداخلية لواجهة VHAL حسب المورّد. استخدِم نماذج الرموز البرمجية التالية كمرجع.

  1. سجِّل سمة المركبة VHAL_HEARTBEAT.

    عند بدء خدمة VHAL، سجِّل سمة المركبة VHAL_HEARTBEAT. في المثال أدناه، يتم استخدام unordered_map، الذي يربط رقم تعريف الموقع بالتهيئة، للاحتفاظ بجميع عمليات التهيئة المتوافقة. تتم إضافة إعدادات VHAL_HEARTBEAT إلى الخريطة، وبالتالي عند طلب VHAL_HEARTBEAT، يتم عرض إعدادات الضبط المطابقة.

    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. عدِّل VHAL_HEARTBEAT خاصية المركبة.

    استنادًا إلى معدّل تكرار عملية التحقّق من سلامة VHAL (الموضّح في تحديد معدّل تكرار عملية التحقّق من سلامة VHAL)، عدِّل سمة المركبة VHAL_HEARTBEAT مرة واحدة كل N ثانية. يمكنك إجراء ذلك باستخدام RecurrentTimer لاستدعاء الإجراء الذي يتحقّق من حالة VHAL ويعدّل السمة VHAL_HEARTBEAT للمركبة خلال المهلة المحدّدة.

    في ما يلي نموذج تنفيذ باستخدام 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. (اختياري) حدِّد معدّل تكرار عملية التحقّق من صحة VHAL.

    تحدّد سمة المنتج ro.carwatchdog.vhal_healthcheck.interval للقراءة فقط في Watchdog عدد مرّات التحقّق من سلامة VHAL. معدّل التكرار التلقائي لعمليات التحقّق من سلامة الجهاز (عندما لا يتم تحديد هذه السمة) هو ثلاث ثوانٍ. إذا لم تكن ثلاث ثوانٍ كافية لكي تعدّل خدمة VHAL VHAL_HEARTBEAT سمة المركبة، حدِّد معدّل تكرار عملية التحقّق من سلامة VHAL استنادًا إلى سرعة استجابة الخدمة.

تصحيح الأخطاء في العمليات غير السليمة التي أوقفها برنامج Watchdog

تتخلص خدمة Watchdog من حالة العملية وتنهي العمليات غير السليمة. عند إنهاء عملية غير سليمة، يسجّل Watchdog النص carwatchdog terminated <process name> (pid:<process id>) في logcat. يوفّر سطر السجلّ هذا معلومات عن العملية التي تم إنهاؤها، مثل اسم العملية ورقم تعريفها.

  1. يمكن البحث في logcat عن النص المذكور أعلاه من خلال تنفيذ ما يلي:
    $ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"

    على سبيل المثال، عندما يكون تطبيق KitchenSink عميلاً مسجّلاً في Watchdog ويصبح غير مستجيب لطلبات ping من Watchdog، يسجّل Watchdog سطرًا مثل السطر أدناه عند إنهاء عملية KitchenSink المسجّلة.

    05-01 09:50:19.683   578  5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)
  2. لتحديد السبب الرئيسي لعدم الاستجابة، استخدِم تفريغ العملية المخزَّن في /data/anr كما تفعل في حالات ANR المتعلقة بالنشاط. لاسترداد ملف تفريغ العملية التي تم إنهاؤها، استخدِم الأوامر أدناه.
    $ adb root
    $ adb shell grep -Hn "pid process_pid" /data/anr/*

    يكون ناتج العيّنة التالي خاصًا بتطبيق 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 -----

    يقع ملف تفريغ عملية KitchenSink التي تم إنهاؤها في /data/anr/anr_2020-05-01-09-50-18-290. ابدأ التحليل باستخدام ملف تفريغ ANR الخاص بالعملية التي تم إنهاؤها.