يراقب Watchdog سلامة خدمات المورّد وخدمة VHAL، ويوقف أي عملية غير سليمة. عند إنهاء عملية غير سليمة، يرسل Watchdog حالة العملية إلى /data/anr
كما هو الحال مع عمليات تفريغ أخطاء "التطبيق لا يستجيب" (ANR) الأخرى. يؤدي ذلك إلى تسهيل عملية تصحيح الأخطاء.
مراقبة سلامة خدمة البائع
تتم مراقبة خدمات المورِّدين على كل من الجانب الأصلي وجانب Java. لكي تتم مراقبة خدمة المورّد، يجب أن تسجّل الخدمة عملية فحص السلامة لدى Watchdog من خلال تحديد مهلة زمنية محددة مسبقًا. يراقب Watchdog حالة عملية فحص الصحة المسجّلة من خلال إرسال طلبات ping إليها على فترات زمنية مرتبطة بمهلة انتهاء الوقت المحدّدة أثناء التسجيل. عندما لا تستجيب عملية تم إرسال إشارة إليها خلال المهلة المحدّدة، يتم اعتبار العملية غير سليمة.
مراقبة سلامة الخدمة الأصلية
تحديد ملف makefile الخاص بـ Watchdog AIDL
- أدرِج
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
- لإضافة سياسة 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
- في
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
.
- ابحث عن البرنامج الخفي بالاسم واستدعِ
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
- عدِّل الملف الجديد على النحو التالي:
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() {} };
تسجيل العميل
- الاتصال بـ "
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); }
إلغاء تسجيل العميل
- الاتصال بالرقم
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 حسب المورّد. استخدِم نماذج الرموز البرمجية التالية كمرجع.
- سجِّل سمة المركبة
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; }
- عدِّل
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)); } }
- (اختياري) حدِّد معدّل تكرار عملية التحقّق من صحة VHAL.
تحدّد سمة المنتج
ro.carwatchdog.vhal_healthcheck.interval
للقراءة فقط في Watchdog عدد مرّات التحقّق من سلامة VHAL. معدّل التكرار التلقائي لعمليات التحقّق من سلامة الجهاز (عندما لا يتم تحديد هذه السمة) هو ثلاث ثوانٍ. إذا لم تكن ثلاث ثوانٍ كافية لكي تعدّل خدمة VHALVHAL_HEARTBEAT
سمة المركبة، حدِّد معدّل تكرار عملية التحقّق من سلامة VHAL استنادًا إلى سرعة استجابة الخدمة.
تصحيح الأخطاء في العمليات غير السليمة التي أوقفها برنامج Watchdog
تتخلص خدمة Watchdog من حالة العملية وتنهي العمليات غير السليمة. عند إنهاء عملية غير سليمة، يسجّل Watchdog النص carwatchdog terminated
<process name> (pid:<process id>)
في logcat. يوفّر سطر السجلّ هذا معلومات عن العملية التي تم إنهاؤها، مثل اسم العملية ورقم تعريفها.
- يمكن البحث في 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)
- لتحديد السبب الرئيسي لعدم الاستجابة، استخدِم تفريغ العملية المخزَّن في
/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 الخاص بالعملية التي تم إنهاؤها.