از نگهبان خودرو برای کمک به رفع اشکال VHAL استفاده کنید. ناظر خودرو بر سلامت فرآیندهای ناسالم نظارت می کند - و آنها را می کشد. برای اینکه فرآیندی توسط سازمان دیده بان خودرو نظارت شود، فرآیند باید در سازمان دیده بان خودرو ثبت شود. هنگامی که ناظر خودرو فرآیندهای ناسالم را از بین میبرد، سازمان دیدهبان خودرو وضعیت فرآیندها را مانند سایر برنامههای کاربردی بدون پاسخ (ANR) روی data/anr
مینویسد. انجام این کار فرآیند اشکال زدایی را تسهیل می کند.
این مقاله توضیح میدهد که چگونه HALها و خدمات فروشنده میتوانند فرآیندی را در ناظر خودرو ثبت کنند.
فروشنده HAL
به طور معمول، فروشنده HAL از یک thread pool برای hwbinder
استفاده می کند. با این حال، مشتری نگهبان خودرو با دیمون نگهبان خودرو از طریق binder
ارتباط برقرار می کند که با hwbinder
متفاوت است. بنابراین، استخر نخ دیگری برای binder
در حال استفاده است.
کمک نگهبان خودرو را در makefile مشخص کنید
- شامل
carwatchdog_aidl_interface-ndk_platform
درshared_libs
:Android.bp
:cc_defaults { name: "vhal_v2_0_defaults", shared_libs: [ "libbinder_ndk", "libhidlbase", "liblog", "libutils", "android.hardware.automotive.vehicle@2.0", "carwatchdog_aidl_interface-ndk_platform", ], cflags: [ "-Wall", "-Wextra", "-Werror", ], }
یک سیاست SELinux اضافه کنید
- به
system_server
اجازه دهید HAL شما را بکشد. اگرsystem_server.te
ندارید، یکی ایجاد کنید. اکیداً توصیه می شود که یک خط مشی SELinux را به هر دستگاه اضافه کنید. - به فروشنده HAL اجازه دهید از binder (
binder_use
macro) استفاده کند و فروشنده HAL را به دامنه مشتریcarwatchdog
(carwatchdog_client_domain
macro) اضافه کند. کد زیر را برایsystemserver.te
وvehicle_default.te
ببینید:system_server.te
# Allow system_server to kill vehicle HAL allow system_server hal_vehicle_server:process sigkill;
hal_vehicle_default.te
# Configuration for register VHAL to car watchdog carwatchdog_client_domain(hal_vehicle_default) binder_use(hal_vehicle_default)
با به ارث بردن BnCarWatchdogClient یک کلاس کلاینت را پیاده سازی کنید
- در
checkIfAlive
، بررسی سلامت را انجام دهید. به عنوان مثال، به کنترل کننده حلقه موضوع ارسال کنید. اگر سالم هستید، باICarWatchdog::tellClientAlive
تماس بگیرید. کد زیر را برایWatchogClient.h
وWatchogClient.cpp
ببینید:WatchogClient.h
class WatchdogClient : public aidl::android::automotive::watchdog::BnCarWatchdogClient { public: explicit WatchdogClient(const ::android::sp<::android::Looper>& handlerLooper, VehicleHalManager* vhalManager);
ndk::ScopedAStatus checkIfAlive(int32_t sessionId, aidl::android::automotive::watchdog::TimeoutLength timeout) override; ndk::ScopedAStatus prepareProcessTermination() override; };WatchogClient.cpp
ndk::ScopedAStatus WatchdogClient::checkIfAlive(int32_t sessionId, TimeoutLength /*timeout*/) { // Implement or call your health check logic here return ndk::ScopedAStatus::ok(); }
رشته کلاسور را راه اندازی کنید و مشتری را ثبت کنید
- یک مخزن نخ برای ارتباط بایندر ایجاد کنید. اگر فروشنده HAL از hwbinder برای هدف خود استفاده می کند، باید یک Thread Pool دیگر برای ارتباط بایندر نگهبان خودرو ایجاد کنید.
- دیمون را با نام جستجو کنید و با
ICarWatchdog::registerClient
تماس بگیرید. نام واسط شبح نگهبان خودروandroid.automotive.watchdog.ICarWatchdog/default
است. - بر اساس پاسخگویی سرویس، یکی از سه نوع مهلت زمانی زیر را که توسط ناظر خودرو پشتیبانی می شود انتخاب کنید و سپس در تماس با
ICarWatchdog::registerClient
، مهلت زمانی را بگذرانید:- بحرانی (3s)
- متوسط (5 ثانیه)
- عادی (10 ثانیه)
VehicleService.cpp
وWatchogClient.cpp
ببینید:VehicleService.cpp
int main(int /* argc */, char* /* argv */ []) { // Set up thread pool for hwbinder configureRpcThreadpool(4, false /* callerWillJoin */); ALOGI("Registering as service..."); status_t status = service->registerAsService(); if (status != OK) { ALOGE("Unable to register vehicle service (%d)", status); return 1; } // Setup a binder thread pool to be a car watchdog client. ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); sp<Looper> looper(Looper::prepare(0 /* opts */)); std::shared_ptr<WatchdogClient> watchdogClient = ndk::SharedRefBase::make<WatchdogClient>(looper, service.get()); // The current health check is done in the main thread, so it falls short of capturing the real // situation. Checking through HAL binder thread should be considered. if (!watchdogClient->initialize()) { ALOGE("Failed to initialize car watchdog client"); return 1; } ALOGI("Ready"); while (true) { looper->pollAll(-1 /* timeoutMillis */); } return 1; }
WatchogClient.cpp
bool WatchdogClient::initialize() { ndk::SpAIBinder binder(AServiceManager_getService("android.automotive.watchdog.ICarWatchdog/default")); if (binder.get() == nullptr) { ALOGE("Failed to get carwatchdog daemon"); return false; } std::shared_ptr<ICarWatchdog> server = ICarWatchdog::fromBinder(binder); if (server == nullptr) { ALOGE("Failed to connect to carwatchdog daemon"); return false; } mWatchdogServer = server; binder = this->asBinder(); if (binder.get() == nullptr) { ALOGE("Failed to get car watchdog client binder object"); return false; } std::shared_ptr<ICarWatchdogClient> client = ICarWatchdogClient::fromBinder(binder); if (client == nullptr) { ALOGE("Failed to get ICarWatchdogClient from binder"); return false; } mTestClient = client; mWatchdogServer->registerClient(client, TimeoutLength::TIMEOUT_NORMAL); ALOGI("Successfully registered the client to car watchdog server"); return true; }
خدمات فروشنده (بومی)
فایل makefile car watchdog idl را مشخص کنید
- شامل
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
(macrocarwatchdog_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); }
یک thread بایندر راه اندازی کنید و مشتری را ثبت کنید
نام واسط شبح نگهبان خودرو 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); }
خدمات فروشنده (اندروید)
با به ارث بردن 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); }
فرآیندهای پایان یافته توسط ناظر خودرو را شناسایی کنید
نگهبان خودرو فرآیندهایی (HAL فروشنده، سرویسهای بومی فروشنده، سرویسهای Android فروشنده) را که در ناظر خودرو ثبت میشوند، زمانی که گیر میکنند و پاسخ نمیدهند، از بین میبرد. چنین تخلیه ای با بررسی logcat ها تشخیص داده می شود. هنگامی که یک فرآیند مشکل ساز از بین می رود یا کشته می شود، سازمان دیده بان خودرو یک log carwatchdog killed process_name (pid:process_id)
خروجی می دهد. بنابراین:
$ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"
سیاهههای مربوطه گرفته شده است. به عنوان مثال، اگر برنامه KitchenSink (یک مشتری نگهبان خودرو) گیر کند، خطی مانند زیر در گزارش نوشته می شود:
05-01 09:50:19.683 578 5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)
برای تعیین اینکه چرا یا کجا برنامه KitchenSink گیر کرده است، از فرآیند تخلیه ذخیره شده در /data/anr
استفاده کنید، همانطور که از موارد Activity 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 -----
فایل dump را پیدا کنید (به عنوان مثال /data/anr/anr_2020-05-01-09-50-18-290
در مثال بالا) و تجزیه و تحلیل خود را شروع کنید.