From 562c0f803579a5f9814a0426db90e0b401b17196 Mon Sep 17 00:00:00 2001 From: Yu Shan Date: Thu, 4 Aug 2022 17:10:38 -0700 Subject: [PATCH 1/3] Define remoteaccess grpc HAL. Define a remote access HAL that talks with a wakeup client on TCU through grpc. Test: make android.hardware.automotive.remoteaccess Bug: 241483300 Change-Id: I3bc6c8b837c157744f1d2b5f2e9c287e44a33883 --- .../impl/default/client/Android.bp | 54 +++++++++++++++++++ .../client/include/RemoteAccessService.h | 51 ++++++++++++++++++ .../client/remoteaccess-default-service.rc | 4 ++ .../client/remoteaccess-default-service.xml | 7 +++ .../default/client/src/RemoteAccessImpl.cpp | 53 ++++++++++++++++++ .../client/src/RemoteAccessService.cpp | 53 ++++++++++++++++++ .../{grpc => impl/default}/proto/Android.bp | 0 .../default}/proto/wakeup_client.proto | 0 8 files changed, 222 insertions(+) create mode 100644 automotive/remoteaccess/impl/default/client/Android.bp create mode 100644 automotive/remoteaccess/impl/default/client/include/RemoteAccessService.h create mode 100644 automotive/remoteaccess/impl/default/client/remoteaccess-default-service.rc create mode 100644 automotive/remoteaccess/impl/default/client/remoteaccess-default-service.xml create mode 100644 automotive/remoteaccess/impl/default/client/src/RemoteAccessImpl.cpp create mode 100644 automotive/remoteaccess/impl/default/client/src/RemoteAccessService.cpp rename automotive/remoteaccess/{grpc => impl/default}/proto/Android.bp (100%) rename automotive/remoteaccess/{grpc => impl/default}/proto/wakeup_client.proto (100%) diff --git a/automotive/remoteaccess/impl/default/client/Android.bp b/automotive/remoteaccess/impl/default/client/Android.bp new file mode 100644 index 0000000000..38c79af686 --- /dev/null +++ b/automotive/remoteaccess/impl/default/client/Android.bp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_binary { + name: "android.hardware.automotive.remoteaccess@V1-default-service", + vendor: true, + vintf_fragments: ["remoteaccess-default-service.xml"], + init_rc: ["remoteaccess-default-service.rc"], + relative_install_path: "hw", + srcs: ["src/RemoteAccessImpl.cpp"], + whole_static_libs: [ + "RemoteAccessService", + ], + shared_libs: [ + "libbinder_ndk", + "liblog", + "libutils", + ], +} + +cc_library { + name: "RemoteAccessService", + vendor: true, + local_include_dirs: ["include"], + export_include_dirs: ["include"], + srcs: [ + "src/RemoteAccessService.cpp", + ], + whole_static_libs: [ + "android.hardware.automotive.remoteaccess-V1-ndk", + ], + shared_libs: [ + "libbinder_ndk", + "liblog", + "libutils", + ], +} diff --git a/automotive/remoteaccess/impl/default/client/include/RemoteAccessService.h b/automotive/remoteaccess/impl/default/client/include/RemoteAccessService.h new file mode 100644 index 0000000000..c54aefe312 --- /dev/null +++ b/automotive/remoteaccess/impl/default/client/include/RemoteAccessService.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace android { +namespace hardware { +namespace automotive { +namespace remoteaccess { + +class RemoteAccessService + : public aidl::android::hardware::automotive::remoteaccess::BnRemoteAccess { + public: + ndk::ScopedAStatus getDeviceId(std::string* deviceId) override; + + ndk::ScopedAStatus getWakeupServiceName(std::string* wakeupServerName) override; + + ndk::ScopedAStatus setRemoteTaskCallback( + const std::shared_ptr< + aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback>& + callback) override; + + ndk::ScopedAStatus clearRemoteTaskCallback() override; + + ndk::ScopedAStatus notifyApStateChange( + const aidl::android::hardware::automotive::remoteaccess::ApState& newState) override; +}; + +} // namespace remoteaccess +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/remoteaccess/impl/default/client/remoteaccess-default-service.rc b/automotive/remoteaccess/impl/default/client/remoteaccess-default-service.rc new file mode 100644 index 0000000000..b7a9cdc82a --- /dev/null +++ b/automotive/remoteaccess/impl/default/client/remoteaccess-default-service.rc @@ -0,0 +1,4 @@ +service vendor.remoteaccess-default /vendor/bin/hw/android.hardware.automotive.remoteaccess@V1-default-service + class hal + user vehicle_network + group system inet diff --git a/automotive/remoteaccess/impl/default/client/remoteaccess-default-service.xml b/automotive/remoteaccess/impl/default/client/remoteaccess-default-service.xml new file mode 100644 index 0000000000..d050a1b646 --- /dev/null +++ b/automotive/remoteaccess/impl/default/client/remoteaccess-default-service.xml @@ -0,0 +1,7 @@ + + + android.hardware.automotive.remoteaccess + 1 + IRemoteAccess/default + + diff --git a/automotive/remoteaccess/impl/default/client/src/RemoteAccessImpl.cpp b/automotive/remoteaccess/impl/default/client/src/RemoteAccessImpl.cpp new file mode 100644 index 0000000000..91a6419b02 --- /dev/null +++ b/automotive/remoteaccess/impl/default/client/src/RemoteAccessImpl.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "RemoteAccessImpl" + +#include "RemoteAccessService.h" + +#include +#include +#include + +int main(int /* argc */, char* /* argv */[]) { + ALOGI("Registering RemoteAccessService as service..."); + + auto service = ndk::SharedRefBase::make< + android::hardware::automotive::remoteaccess::RemoteAccessService>(); + + binder_exception_t err = AServiceManager_addService( + service->asBinder().get(), "android.hardware.automotive.remote.IRemoteAccess/default"); + if (err != EX_NONE) { + ALOGE("failed to register android.hardware.automotive.remote.IRemoteAccess service, " + "exception: %d", + err); + return 1; + } + + if (!ABinderProcess_setThreadPoolMaxThreadCount(1)) { + ALOGE("%s", "failed to set thread pool max thread count"); + return 1; + } + ABinderProcess_startThreadPool(); + + ALOGI("RemoteAccess service Ready"); + + ABinderProcess_joinThreadPool(); + + ALOGW("Should not reach here"); + + return 0; +} diff --git a/automotive/remoteaccess/impl/default/client/src/RemoteAccessService.cpp b/automotive/remoteaccess/impl/default/client/src/RemoteAccessService.cpp new file mode 100644 index 0000000000..e24293f846 --- /dev/null +++ b/automotive/remoteaccess/impl/default/client/src/RemoteAccessService.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "RemoteAccessService.h" + +namespace android { +namespace hardware { +namespace automotive { +namespace remoteaccess { + +using ::aidl::android::hardware::automotive::remoteaccess::ApState; +using ::aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback; +using ::ndk::ScopedAStatus; + +ScopedAStatus RemoteAccessService::getDeviceId([[maybe_unused]] std::string* deviceId) { + return ScopedAStatus::ok(); +} + +ScopedAStatus RemoteAccessService::getWakeupServiceName( + [[maybe_unused]] std::string* wakeupServiceName) { + return ScopedAStatus::ok(); +} + +ScopedAStatus RemoteAccessService::setRemoteTaskCallback( + [[maybe_unused]] const std::shared_ptr& callback) { + return ScopedAStatus::ok(); +} + +ScopedAStatus RemoteAccessService::clearRemoteTaskCallback() { + return ScopedAStatus::ok(); +} + +ScopedAStatus RemoteAccessService::notifyApStateChange([[maybe_unused]] const ApState& newState) { + return ScopedAStatus::ok(); +} + +} // namespace remoteaccess +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/remoteaccess/grpc/proto/Android.bp b/automotive/remoteaccess/impl/default/proto/Android.bp similarity index 100% rename from automotive/remoteaccess/grpc/proto/Android.bp rename to automotive/remoteaccess/impl/default/proto/Android.bp diff --git a/automotive/remoteaccess/grpc/proto/wakeup_client.proto b/automotive/remoteaccess/impl/default/proto/wakeup_client.proto similarity index 100% rename from automotive/remoteaccess/grpc/proto/wakeup_client.proto rename to automotive/remoteaccess/impl/default/proto/wakeup_client.proto From e29745606af6a5cd0cde77a6852466c33ac74e64 Mon Sep 17 00:00:00 2001 From: Yu Shan Date: Wed, 10 Aug 2022 18:37:03 -0700 Subject: [PATCH 2/3] Implement the GrpcWakeupClient. Implement a remoteaccess HAL implementation that talks with the wake up client using grpc. Test: local build. Bug: 241483300 Change-Id: I02a51f41427fa2854930d2076ca3a49791488fd9 --- .../impl/default/client/Android.bp | 12 +++ .../client/include/RemoteAccessService.h | 14 ++- .../default/client/src/RemoteAccessImpl.cpp | 3 +- .../client/src/RemoteAccessService.cpp | 34 ++++++- .../impl/default/proto/Android.bp | 4 + .../remoteaccess/impl/default/test/Android.bp | 43 +++++++++ .../test/RemoteAccessServiceUnitTest.cpp | 90 +++++++++++++++++++ 7 files changed, 195 insertions(+), 5 deletions(-) create mode 100644 automotive/remoteaccess/impl/default/test/Android.bp create mode 100644 automotive/remoteaccess/impl/default/test/RemoteAccessServiceUnitTest.cpp diff --git a/automotive/remoteaccess/impl/default/client/Android.bp b/automotive/remoteaccess/impl/default/client/Android.bp index 38c79af686..6327637941 100644 --- a/automotive/remoteaccess/impl/default/client/Android.bp +++ b/automotive/remoteaccess/impl/default/client/Android.bp @@ -32,6 +32,11 @@ cc_binary { "libbinder_ndk", "liblog", "libutils", + "libgrpc++", + "libprotobuf-cpp-full", + ], + cflags: [ + "-Wno-unused-parameter", ], } @@ -45,10 +50,17 @@ cc_library { ], whole_static_libs: [ "android.hardware.automotive.remoteaccess-V1-ndk", + "wakeup_client_protos", ], shared_libs: [ + "libbase", "libbinder_ndk", "liblog", "libutils", + "libgrpc++", + "libprotobuf-cpp-full", + ], + cflags: [ + "-Wno-unused-parameter", ], } diff --git a/automotive/remoteaccess/impl/default/client/include/RemoteAccessService.h b/automotive/remoteaccess/impl/default/client/include/RemoteAccessService.h index c54aefe312..f060b6ca98 100644 --- a/automotive/remoteaccess/impl/default/client/include/RemoteAccessService.h +++ b/automotive/remoteaccess/impl/default/client/include/RemoteAccessService.h @@ -19,8 +19,10 @@ #include #include #include +#include #include +#include namespace android { namespace hardware { @@ -30,9 +32,11 @@ namespace remoteaccess { class RemoteAccessService : public aidl::android::hardware::automotive::remoteaccess::BnRemoteAccess { public: + RemoteAccessService(WakeupClient::StubInterface* grpcStub); + ndk::ScopedAStatus getDeviceId(std::string* deviceId) override; - ndk::ScopedAStatus getWakeupServiceName(std::string* wakeupServerName) override; + ndk::ScopedAStatus getWakeupServiceName(std::string* wakeupServiceName) override; ndk::ScopedAStatus setRemoteTaskCallback( const std::shared_ptr< @@ -43,6 +47,14 @@ class RemoteAccessService ndk::ScopedAStatus notifyApStateChange( const aidl::android::hardware::automotive::remoteaccess::ApState& newState) override; + + private: + WakeupClient::StubInterface* mGrpcStub; + std::shared_ptr + mRemoteTaskCallback; + std::thread mThread; + + void taskLoop(); }; } // namespace remoteaccess diff --git a/automotive/remoteaccess/impl/default/client/src/RemoteAccessImpl.cpp b/automotive/remoteaccess/impl/default/client/src/RemoteAccessImpl.cpp index 91a6419b02..743189854b 100644 --- a/automotive/remoteaccess/impl/default/client/src/RemoteAccessImpl.cpp +++ b/automotive/remoteaccess/impl/default/client/src/RemoteAccessImpl.cpp @@ -25,8 +25,9 @@ int main(int /* argc */, char* /* argv */[]) { ALOGI("Registering RemoteAccessService as service..."); + // TODO(b/241483300): Create GrpcClientStub here. auto service = ndk::SharedRefBase::make< - android::hardware::automotive::remoteaccess::RemoteAccessService>(); + android::hardware::automotive::remoteaccess::RemoteAccessService>(nullptr); binder_exception_t err = AServiceManager_addService( service->asBinder().get(), "android.hardware.automotive.remote.IRemoteAccess/default"); diff --git a/automotive/remoteaccess/impl/default/client/src/RemoteAccessService.cpp b/automotive/remoteaccess/impl/default/client/src/RemoteAccessService.cpp index e24293f846..f567113b0e 100644 --- a/automotive/remoteaccess/impl/default/client/src/RemoteAccessService.cpp +++ b/automotive/remoteaccess/impl/default/client/src/RemoteAccessService.cpp @@ -16,30 +16,58 @@ #include "RemoteAccessService.h" +#include +#include +#include +#include +#include + namespace android { namespace hardware { namespace automotive { namespace remoteaccess { +namespace { + using ::aidl::android::hardware::automotive::remoteaccess::ApState; using ::aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback; +using ::grpc::ClientContext; +using ::grpc::ClientReader; +using ::grpc::Status; +using ::grpc::StatusCode; using ::ndk::ScopedAStatus; -ScopedAStatus RemoteAccessService::getDeviceId([[maybe_unused]] std::string* deviceId) { +const std::string WAKEUP_SERVICE_NAME = "com.google.vehicle.wakeup"; + +} // namespace + +RemoteAccessService::RemoteAccessService(WakeupClient::StubInterface* grpcStub) + : mGrpcStub(grpcStub) { + // mThread = std::thread([this]() { taskLoop(); }); +} + +void RemoteAccessService::taskLoop() { + // TODO(b/241483300): handle remote tasks. +} + +ScopedAStatus RemoteAccessService::getDeviceId(std::string* deviceId) { + // TODO(b/241483300): Call VHAL to get VIN. return ScopedAStatus::ok(); } -ScopedAStatus RemoteAccessService::getWakeupServiceName( - [[maybe_unused]] std::string* wakeupServiceName) { +ScopedAStatus RemoteAccessService::getWakeupServiceName(std::string* wakeupServiceName) { + *wakeupServiceName = WAKEUP_SERVICE_NAME; return ScopedAStatus::ok(); } ScopedAStatus RemoteAccessService::setRemoteTaskCallback( [[maybe_unused]] const std::shared_ptr& callback) { + mRemoteTaskCallback = callback; return ScopedAStatus::ok(); } ScopedAStatus RemoteAccessService::clearRemoteTaskCallback() { + mRemoteTaskCallback.reset(); return ScopedAStatus::ok(); } diff --git a/automotive/remoteaccess/impl/default/proto/Android.bp b/automotive/remoteaccess/impl/default/proto/Android.bp index c882f55446..d3c75a65f2 100644 --- a/automotive/remoteaccess/impl/default/proto/Android.bp +++ b/automotive/remoteaccess/impl/default/proto/Android.bp @@ -24,7 +24,9 @@ genrule { ], out: [ "wakeup_client.pb.h", + "wakeup_client.grpc.pb.h", ], + vendor: true, } genrule { @@ -39,7 +41,9 @@ genrule { ], out: [ "wakeup_client.pb.cc", + "wakeup_client.grpc.pb.cc", ], + vendor: true, } cc_library_static { diff --git a/automotive/remoteaccess/impl/default/test/Android.bp b/automotive/remoteaccess/impl/default/test/Android.bp new file mode 100644 index 0000000000..e92440f55f --- /dev/null +++ b/automotive/remoteaccess/impl/default/test/Android.bp @@ -0,0 +1,43 @@ +// Copyright (C) 2022 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_test { + name: "RemoteAccessServiceUnitTest", + vendor: true, + srcs: ["*.cpp"], + whole_static_libs: [ + "RemoteAccessService", + ], + shared_libs: [ + "libbinder_ndk", + "liblog", + "libutils", + "libgrpc++", + "libprotobuf-cpp-full", + ], + // libgrpc++.so is installed as root, require root to access it. + require_root: true, + static_libs: [ + "libgtest", + "libgmock", + ], + cflags: [ + "-Wno-unused-parameter", + ], + test_suites: ["device-tests"], +} diff --git a/automotive/remoteaccess/impl/default/test/RemoteAccessServiceUnitTest.cpp b/automotive/remoteaccess/impl/default/test/RemoteAccessServiceUnitTest.cpp new file mode 100644 index 0000000000..de259271b0 --- /dev/null +++ b/automotive/remoteaccess/impl/default/test/RemoteAccessServiceUnitTest.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "RemoteAccessService.h" + +#include +#include +#include + +namespace android { +namespace hardware { +namespace automotive { +namespace remoteaccess { + +using ::grpc::ClientAsyncReaderInterface; +using ::grpc::ClientAsyncResponseReaderInterface; +using ::grpc::ClientContext; +using ::grpc::ClientReader; +using ::grpc::ClientReaderInterface; +using ::grpc::CompletionQueue; +using ::grpc::Status; + +using ::ndk::ScopedAStatus; + +class MockGrpcClientStub : public WakeupClient::StubInterface { + public: + MOCK_METHOD(ClientReaderInterface*, GetRemoteTasksRaw, + (ClientContext * context, const GetRemoteTasksRequest& request)); + MOCK_METHOD(Status, NotifyWakeupRequired, + (ClientContext * context, const NotifyWakeupRequiredRequest& request, + NotifyWakeupRequiredResponse* response)); + // Async methods which we do not care. + MOCK_METHOD(ClientAsyncReaderInterface*, AsyncGetRemoteTasksRaw, + (ClientContext * context, const GetRemoteTasksRequest& request, CompletionQueue* cq, + void* tag)); + MOCK_METHOD(ClientAsyncReaderInterface*, PrepareAsyncGetRemoteTasksRaw, + (ClientContext * context, const GetRemoteTasksRequest& request, + CompletionQueue* cq)); + MOCK_METHOD(ClientAsyncResponseReaderInterface*, + AsyncNotifyWakeupRequiredRaw, + (ClientContext * context, const NotifyWakeupRequiredRequest& request, + CompletionQueue* cq)); + MOCK_METHOD(ClientAsyncResponseReaderInterface*, + PrepareAsyncNotifyWakeupRequiredRaw, + (ClientContext * context, const NotifyWakeupRequiredRequest& request, + CompletionQueue* cq)); +}; + +class RemoteAccessServiceUnitTest : public ::testing::Test { + public: + RemoteAccessServiceUnitTest() { + mGrpcWakeupClientStub = std::make_unique(); + mService = ndk::SharedRefBase::make(mGrpcWakeupClientStub.get()); + } + + MockGrpcClientStub* getGrpcWakeupClientStub() { return mGrpcWakeupClientStub.get(); } + + RemoteAccessService* getService() { return mService.get(); } + + private: + std::unique_ptr mGrpcWakeupClientStub; + std::shared_ptr mService; +}; + +TEST_F(RemoteAccessServiceUnitTest, TestGetWakeupServiceName) { + std::string serviceName; + + ScopedAStatus status = getService()->getWakeupServiceName(&serviceName); + + EXPECT_TRUE(status.isOk()); + EXPECT_EQ(serviceName, "com.google.vehicle.wakeup"); +} + +} // namespace remoteaccess +} // namespace automotive +} // namespace hardware +} // namespace android From b138c42a0d09516c05ef2bed60a5ed9a6ffc3466 Mon Sep 17 00:00:00 2001 From: Yu Shan Date: Tue, 6 Sep 2022 19:11:23 -0700 Subject: [PATCH 3/3] Implement receiving remote task. Test: atest RemoteAccessServiceUnitTest Bug: 241483300 Change-Id: I3d7f54f154beebba1bb1bdb7640dfe973ad012e4 --- .../client/include/RemoteAccessService.h | 32 ++- .../client/src/RemoteAccessService.cpp | 116 +++++++++- .../test/RemoteAccessServiceUnitTest.cpp | 202 +++++++++++++++++- 3 files changed, 336 insertions(+), 14 deletions(-) diff --git a/automotive/remoteaccess/impl/default/client/include/RemoteAccessService.h b/automotive/remoteaccess/impl/default/client/include/RemoteAccessService.h index f060b6ca98..806440a2df 100644 --- a/automotive/remoteaccess/impl/default/client/include/RemoteAccessService.h +++ b/automotive/remoteaccess/impl/default/client/include/RemoteAccessService.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -32,7 +33,9 @@ namespace remoteaccess { class RemoteAccessService : public aidl::android::hardware::automotive::remoteaccess::BnRemoteAccess { public: - RemoteAccessService(WakeupClient::StubInterface* grpcStub); + explicit RemoteAccessService(WakeupClient::StubInterface* grpcStub); + + ~RemoteAccessService(); ndk::ScopedAStatus getDeviceId(std::string* deviceId) override; @@ -49,12 +52,29 @@ class RemoteAccessService const aidl::android::hardware::automotive::remoteaccess::ApState& newState) override; private: - WakeupClient::StubInterface* mGrpcStub; - std::shared_ptr - mRemoteTaskCallback; - std::thread mThread; + // For testing. + friend class RemoteAccessServiceUnitTest; - void taskLoop(); + WakeupClient::StubInterface* mGrpcStub; + std::thread mThread; + std::mutex mLock; + std::condition_variable mCv; + std::shared_ptr + mRemoteTaskCallback GUARDED_BY(mLock); + std::unique_ptr mGetRemoteTasksContext GUARDED_BY(mLock); + // Associated with mCv to notify the task loop to stop waiting and exit. + bool mTaskWaitStopped GUARDED_BY(mLock); + // A mutex to make sure startTaskLoop does not overlap with stopTaskLoop. + std::mutex mStartStopTaskLoopLock; + bool mTaskLoopRunning GUARDED_BY(mStartStopTaskLoopLock); + // Default wait time before retry connecting to remote access client is 10s. + size_t mRetryWaitInMs = 10'000; + + void runTaskLoop(); + void maybeStartTaskLoop(); + void maybeStopTaskLoop(); + + void setRetryWaitInMs(size_t retryWaitInMs) { mRetryWaitInMs = retryWaitInMs; } }; } // namespace remoteaccess diff --git a/automotive/remoteaccess/impl/default/client/src/RemoteAccessService.cpp b/automotive/remoteaccess/impl/default/client/src/RemoteAccessService.cpp index f567113b0e..6b97840999 100644 --- a/automotive/remoteaccess/impl/default/client/src/RemoteAccessService.cpp +++ b/automotive/remoteaccess/impl/default/client/src/RemoteAccessService.cpp @@ -31,23 +31,111 @@ namespace { using ::aidl::android::hardware::automotive::remoteaccess::ApState; using ::aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback; +using ::android::base::ScopedLockAssertion; using ::grpc::ClientContext; -using ::grpc::ClientReader; +using ::grpc::ClientReaderInterface; using ::grpc::Status; using ::grpc::StatusCode; using ::ndk::ScopedAStatus; const std::string WAKEUP_SERVICE_NAME = "com.google.vehicle.wakeup"; +std::vector stringToBytes(const std::string& s) { + const char* data = s.data(); + return std::vector(data, data + s.size()); +} + +ScopedAStatus rpcStatusToScopedAStatus(const Status& status, const std::string& errorMsg) { + return ScopedAStatus::fromServiceSpecificErrorWithMessage( + status.error_code(), (errorMsg + ", error: " + status.error_message()).c_str()); +} + } // namespace RemoteAccessService::RemoteAccessService(WakeupClient::StubInterface* grpcStub) - : mGrpcStub(grpcStub) { - // mThread = std::thread([this]() { taskLoop(); }); + : mGrpcStub(grpcStub){}; + +RemoteAccessService::~RemoteAccessService() { + maybeStopTaskLoop(); } -void RemoteAccessService::taskLoop() { - // TODO(b/241483300): handle remote tasks. +void RemoteAccessService::maybeStartTaskLoop() { + std::lock_guard lockGuard(mStartStopTaskLoopLock); + if (mTaskLoopRunning) { + return; + } + + mThread = std::thread([this]() { runTaskLoop(); }); + + mTaskLoopRunning = true; +} + +void RemoteAccessService::maybeStopTaskLoop() { + std::lock_guard lockGuard(mStartStopTaskLoopLock); + if (!mTaskLoopRunning) { + return; + } + + { + std::lock_guard lockGuard(mLock); + // Try to stop the reading stream. + if (mGetRemoteTasksContext) { + mGetRemoteTasksContext->TryCancel(); + mGetRemoteTasksContext.reset(); + } + mTaskWaitStopped = true; + mCv.notify_all(); + } + if (mThread.joinable()) { + mThread.join(); + } + + mTaskLoopRunning = false; +} + +void RemoteAccessService::runTaskLoop() { + GetRemoteTasksRequest request = {}; + std::unique_ptr> reader; + while (true) { + { + std::lock_guard lockGuard(mLock); + mGetRemoteTasksContext.reset(new ClientContext()); + reader = mGrpcStub->GetRemoteTasks(mGetRemoteTasksContext.get(), request); + } + GetRemoteTasksResponse response; + while (reader->Read(&response)) { + std::shared_ptr callback; + { + std::lock_guard lockGuard(mLock); + callback = mRemoteTaskCallback; + } + if (callback == nullptr) { + continue; + } + ScopedAStatus callbackStatus = callback->onRemoteTaskRequested( + response.clientid(), stringToBytes(response.data())); + if (!callbackStatus.isOk()) { + ALOGE("Failed to call onRemoteTaskRequested callback, status: %d, message: %s", + callbackStatus.getStatus(), callbackStatus.getMessage()); + } + } + Status status = reader->Finish(); + + ALOGE("GetRemoteTasks stream breaks, code: %d, message: %s, sleeping for 10s and retry", + status.error_code(), status.error_message().c_str()); + // The long lasting connection should not return. But if the server returns, retry after + // 10s. + { + std::unique_lock lk(mLock); + if (mCv.wait_for(lk, std::chrono::milliseconds(mRetryWaitInMs), [this] { + ScopedLockAssertion lockAssertion(mLock); + return mTaskWaitStopped; + })) { + // If the stopped flag is set, we are quitting, exit the loop. + break; + } + } + } } ScopedAStatus RemoteAccessService::getDeviceId(std::string* deviceId) { @@ -62,16 +150,32 @@ ScopedAStatus RemoteAccessService::getWakeupServiceName(std::string* wakeupServi ScopedAStatus RemoteAccessService::setRemoteTaskCallback( [[maybe_unused]] const std::shared_ptr& callback) { + std::lock_guard lockGuard(mLock); mRemoteTaskCallback = callback; return ScopedAStatus::ok(); } ScopedAStatus RemoteAccessService::clearRemoteTaskCallback() { + std::lock_guard lockGuard(mLock); mRemoteTaskCallback.reset(); return ScopedAStatus::ok(); } -ScopedAStatus RemoteAccessService::notifyApStateChange([[maybe_unused]] const ApState& newState) { +ScopedAStatus RemoteAccessService::notifyApStateChange(const ApState& newState) { + ClientContext context; + NotifyWakeupRequiredRequest request = {}; + request.set_iswakeuprequired(newState.isWakeupRequired); + NotifyWakeupRequiredResponse response = {}; + Status status = mGrpcStub->NotifyWakeupRequired(&context, request, &response); + if (!status.ok()) { + return rpcStatusToScopedAStatus(status, "Failed to notify isWakeupRequired"); + } + + if (newState.isReadyForRemoteTask) { + maybeStartTaskLoop(); + } else { + maybeStopTaskLoop(); + } return ScopedAStatus::ok(); } diff --git a/automotive/remoteaccess/impl/default/test/RemoteAccessServiceUnitTest.cpp b/automotive/remoteaccess/impl/default/test/RemoteAccessServiceUnitTest.cpp index de259271b0..11523f69db 100644 --- a/automotive/remoteaccess/impl/default/test/RemoteAccessServiceUnitTest.cpp +++ b/automotive/remoteaccess/impl/default/test/RemoteAccessServiceUnitTest.cpp @@ -16,15 +16,25 @@ #include "RemoteAccessService.h" +#include +#include #include +#include #include #include +#include +#include namespace android { namespace hardware { namespace automotive { namespace remoteaccess { +using ::android::base::ScopedLockAssertion; + +using ::aidl::android::hardware::automotive::remoteaccess::ApState; +using ::aidl::android::hardware::automotive::remoteaccess::BnRemoteTaskCallback; + using ::grpc::ClientAsyncReaderInterface; using ::grpc::ClientAsyncResponseReaderInterface; using ::grpc::ClientContext; @@ -32,8 +42,12 @@ using ::grpc::ClientReader; using ::grpc::ClientReaderInterface; using ::grpc::CompletionQueue; using ::grpc::Status; - +using ::grpc::testing::MockClientReader; using ::ndk::ScopedAStatus; +using ::testing::_; +using ::testing::DoAll; +using ::testing::Return; +using ::testing::SetArgPointee; class MockGrpcClientStub : public WakeupClient::StubInterface { public: @@ -59,9 +73,37 @@ class MockGrpcClientStub : public WakeupClient::StubInterface { CompletionQueue* cq)); }; +class FakeRemoteTaskCallback : public BnRemoteTaskCallback { + public: + ScopedAStatus onRemoteTaskRequested(const std::string& clientId, + const std::vector& data) override { + std::lock_guard lockGuard(mLock); + mDataByClientId[clientId] = data; + mTaskCount++; + mCv.notify_all(); + return ScopedAStatus::ok(); + } + + std::vector getData(const std::string& clientId) { return mDataByClientId[clientId]; } + + bool wait(size_t taskCount, size_t timeoutInSec) { + std::unique_lock lock(mLock); + return mCv.wait_for(lock, std::chrono::seconds(timeoutInSec), [taskCount, this] { + ScopedLockAssertion lockAssertion(mLock); + return mTaskCount >= taskCount; + }); + } + + private: + std::mutex mLock; + std::unordered_map> mDataByClientId GUARDED_BY(mLock); + size_t mTaskCount GUARDED_BY(mLock) = 0; + std::condition_variable mCv; +}; + class RemoteAccessServiceUnitTest : public ::testing::Test { public: - RemoteAccessServiceUnitTest() { + virtual void SetUp() override { mGrpcWakeupClientStub = std::make_unique(); mService = ndk::SharedRefBase::make(mGrpcWakeupClientStub.get()); } @@ -70,9 +112,12 @@ class RemoteAccessServiceUnitTest : public ::testing::Test { RemoteAccessService* getService() { return mService.get(); } + void setRetryWaitInMs(size_t retryWaitInMs) { mService->setRetryWaitInMs(retryWaitInMs); } + private: std::unique_ptr mGrpcWakeupClientStub; std::shared_ptr mService; + MockClientReader* mMockTaskReader; }; TEST_F(RemoteAccessServiceUnitTest, TestGetWakeupServiceName) { @@ -84,6 +129,159 @@ TEST_F(RemoteAccessServiceUnitTest, TestGetWakeupServiceName) { EXPECT_EQ(serviceName, "com.google.vehicle.wakeup"); } +TEST_F(RemoteAccessServiceUnitTest, TestNotifyApStateChangeWakeupRequired) { + bool isWakeupRequired = false; + EXPECT_CALL(*getGrpcWakeupClientStub(), NotifyWakeupRequired) + .WillOnce([&isWakeupRequired]([[maybe_unused]] ClientContext* context, + const NotifyWakeupRequiredRequest& request, + [[maybe_unused]] NotifyWakeupRequiredResponse* response) { + isWakeupRequired = request.iswakeuprequired(); + return Status(); + }); + + ApState newState = { + .isWakeupRequired = true, + }; + ScopedAStatus status = getService()->notifyApStateChange(newState); + + EXPECT_TRUE(status.isOk()); + EXPECT_TRUE(isWakeupRequired); +} + +TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasks) { + GetRemoteTasksResponse response1; + std::vector testData = {0xde, 0xad, 0xbe, 0xef}; + response1.set_clientid("1"); + response1.set_data(testData.data(), testData.size()); + GetRemoteTasksResponse response2; + response2.set_clientid("2"); + std::shared_ptr callback = + ndk::SharedRefBase::make(); + + ON_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw) + .WillByDefault( + [response1, response2]([[maybe_unused]] ClientContext* context, + [[maybe_unused]] const GetRemoteTasksRequest& request) { + // mockReader ownership will be transferred to the client so we don't own it + // here. + MockClientReader* mockClientReader = + new MockClientReader(); + EXPECT_CALL(*mockClientReader, Finish()).WillOnce(Return(Status::OK)); + EXPECT_CALL(*mockClientReader, Read(_)) + .WillOnce(DoAll(SetArgPointee<0>(response1), Return(true))) + .WillOnce(DoAll(SetArgPointee<0>(response2), Return(true))) + .WillRepeatedly(Return(false)); + return mockClientReader; + }); + + getService()->setRemoteTaskCallback(callback); + // Start the long live connection to receive tasks. + ApState newState = { + .isReadyForRemoteTask = true, + }; + ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk()); + + ASSERT_TRUE(callback->wait(/*taskCount=*/2, /*timeoutInSec=*/10)) + << "Did not receive enough tasks"; + EXPECT_EQ(callback->getData("1"), testData); + EXPECT_EQ(callback->getData("2"), std::vector()); +} + +TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasksRetryConnection) { + GetRemoteTasksResponse response; + std::shared_ptr callback = + ndk::SharedRefBase::make(); + + ON_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw) + .WillByDefault([response]([[maybe_unused]] ClientContext* context, + [[maybe_unused]] const GetRemoteTasksRequest& request) { + // mockReader ownership will be transferred to the client so we don't own it here. + MockClientReader* mockClientReader = + new MockClientReader(); + EXPECT_CALL(*mockClientReader, Finish()).WillOnce(Return(Status::OK)); + // Connection fails after receiving one task. Should retry after some time. + EXPECT_CALL(*mockClientReader, Read(_)) + .WillOnce(DoAll(SetArgPointee<0>(response), Return(true))) + .WillRepeatedly(Return(false)); + return mockClientReader; + }); + + getService()->setRemoteTaskCallback(callback); + setRetryWaitInMs(100); + // Start the long live connection to receive tasks. + ApState newState = { + .isReadyForRemoteTask = true, + }; + ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk()); + + ASSERT_TRUE(callback->wait(/*taskCount=*/2, /*timeoutInSec=*/10)) + << "Did not receive enough tasks"; +} + +TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasksDefaultNotReady) { + GetRemoteTasksResponse response1; + std::vector testData = {0xde, 0xad, 0xbe, 0xef}; + response1.set_clientid("1"); + response1.set_data(testData.data(), testData.size()); + GetRemoteTasksResponse response2; + response2.set_clientid("2"); + std::shared_ptr callback = + ndk::SharedRefBase::make(); + + EXPECT_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw).Times(0); + + // Default state is not ready for remote tasks, so no callback will be called. + getService()->setRemoteTaskCallback(callback); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); +} + +TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasksNotReadyAfterReady) { + GetRemoteTasksResponse response1; + std::vector testData = {0xde, 0xad, 0xbe, 0xef}; + response1.set_clientid("1"); + response1.set_data(testData.data(), testData.size()); + GetRemoteTasksResponse response2; + response2.set_clientid("2"); + std::shared_ptr callback = + ndk::SharedRefBase::make(); + + ON_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw) + .WillByDefault( + [response1, response2]([[maybe_unused]] ClientContext* context, + [[maybe_unused]] const GetRemoteTasksRequest& request) { + // mockReader ownership will be transferred to the client so we don't own it + // here. + MockClientReader* mockClientReader = + new MockClientReader(); + EXPECT_CALL(*mockClientReader, Finish()).WillOnce(Return(Status::OK)); + EXPECT_CALL(*mockClientReader, Read(_)) + .WillOnce(DoAll(SetArgPointee<0>(response1), Return(true))) + .WillOnce(DoAll(SetArgPointee<0>(response2), Return(true))) + .WillRepeatedly(Return(false)); + return mockClientReader; + }); + // Should only be called once when is is ready for remote task. + EXPECT_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw).Times(1); + + getService()->setRemoteTaskCallback(callback); + setRetryWaitInMs(100); + // Start the long live connection to receive tasks. + ApState newState = { + .isReadyForRemoteTask = true, + }; + ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk()); + ASSERT_TRUE(callback->wait(/*taskCount=*/2, /*timeoutInSec=*/10)) + << "Did not receive enough tasks"; + + // Stop the long live connection. + newState.isReadyForRemoteTask = false; + ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk()); + + // Wait for the retry delay, but the loop should already exit. + std::this_thread::sleep_for(std::chrono::milliseconds(150)); +} + } // namespace remoteaccess } // namespace automotive } // namespace hardware