Add a test wakeupclient service implementation.

The test implementation will send out a remote task to remote access
HAL every 5s.

Test: Manually test on gcar_emu.
adb root
adb remount
adb reboot
adb root
adb remount
m -j TestWakeupClientServer
cd out/target/product/emulator_car64_x86_64
adb push ./vendor/bin/TestWakeupClientServer /vendor/bin
adb shell

In the shell:
su
/vendor/bin/TestWakeupClientServer

Check adb logcat, verify tasks are received.
Bug: 246841306

Change-Id: Idaf198662f7004e3a9e77d75caeffc00cda49218
This commit is contained in:
Yu Shan
2022-09-12 14:56:32 -07:00
parent 8a1c807cee
commit a725df46fc
15 changed files with 354 additions and 6 deletions

View File

@@ -37,6 +37,7 @@ cc_binary {
],
cflags: [
"-Wno-unused-parameter",
"-DGRPC_SERVICE_ADDRESS=\"localhost:50051\"",
],
}

View File

@@ -20,27 +20,35 @@
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <grpcpp/create_channel.h>
#include <stdlib.h>
#include <utils/Log.h>
constexpr char SERVICE_NAME[] = "android.hardware.automotive.remoteaccess.IRemoteAccess/default";
int main(int /* argc */, char* /* argv */[]) {
ALOGI("Registering RemoteAccessService as service...");
// TODO(b/241483300): Create GrpcClientStub here.
#ifndef GRPC_SERVICE_ADDRESS
ALOGE("GRPC_SERVICE_ADDRESS is not defined, exiting");
exit(1);
#endif
auto channel = grpc::CreateChannel(GRPC_SERVICE_ADDRESS, grpc::InsecureChannelCredentials());
auto clientStub = android::hardware::automotive::remoteaccess::WakeupClient::NewStub(channel);
auto service = ndk::SharedRefBase::make<
android::hardware::automotive::remoteaccess::RemoteAccessService>(nullptr);
android::hardware::automotive::remoteaccess::RemoteAccessService>(clientStub.get());
binder_exception_t err = AServiceManager_addService(
service->asBinder().get(), "android.hardware.automotive.remote.IRemoteAccess/default");
binder_exception_t err = AServiceManager_addService(service->asBinder().get(), SERVICE_NAME);
if (err != EX_NONE) {
ALOGE("failed to register android.hardware.automotive.remote.IRemoteAccess service, "
"exception: %d",
err);
return 1;
exit(1);
}
if (!ABinderProcess_setThreadPoolMaxThreadCount(1)) {
ALOGE("%s", "failed to set thread pool max thread count");
return 1;
exit(1);
}
ABinderProcess_startThreadPool();

View File

@@ -104,14 +104,19 @@ void RemoteAccessService::runTaskLoop() {
}
GetRemoteTasksResponse response;
while (reader->Read(&response)) {
ALOGI("Receiving one task from remote task client");
std::shared_ptr<IRemoteTaskCallback> callback;
{
std::lock_guard<std::mutex> lockGuard(mLock);
callback = mRemoteTaskCallback;
}
if (callback == nullptr) {
ALOGD("No callback registered, task ignored");
continue;
}
ALOGD("Calling onRemoteTaskRequested callback for client ID: %s",
response.clientid().c_str());
ScopedAStatus callbackStatus = callback->onRemoteTaskRequested(
response.clientid(), stringToBytes(response.data()));
if (!callbackStatus.isOk()) {

View File

@@ -0,0 +1,7 @@
# Test GRPC Server.
A test GRPC server that implements wakeup_client.proto. This test server acts
as a reference implementation for a remote wakeup client running on TCU. This
reference server also implements wakeup_client_debug.proto which is the
debugging interface. It is recommended that the actual implementation also
implements this test interface for easier end-to-end testing.

View File

@@ -0,0 +1,34 @@
// 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.
cc_binary {
name: "TestWakeupClientServer",
vendor: true,
srcs: ["src/*.cpp"],
local_include_dirs: ["include"],
shared_libs: [
"libbase",
"liblog",
"libutils",
"libgrpc++",
"libprotobuf-cpp-full",
],
whole_static_libs: [
"wakeup_client_protos",
],
cflags: [
"-Wno-unused-parameter",
"-DGRPC_SERVICE_ADDRESS=\"localhost:50051\"",
],
}

View File

@@ -0,0 +1,93 @@
/*
* 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 <android-base/thread_annotations.h>
#include <wakeup_client.grpc.pb.h>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <string>
#include <thread>
namespace android {
namespace hardware {
namespace automotive {
namespace remoteaccess {
// A class to generate fake task for testing. Not required for real implementation. In real
// implementation, the task should come from remote task server. This class is thread-safe.
class FakeTaskGenerator final {
public:
GetRemoteTasksResponse generateTask();
private:
// Simulates the client ID for each task.
std::atomic<int> mCurrentClientId = 0;
constexpr static uint8_t DATA[] = {0xde, 0xad, 0xbe, 0xef};
};
// TaskQueue is thread-safe.
class TaskQueue final {
public:
void add(const GetRemoteTasksResponse& response);
std::optional<GetRemoteTasksResponse> maybePopOne();
void waitForTask();
void stopWait();
private:
std::mutex mLock;
std::queue<GetRemoteTasksResponse> mTasks GUARDED_BY(mLock);
// A variable to notify mTasks is not empty.
std::condition_variable mTasksNotEmptyCv;
bool mStopped GUARDED_BY(mLock);
};
class TestWakeupClientServiceImpl final : public WakeupClient::Service {
public:
TestWakeupClientServiceImpl();
~TestWakeupClientServiceImpl();
grpc::Status GetRemoteTasks(grpc::ServerContext* context, const GetRemoteTasksRequest* request,
grpc::ServerWriter<GetRemoteTasksResponse>* writer) override;
grpc::Status NotifyWakeupRequired(grpc::ServerContext* context,
const NotifyWakeupRequiredRequest* request,
NotifyWakeupRequiredResponse* response) override;
private:
// This is a thread for communicating with remote wakeup server (via network) and receive tasks
// from it.
std::thread mThread;
// A variable to notify server is stopping.
std::condition_variable mServerStoppedCv;
std::mutex mLock;
bool mServerStopped GUARDED_BY(mLock);
// Thread-safe. For test impl only.
FakeTaskGenerator mFakeTaskGenerator;
// Thread-sfae.
TaskQueue mTaskQueue;
void fakeTaskGenerateLoop();
};
} // namespace remoteaccess
} // namespace automotive
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,152 @@
/*
* 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 "TestWakeupClientServiceImpl.h"
#include <android-base/stringprintf.h>
#include <utils/Log.h>
#include <chrono>
#include <thread>
namespace android {
namespace hardware {
namespace automotive {
namespace remoteaccess {
namespace {
using ::android::base::ScopedLockAssertion;
using ::android::base::StringPrintf;
using ::grpc::ServerContext;
using ::grpc::ServerWriter;
using ::grpc::Status;
constexpr int kTaskIntervalInSec = 5;
} // namespace
GetRemoteTasksResponse FakeTaskGenerator::generateTask() {
int clientId = mCurrentClientId++;
GetRemoteTasksResponse response;
response.set_data(std::string(reinterpret_cast<const char*>(DATA), sizeof(DATA)));
std::string clientIdStr = StringPrintf("%d", clientId);
response.set_clientid(clientIdStr);
return response;
}
std::optional<GetRemoteTasksResponse> TaskQueue::maybePopOne() {
std::lock_guard<std::mutex> lockGuard(mLock);
if (mTasks.size() == 0) {
return std::nullopt;
}
GetRemoteTasksResponse response = mTasks.front();
mTasks.pop();
return std::move(response);
}
void TaskQueue::add(const GetRemoteTasksResponse& task) {
// TODO (b/246841306): add timeout to tasks.
std::lock_guard<std::mutex> lockGuard(mLock);
mTasks.push(task);
mTasksNotEmptyCv.notify_all();
}
void TaskQueue::waitForTask() {
std::unique_lock<std::mutex> lock(mLock);
mTasksNotEmptyCv.wait(lock, [this] {
ScopedLockAssertion lockAssertion(mLock);
return mTasks.size() > 0 || mStopped;
});
}
void TaskQueue::stopWait() {
std::lock_guard<std::mutex> lockGuard(mLock);
mStopped = true;
mTasksNotEmptyCv.notify_all();
}
TestWakeupClientServiceImpl::TestWakeupClientServiceImpl() {
mThread = std::thread([this] { fakeTaskGenerateLoop(); });
}
TestWakeupClientServiceImpl::~TestWakeupClientServiceImpl() {
{
std::lock_guard<std::mutex> lockGuard(mLock);
mServerStopped = true;
mServerStoppedCv.notify_all();
}
mTaskQueue.stopWait();
if (mThread.joinable()) {
mThread.join();
}
}
void TestWakeupClientServiceImpl::fakeTaskGenerateLoop() {
// In actual implementation, this should communicate with the remote server and receives tasks
// from it. Here we simulate receiving one remote task every {kTaskIntervalInSec}s.
while (true) {
mTaskQueue.add(mFakeTaskGenerator.generateTask());
ALOGI("Sleeping for %d seconds until next task", kTaskIntervalInSec);
std::unique_lock lk(mLock);
if (mServerStoppedCv.wait_for(lk, std::chrono::seconds(kTaskIntervalInSec), [this] {
ScopedLockAssertion lockAssertion(mLock);
return mServerStopped;
})) {
// If the stopped flag is set, we are quitting, exit the loop.
return;
}
}
}
Status TestWakeupClientServiceImpl::GetRemoteTasks(ServerContext* context,
const GetRemoteTasksRequest* request,
ServerWriter<GetRemoteTasksResponse>* writer) {
ALOGD("GetRemoteTasks called");
while (true) {
mTaskQueue.waitForTask();
while (true) {
auto maybeTask = mTaskQueue.maybePopOne();
if (!maybeTask.has_value()) {
// No task left, loop again and wait for another task(s).
break;
}
// Loop through all the task in the queue but obtain lock for each element so we don't
// hold lock while writing the response.
const GetRemoteTasksResponse& response = maybeTask.value();
if (!writer->Write(response)) {
// Broken stream, maybe the client is shutting down.
ALOGW("Failed to deliver remote task to remote access HAL");
// The task failed to be sent, add it back to the queue. The order might change, but
// it is okay.
mTaskQueue.add(response);
return Status::CANCELLED;
}
}
}
return Status::OK;
}
Status TestWakeupClientServiceImpl::NotifyWakeupRequired(ServerContext* context,
const NotifyWakeupRequiredRequest* request,
NotifyWakeupRequiredResponse* response) {
return Status::OK;
}
} // namespace remoteaccess
} // namespace automotive
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,48 @@
/*
* 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 <string>
#include "TestWakeupClientServiceImpl.h"
#include <grpc/grpc.h>
#include <grpcpp/security/server_credentials.h>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
#include <utils/Log.h>
using ::android::hardware::automotive::remoteaccess::TestWakeupClientServiceImpl;
using ::grpc::Server;
using ::grpc::ServerBuilder;
using ::grpc::ServerWriter;
void RunServer() {
std::string serverAddress(GRPC_SERVICE_ADDRESS);
std::shared_ptr<TestWakeupClientServiceImpl> service =
std::make_unique<TestWakeupClientServiceImpl>();
ServerBuilder builder;
builder.AddListeningPort(serverAddress, grpc::InsecureServerCredentials());
builder.RegisterService(service.get());
std::unique_ptr<Server> server(builder.BuildAndStart());
ALOGI("Test Remote Access GRPC Server listening on %s", serverAddress.c_str());
server->Wait();
}
int main(int argc, char** argv) {
RunServer();
return 0;
}