mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 16:23:37 +00:00
Merge changes from topic "remoteaccess_hal"
* changes: Implement receiving remote task. Implement the GrpcWakeupClient. Define remoteaccess grpc HAL.
This commit is contained in:
committed by
Android (Google) Code Review
commit
5df6128060
66
automotive/remoteaccess/impl/default/client/Android.bp
Normal file
66
automotive/remoteaccess/impl/default/client/Android.bp
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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",
|
||||
"libgrpc++",
|
||||
"libprotobuf-cpp-full",
|
||||
],
|
||||
cflags: [
|
||||
"-Wno-unused-parameter",
|
||||
],
|
||||
}
|
||||
|
||||
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",
|
||||
"wakeup_client_protos",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
"liblog",
|
||||
"libutils",
|
||||
"libgrpc++",
|
||||
"libprotobuf-cpp-full",
|
||||
],
|
||||
cflags: [
|
||||
"-Wno-unused-parameter",
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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 <aidl/android/hardware/automotive/remoteaccess/ApState.h>
|
||||
#include <aidl/android/hardware/automotive/remoteaccess/BnRemoteAccess.h>
|
||||
#include <aidl/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.h>
|
||||
#include <android-base/thread_annotations.h>
|
||||
#include <wakeup_client.grpc.pb.h>
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace remoteaccess {
|
||||
|
||||
class RemoteAccessService
|
||||
: public aidl::android::hardware::automotive::remoteaccess::BnRemoteAccess {
|
||||
public:
|
||||
explicit RemoteAccessService(WakeupClient::StubInterface* grpcStub);
|
||||
|
||||
~RemoteAccessService();
|
||||
|
||||
ndk::ScopedAStatus getDeviceId(std::string* deviceId) override;
|
||||
|
||||
ndk::ScopedAStatus getWakeupServiceName(std::string* wakeupServiceName) 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;
|
||||
|
||||
private:
|
||||
// For testing.
|
||||
friend class RemoteAccessServiceUnitTest;
|
||||
|
||||
WakeupClient::StubInterface* mGrpcStub;
|
||||
std::thread mThread;
|
||||
std::mutex mLock;
|
||||
std::condition_variable mCv;
|
||||
std::shared_ptr<aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback>
|
||||
mRemoteTaskCallback GUARDED_BY(mLock);
|
||||
std::unique_ptr<grpc::ClientContext> 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
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
@@ -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
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest version="1.0" type="device">
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.automotive.remoteaccess</name>
|
||||
<version>1</version>
|
||||
<fqname>IRemoteAccess/default</fqname>
|
||||
</hal>
|
||||
</manifest>
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "RemoteAccessImpl"
|
||||
|
||||
#include "RemoteAccessService.h"
|
||||
|
||||
#include <android/binder_manager.h>
|
||||
#include <android/binder_process.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
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>(nullptr);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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 <android/binder_status.h>
|
||||
#include <grpc++/grpc++.h>
|
||||
#include <utils/Log.h>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace remoteaccess {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::aidl::android::hardware::automotive::remoteaccess::ApState;
|
||||
using ::aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback;
|
||||
using ::android::base::ScopedLockAssertion;
|
||||
using ::grpc::ClientContext;
|
||||
using ::grpc::ClientReaderInterface;
|
||||
using ::grpc::Status;
|
||||
using ::grpc::StatusCode;
|
||||
using ::ndk::ScopedAStatus;
|
||||
|
||||
const std::string WAKEUP_SERVICE_NAME = "com.google.vehicle.wakeup";
|
||||
|
||||
std::vector<uint8_t> stringToBytes(const std::string& s) {
|
||||
const char* data = s.data();
|
||||
return std::vector<uint8_t>(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){};
|
||||
|
||||
RemoteAccessService::~RemoteAccessService() {
|
||||
maybeStopTaskLoop();
|
||||
}
|
||||
|
||||
void RemoteAccessService::maybeStartTaskLoop() {
|
||||
std::lock_guard<std::mutex> lockGuard(mStartStopTaskLoopLock);
|
||||
if (mTaskLoopRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
mThread = std::thread([this]() { runTaskLoop(); });
|
||||
|
||||
mTaskLoopRunning = true;
|
||||
}
|
||||
|
||||
void RemoteAccessService::maybeStopTaskLoop() {
|
||||
std::lock_guard<std::mutex> lockGuard(mStartStopTaskLoopLock);
|
||||
if (!mTaskLoopRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<ClientReaderInterface<GetRemoteTasksResponse>> reader;
|
||||
while (true) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lockGuard(mLock);
|
||||
mGetRemoteTasksContext.reset(new ClientContext());
|
||||
reader = mGrpcStub->GetRemoteTasks(mGetRemoteTasksContext.get(), request);
|
||||
}
|
||||
GetRemoteTasksResponse response;
|
||||
while (reader->Read(&response)) {
|
||||
std::shared_ptr<IRemoteTaskCallback> callback;
|
||||
{
|
||||
std::lock_guard<std::mutex> 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) {
|
||||
// TODO(b/241483300): Call VHAL to get VIN.
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus RemoteAccessService::getWakeupServiceName(std::string* wakeupServiceName) {
|
||||
*wakeupServiceName = WAKEUP_SERVICE_NAME;
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus RemoteAccessService::setRemoteTaskCallback(
|
||||
[[maybe_unused]] const std::shared_ptr<IRemoteTaskCallback>& callback) {
|
||||
std::lock_guard<std::mutex> lockGuard(mLock);
|
||||
mRemoteTaskCallback = callback;
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus RemoteAccessService::clearRemoteTaskCallback() {
|
||||
std::lock_guard<std::mutex> lockGuard(mLock);
|
||||
mRemoteTaskCallback.reset();
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
} // namespace remoteaccess
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
@@ -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 {
|
||||
43
automotive/remoteaccess/impl/default/test/Android.bp
Normal file
43
automotive/remoteaccess/impl/default/test/Android.bp
Normal file
@@ -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"],
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* 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 <aidl/android/hardware/automotive/remoteaccess/ApState.h>
|
||||
#include <aidl/android/hardware/automotive/remoteaccess/BnRemoteTaskCallback.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <grpcpp/test/mock_stream.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <wakeup_client.grpc.pb.h>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
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;
|
||||
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:
|
||||
MOCK_METHOD(ClientReaderInterface<GetRemoteTasksResponse>*, 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<GetRemoteTasksResponse>*, AsyncGetRemoteTasksRaw,
|
||||
(ClientContext * context, const GetRemoteTasksRequest& request, CompletionQueue* cq,
|
||||
void* tag));
|
||||
MOCK_METHOD(ClientAsyncReaderInterface<GetRemoteTasksResponse>*, PrepareAsyncGetRemoteTasksRaw,
|
||||
(ClientContext * context, const GetRemoteTasksRequest& request,
|
||||
CompletionQueue* cq));
|
||||
MOCK_METHOD(ClientAsyncResponseReaderInterface<NotifyWakeupRequiredResponse>*,
|
||||
AsyncNotifyWakeupRequiredRaw,
|
||||
(ClientContext * context, const NotifyWakeupRequiredRequest& request,
|
||||
CompletionQueue* cq));
|
||||
MOCK_METHOD(ClientAsyncResponseReaderInterface<NotifyWakeupRequiredResponse>*,
|
||||
PrepareAsyncNotifyWakeupRequiredRaw,
|
||||
(ClientContext * context, const NotifyWakeupRequiredRequest& request,
|
||||
CompletionQueue* cq));
|
||||
};
|
||||
|
||||
class FakeRemoteTaskCallback : public BnRemoteTaskCallback {
|
||||
public:
|
||||
ScopedAStatus onRemoteTaskRequested(const std::string& clientId,
|
||||
const std::vector<uint8_t>& data) override {
|
||||
std::lock_guard<std::mutex> lockGuard(mLock);
|
||||
mDataByClientId[clientId] = data;
|
||||
mTaskCount++;
|
||||
mCv.notify_all();
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> getData(const std::string& clientId) { return mDataByClientId[clientId]; }
|
||||
|
||||
bool wait(size_t taskCount, size_t timeoutInSec) {
|
||||
std::unique_lock<std::mutex> 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<std::string, std::vector<uint8_t>> mDataByClientId GUARDED_BY(mLock);
|
||||
size_t mTaskCount GUARDED_BY(mLock) = 0;
|
||||
std::condition_variable mCv;
|
||||
};
|
||||
|
||||
class RemoteAccessServiceUnitTest : public ::testing::Test {
|
||||
public:
|
||||
virtual void SetUp() override {
|
||||
mGrpcWakeupClientStub = std::make_unique<MockGrpcClientStub>();
|
||||
mService = ndk::SharedRefBase::make<RemoteAccessService>(mGrpcWakeupClientStub.get());
|
||||
}
|
||||
|
||||
MockGrpcClientStub* getGrpcWakeupClientStub() { return mGrpcWakeupClientStub.get(); }
|
||||
|
||||
RemoteAccessService* getService() { return mService.get(); }
|
||||
|
||||
void setRetryWaitInMs(size_t retryWaitInMs) { mService->setRetryWaitInMs(retryWaitInMs); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<MockGrpcClientStub> mGrpcWakeupClientStub;
|
||||
std::shared_ptr<RemoteAccessService> mService;
|
||||
MockClientReader<GetRemoteTasksResponse>* mMockTaskReader;
|
||||
};
|
||||
|
||||
TEST_F(RemoteAccessServiceUnitTest, TestGetWakeupServiceName) {
|
||||
std::string serviceName;
|
||||
|
||||
ScopedAStatus status = getService()->getWakeupServiceName(&serviceName);
|
||||
|
||||
EXPECT_TRUE(status.isOk());
|
||||
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<uint8_t> 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<FakeRemoteTaskCallback> callback =
|
||||
ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
|
||||
|
||||
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<GetRemoteTasksResponse>* mockClientReader =
|
||||
new MockClientReader<GetRemoteTasksResponse>();
|
||||
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<uint8_t>());
|
||||
}
|
||||
|
||||
TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasksRetryConnection) {
|
||||
GetRemoteTasksResponse response;
|
||||
std::shared_ptr<FakeRemoteTaskCallback> callback =
|
||||
ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
|
||||
|
||||
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<GetRemoteTasksResponse>* mockClientReader =
|
||||
new MockClientReader<GetRemoteTasksResponse>();
|
||||
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<uint8_t> 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<FakeRemoteTaskCallback> callback =
|
||||
ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
|
||||
|
||||
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<uint8_t> 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<FakeRemoteTaskCallback> callback =
|
||||
ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
|
||||
|
||||
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<GetRemoteTasksResponse>* mockClientReader =
|
||||
new MockClientReader<GetRemoteTasksResponse>();
|
||||
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
|
||||
} // namespace android
|
||||
Reference in New Issue
Block a user