Merge changes from topic "remoteaccess_hal"

* changes:
  Implement receiving remote task.
  Implement the GrpcWakeupClient.
  Define remoteaccess grpc HAL.
This commit is contained in:
TreeHugger Robot
2022-09-29 07:34:44 +00:00
committed by Android (Google) Code Review
10 changed files with 734 additions and 0 deletions

View 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",
],
}

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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 {

View 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"],
}

View File

@@ -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