Sử dụng bộ đếm giờ phòng vệ của ô tô để giúp gỡ lỗi VHAL. Thiết bị giám sát bộ đếm giờ phòng vệ của ô tô
sức khoẻ và giết chết những quy trình không lành mạnh. Để có quy trình được giám sát
bộ đếm giờ phòng vệ ô tô phải đăng ký quy trình này với bộ đếm giờ phòng vệ ô tô. Thời gian
bộ đếm giờ phòng vệ ô tô hủy các quy trình không lành mạnh, bộ đếm giờ phòng vệ ô tô ghi trạng thái của
các quy trình xảy ra với data/anr
cũng như với các ứng dụng khác không phản hồi
Tệp kết xuất (ANR). Làm như vậy sẽ thiết lập cơ sở cho quá trình gỡ lỗi.
Bài viết này mô tả cách HAL và dịch vụ của nhà cung cấp có thể đăng ký quy trình với bộ đếm giờ phòng vệ của ô tô.
Lớp trừu tượng phần cứng (HAL) của nhà cung cấp
Thông thường, HAL của nhà cung cấp sử dụng nhóm luồng cho hwbinder
. Tuy nhiên,
ứng dụng bộ đếm giờ phòng vệ của ô tô giao tiếp với trình nền của bộ đếm giờ phòng vệ của ô tô thông qua
binder
, khác với hwbinder
. Do đó,
một nhóm luồng khác cho binder
đang được sử dụng.
Chỉ định aidl của bộ đếm giờ phòng vệ của ô tô trong tệp makefile
- Đưa
carwatchdog_aidl_interface-ndk_platform
vàoshared_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", ], }
Thêm chính sách SELinux
-
Cho phép
system_server
loại bỏ HAL của bạn. Nếu bạn chưa cósystem_server.te
, hãy tạo một tài khoản. Chắc chắn là bạn nên thêm chính sách SELinux vào từng thiết bị. -
Cho phép HAL của nhà cung cấp sử dụng liên kết (macro
binder_use
) và thêm HAL của nhà cung cấp vào miền ứng dụngcarwatchdog
(macrocarwatchdog_client_domain
). Xem mã bên dưới chosystemserver.te
và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)
Triển khai lớp ứng dụng khách bằng cách kế thừa BnCarWatchdogClient
-
Trong
checkIfAlive
, hãy kiểm tra tình trạng. Ví dụ: đăng lên trình xử lý vòng lặp luồng. Nếu tình trạng hoạt động tốt, hãy gọiICarWatchdog::tellClientAlive
. Xem mã bên dưới choWatchogClient.h
và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(); }
Bắt đầu luồng liên kết và đăng ký ứng dụng
- Tạo một nhóm luồng để giao tiếp liên kết. Nếu HAL của nhà cung cấp sử dụng hwbinder cho bạn phải tạo một nhóm luồng khác để giao tiếp với trình liên kết của bộ đếm giờ phòng vệ cho ô tô).
-
Tìm trình nền theo tên và gọi
ICarWatchdog::registerClient
. Tên giao diện trình nền của bộ đếm giờ phòng vệ của ô tô làandroid.automotive.watchdog.ICarWatchdog/default
. -
Dựa trên khả năng phản hồi của dịch vụ, hãy chọn một trong ba loại thời gian chờ sau đây
được bộ đếm giờ phòng vệ của ô tô hỗ trợ rồi chuyển thời gian chờ trong lệnh gọi đến
ICarWatchdog::registerClient
:- quan trọng(3)
- trung bình(5 giây)
- bình thường(10 giây)
VehicleService.cpp
vàWatchogClient.cpp
:XeService.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; }
Dịch vụ của nhà cung cấp (Gốc)
Chỉ định tệp makefile của bộ đếm giờ phòng vệ của ô tô
- Đưa
carwatchdog_aidl_interface-ndk_platform
vàoshared_libs
.Android.bp
cc_binary { name: "sample_native_client", srcs: [ "src/*.cpp" ], shared_libs: [ "carwatchdog_aidl_interface-ndk_platform", "libbinder_ndk", ], vendor: true, }
Thêm chính sách SELinux
- Để thêm chính sách SELinux, hãy cho phép miền dịch vụ của nhà cung cấp sử dụng trình liên kết
(macro
binder_use
) và thêm miền dịch vụ của nhà cung cấp vào Miền ứng dụngcarwatchdog
(macrocarwatchdog_client_domain
). Xem mã bên dưới chosample_client.te
vàfile_contexts
:mẫu_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 (ngữ cảnh tệp tin)
/vendor/bin/sample_native_client u:object_r:sample_client_exec:s0
Triển khai lớp ứng dụng khách bằng cách kế thừa BnCarWatchdogClient
- Trong
checkIfAlive
, hãy kiểm tra tình trạng. Một lựa chọn là đăng lên trình xử lý vòng lặp luồng. Nếu tình trạng hoạt động tốt, hãy gọiICarWatchdog::tellClientAlive
. Xem mã bên dưới choSampleNativeClient.h
và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); }
Bắt đầu một luồng liên kết và đăng ký ứng dụng
Tên giao diện trình nền của bộ đếm giờ phòng vệ của ô tô là
android.automotive.watchdog.ICarWatchdog/default
.
- Tìm trình nền có tên và gọi
ICarWatchdog::registerClient
. Xem mã bên dưới chomain.cpp
và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); }
Dịch vụ của nhà cung cấp (Android)
Triển khai ứng dụng bằng cách kế thừa CarWatchdogClientCallback
- Chỉnh sửa tệp mới như sau:
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() {} };
Đăng ký ứng dụng
- Gọi
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); }
Huỷ đăng ký ứng dụng
- Gọi
CarWatchdogManager.unregisterClient()
khi dịch vụ hoàn tất:private void finishClient() { CarWatchdogManager manager = (CarWatchdogManager) car.getCarManager( Car.CAR_WATCHDOG_SERVICE); manager.unregisterClient(mClientCallback); }
Phát hiện các quy trình do bộ đếm giờ phòng vệ của ô tô chấm dứt
Quy trình ngừng hoạt động của bộ phận giám sát ô tô (HAL của nhà cung cấp, dịch vụ gốc của nhà cung cấp,
dịch vụ Android của nhà cung cấp) được đăng ký với bộ đếm giờ phòng vệ của ô tô khi chúng được
bị nghẽn và không phản hồi. Việc kết xuất như vậy được phát hiện bằng cách kiểm tra logcat. Ô tô
bộ đếm giờ phòng vệ xuất ra nhật ký carwatchdog killed process_name (pid:process_id)
khi một quy trình có vấn đề bị kết xuất hoặc bị dừng. Do đó:
$ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"
Các nhật ký có liên quan sẽ được ghi lại. Ví dụ: nếu ứng dụng KitchenSink (một bộ đếm giờ phòng vệ của ô tô ứng dụng khách) bị lỗi, một dòng như bên dưới được ghi vào nhật ký:
05-01 09:50:19.683 578 5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)
Để xác định nguyên nhân hoặc vị trí ứng dụng KitchenSink gặp sự cố, hãy dùng tệp kết xuất quy trình
được lưu trữ tại /data/anr
giống như cách bạn sử dụng các trường hợp ANR hoạt động.
$ adb root $ adb shell grep -Hn "pid process_pid" /data/anr/*
Kết quả mẫu sau đây dành riêng cho ứng dụng 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 -----
Tìm tệp kết xuất (ví dụ: /data/anr/anr_2020-05-01-09-50-18-290
trong ví dụ ở trên) và bắt đầu phân tích.