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

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

مراقبة سلامة خدمة المورّد

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

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

تحديد ملف make الخاص بـ 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_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);
    }
    

مراقبة حالة "القيادة في حالات الازدحام"

على عكس مراقبة حالة خدمة المورّد، تتحقّق أداة Watchdog من حالة خدمة VHAL من خلال الاشتراك في سمة المركبة VHAL_HEARTBEAT. يتوقع مراقب النظام أن يتم تعديل قيمة هذه السمة مرة واحدة كل N ثانية. عندما لا يتم تحديث معدّل نبضات القلب خلال هذه المهلة، تُنهي مراقب النظام خدمة 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 للمراقبة فقط في أداة "الحارس" معدّل تكرار التحقّق من حالة VHAL. المدة التلقائية لفحص الحالة (عندما لا يتم تحديد هذه السمة) هي ثلاث ثوانٍ. إذا لم تكن ثلاث ثوانٍ كافية لخدمة VHAL لتعديل سمة VHAL_HEARTBEAT المركبة، حدِّد معدّل تكرار التحقّق من حالة VHAL استنادًا إلى استجابة الخدمة.

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

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

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

    على سبيل المثال، عندما يكون تطبيق KitchenSink برنامجًا مسجَّلاً في مراقب النظام ويصبح غير مستجيب لإشعارات مراقب النظام، تسجِّل الأداة سطرًا مثل السطر أدناه عند إنهاء عملية 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 dump الخاص بالعملية التي تم إنهاءها.