Merge "Implement a host side TestWakeupClientService." into main
diff --git a/automotive/remoteaccess/hal/default/Android.bp b/automotive/remoteaccess/hal/default/Android.bp
index 0155667..48a7309 100644
--- a/automotive/remoteaccess/hal/default/Android.bp
+++ b/automotive/remoteaccess/hal/default/Android.bp
@@ -53,7 +53,7 @@
vintf_fragments: ["remoteaccess-default-service.xml"],
init_rc: ["remoteaccess-default-service.rc"],
cflags: [
- "-DGRPC_SERVICE_ADDRESS=\"localhost:50051\"",
+ "-DGRPC_SERVICE_ADDRESS=\"10.0.2.2:50051\"",
],
}
diff --git a/automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp b/automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp
index b091162..d4ba864 100644
--- a/automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp
+++ b/automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp
@@ -30,12 +30,12 @@
constexpr char SERVICE_NAME[] = "android.hardware.automotive.remoteaccess.IRemoteAccess/default";
int main(int /* argc */, char* /* argv */[]) {
- LOG(INFO) << "Registering RemoteAccessService as service...";
-
#ifndef GRPC_SERVICE_ADDRESS
LOG(ERROR) << "GRPC_SERVICE_ADDRESS is not defined, exiting";
exit(1);
#endif
+ LOG(INFO) << "Registering RemoteAccessService as service, server: " << GRPC_SERVICE_ADDRESS
+ << "...";
grpc::ChannelArguments grpcargs = {};
#ifdef GRPC_SERVICE_IFNAME
@@ -48,8 +48,7 @@
android::netdevice::WaitCondition::PRESENT_AND_UP);
LOG(INFO) << "Waiting for interface: " << GRPC_SERVICE_IFNAME << " done";
#endif
- auto channel = grpc::CreateCustomChannel(GRPC_SERVICE_ADDRESS,
- grpc::InsecureChannelCredentials(), grpcargs);
+ 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>(clientStub.get());
diff --git a/automotive/remoteaccess/test_grpc_server/impl/Android.bp b/automotive/remoteaccess/test_grpc_server/impl/Android.bp
index 152b528..74c810e 100644
--- a/automotive/remoteaccess/test_grpc_server/impl/Android.bp
+++ b/automotive/remoteaccess/test_grpc_server/impl/Android.bp
@@ -38,6 +38,26 @@
],
cflags: [
"-Wno-unused-parameter",
- "-DGRPC_SERVICE_ADDRESS=\"localhost:50051\"",
+ "-DGRPC_SERVICE_ADDRESS=\"127.0.0.1:50051\"",
+ ],
+}
+
+cc_binary_host {
+ name: "TestWakeupClientServerHost",
+ srcs: ["src/*.cpp"],
+ local_include_dirs: ["include"],
+ shared_libs: [
+ "libbase",
+ "libutils",
+ "libgrpc++",
+ "libprotobuf-cpp-full",
+ ],
+ whole_static_libs: [
+ "wakeup_client_protos",
+ ],
+ cflags: [
+ "-Wno-unused-parameter",
+ "-DGRPC_SERVICE_ADDRESS=\"127.0.0.1:50051\"",
+ "-DHOST",
],
}
diff --git a/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h b/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h
index 6b86b35..4159e83 100644
--- a/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h
+++ b/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h
@@ -34,11 +34,9 @@
// implementation, the task should come from remote task server. This class is thread-safe.
class FakeTaskGenerator final {
public:
- GetRemoteTasksResponse generateTask();
+ GetRemoteTasksResponse generateTask(const std::string& clientId);
private:
- // Simulates the client ID for each task.
- std::atomic<int> mCurrentClientId = 0;
constexpr static uint8_t DATA[] = {0xde, 0xad, 0xbe, 0xef};
};
@@ -99,7 +97,7 @@
void waitForTaskWithLock(std::unique_lock<std::mutex>& lock);
};
-class TestWakeupClientServiceImpl final : public WakeupClient::Service {
+class TestWakeupClientServiceImpl : public WakeupClient::Service {
public:
TestWakeupClientServiceImpl();
@@ -112,25 +110,57 @@
const NotifyWakeupRequiredRequest* request,
NotifyWakeupRequiredResponse* response) override;
+ /**
+ * Starts generating fake tasks for the specific client repeatedly.
+ *
+ * The fake task will have {0xDE 0xAD 0xBE 0xEF} as payload. A new fake task will be sent
+ * to the client every 5s.
+ */
+ void startGeneratingFakeTask(const std::string& clientId);
+ /**
+ * stops generating fake tasks.
+ */
+ void stopGeneratingFakeTask();
+ /**
+ * Returns whether we need to wakeup the target device to send remote tasks.
+ */
+ bool isWakeupRequired();
+ /**
+ * Returns whether we have an active connection with the target device.
+ */
+ bool isRemoteTaskConnectionAlive();
+ /**
+ * Injects a fake task with taskData to be sent to the specific client.
+ */
+ void injectTask(const std::string& taskData, const std::string& clientId);
+ /**
+ * Wakes up the target device.
+ *
+ * This must be implemented by child class and contains device specific logic. E.g. this might
+ * be sending QEMU commands for the emulator device.
+ */
+ virtual void wakeupApplicationProcessor() = 0;
+
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::condition_variable mTaskLoopStoppedCv;
// Whether wakeup AP is required for executing tasks.
std::atomic<bool> mWakeupRequired = true;
+ // Whether we currently have an active long-live connection to deliver remote tasks.
+ std::atomic<bool> mRemoteTaskConnectionAlive = false;
std::mutex mLock;
- bool mServerStopped GUARDED_BY(mLock);
+ bool mGeneratingFakeTask GUARDED_BY(mLock);
// Thread-safe. For test impl only.
FakeTaskGenerator mFakeTaskGenerator;
// Thread-sfae.
TaskQueue mTaskQueue;
- void fakeTaskGenerateLoop();
-
- void wakeupApplicationProcessor();
+ void fakeTaskGenerateLoop(const std::string& clientId);
+ void injectTaskResponse(const GetRemoteTasksResponse& response);
};
} // namespace remoteaccess
diff --git a/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp b/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp
index 7dcd31e..eb3871b 100644
--- a/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp
+++ b/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp
@@ -16,8 +16,6 @@
#include "TestWakeupClientServiceImpl.h"
-#include "ApPowerControl.h"
-
#include <android-base/stringprintf.h>
#include <inttypes.h>
#include <utils/Looper.h>
@@ -44,12 +42,10 @@
} // namespace
-GetRemoteTasksResponse FakeTaskGenerator::generateTask() {
- int clientId = mCurrentClientId++;
+GetRemoteTasksResponse FakeTaskGenerator::generateTask(const std::string& clientId) {
GetRemoteTasksResponse response;
- response.set_data(std::string(reinterpret_cast<const char*>(DATA), sizeof(DATA)));
- std::string clientIdStr = StringPrintf("%d", clientId);
- response.set_clientid(clientIdStr);
+ response.set_data(reinterpret_cast<const char*>(DATA), sizeof(DATA));
+ response.set_clientid(clientId);
return response;
}
@@ -165,38 +161,68 @@
}
}
-TestWakeupClientServiceImpl::TestWakeupClientServiceImpl() {
- mThread = std::thread([this] { fakeTaskGenerateLoop(); });
-}
+TestWakeupClientServiceImpl::TestWakeupClientServiceImpl() {}
TestWakeupClientServiceImpl::~TestWakeupClientServiceImpl() {
+ { std::lock_guard<std::mutex> lockGuard(mLock); }
+ mTaskQueue.stopWait();
+ stopGeneratingFakeTask();
+}
+
+void TestWakeupClientServiceImpl::injectTask(const std::string& taskData,
+ const std::string& clientId) {
+ GetRemoteTasksResponse response;
+ response.set_data(taskData);
+ response.set_clientid(clientId);
+ injectTaskResponse(response);
+}
+
+void TestWakeupClientServiceImpl::injectTaskResponse(const GetRemoteTasksResponse& response) {
+ printf("Received a new task\n");
+ mTaskQueue.add(response);
+ if (mWakeupRequired) {
+ wakeupApplicationProcessor();
+ }
+}
+
+void TestWakeupClientServiceImpl::startGeneratingFakeTask(const std::string& clientId) {
+ std::lock_guard<std::mutex> lockGuard(mLock);
+ if (mGeneratingFakeTask) {
+ printf("Fake task is already being generated\n");
+ return;
+ }
+ mGeneratingFakeTask = true;
+ mThread = std::thread([this, clientId] { fakeTaskGenerateLoop(clientId); });
+ printf("Started generating fake tasks\n");
+}
+
+void TestWakeupClientServiceImpl::stopGeneratingFakeTask() {
{
std::lock_guard<std::mutex> lockGuard(mLock);
- mServerStopped = true;
- mServerStoppedCv.notify_all();
+ if (!mGeneratingFakeTask) {
+ printf("Fake task is not being generated, do nothing\n");
+ return;
+ }
+ mTaskLoopStoppedCv.notify_all();
+ mGeneratingFakeTask = false;
}
- mTaskQueue.stopWait();
if (mThread.joinable()) {
mThread.join();
}
+ printf("Stopped generating fake tasks\n");
}
-void TestWakeupClientServiceImpl::fakeTaskGenerateLoop() {
+void TestWakeupClientServiceImpl::fakeTaskGenerateLoop(const std::string& clientId) {
// In actual implementation, this should communicate with the remote server and receives tasks
// from it. Here we simulate receiving one remote task every {kTaskIntervalInMs}ms.
while (true) {
- mTaskQueue.add(mFakeTaskGenerator.generateTask());
- printf("Received a new task\n");
- if (mWakeupRequired) {
- wakeupApplicationProcessor();
- }
-
+ injectTaskResponse(mFakeTaskGenerator.generateTask(clientId));
printf("Sleeping for %d seconds until next task\n", kTaskIntervalInMs);
std::unique_lock lk(mLock);
- if (mServerStoppedCv.wait_for(lk, std::chrono::milliseconds(kTaskIntervalInMs), [this] {
+ if (mTaskLoopStoppedCv.wait_for(lk, std::chrono::milliseconds(kTaskIntervalInMs), [this] {
ScopedLockAssertion lockAssertion(mLock);
- return mServerStopped;
+ return !mGeneratingFakeTask;
})) {
// If the stopped flag is set, we are quitting, exit the loop.
return;
@@ -208,6 +234,7 @@
const GetRemoteTasksRequest* request,
ServerWriter<GetRemoteTasksResponse>* writer) {
printf("GetRemoteTasks called\n");
+ mRemoteTaskConnectionAlive = true;
while (true) {
mTaskQueue.waitForTask();
@@ -226,16 +253,19 @@
// The task failed to be sent, add it back to the queue. The order might change, but
// it is okay.
mTaskQueue.add(response);
+ mRemoteTaskConnectionAlive = false;
return Status::CANCELLED;
}
}
}
+ mRemoteTaskConnectionAlive = false;
return Status::OK;
}
Status TestWakeupClientServiceImpl::NotifyWakeupRequired(ServerContext* context,
const NotifyWakeupRequiredRequest* request,
NotifyWakeupRequiredResponse* response) {
+ printf("NotifyWakeupRequired called\n");
if (request->iswakeuprequired() && !mWakeupRequired && !mTaskQueue.isEmpty()) {
// If wakeup is now required and previously not required, this means we have finished
// shutting down the device. If there are still pending tasks, try waking up AP again
@@ -243,11 +273,20 @@
wakeupApplicationProcessor();
}
mWakeupRequired = request->iswakeuprequired();
+ if (mWakeupRequired) {
+ // We won't know the connection is down unless we try to send a task over. If wakeup is
+ // required, the connection is very likely already down.
+ mRemoteTaskConnectionAlive = false;
+ }
return Status::OK;
}
-void TestWakeupClientServiceImpl::wakeupApplicationProcessor() {
- wakeupAp();
+bool TestWakeupClientServiceImpl::isWakeupRequired() {
+ return mWakeupRequired;
+}
+
+bool TestWakeupClientServiceImpl::isRemoteTaskConnectionAlive() {
+ return mRemoteTaskConnectionAlive;
}
} // namespace remoteaccess
diff --git a/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp b/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
index d3f519c..be285a8 100644
--- a/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
+++ b/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
@@ -14,7 +14,17 @@
* limitations under the License.
*/
+#include <signal.h>
+#include <stdio.h>
+#include <sys/wait.h>
+#include <iostream>
+#include <sstream>
#include <string>
+#include <thread>
+
+#ifndef HOST
+#include "ApPowerControl.h"
+#endif // #ifndef HOST
#include "TestWakeupClientServiceImpl.h"
@@ -28,10 +38,18 @@
using ::grpc::ServerBuilder;
using ::grpc::ServerWriter;
-void RunServer(const std::string& serviceAddr) {
- std::shared_ptr<TestWakeupClientServiceImpl> service =
- std::make_unique<TestWakeupClientServiceImpl>();
+constexpr int SHUTDOWN_REQUEST = 289410889;
+constexpr int VEHICLE_IN_USE = 287313738;
+const char* COMMAND_RUN_EMU = "source ~/.aae-toolbox/bin/bashrc && aae emulator run";
+const char* COMMAND_SET_VHAL_PROP =
+ "adb -s emulator-5554 wait-for-device && adb -s emulator-5554 root "
+ "&& sleep 1 && adb -s emulator-5554 wait-for-device && adb -s emulator-5554 shell "
+ "dumpsys android.hardware.automotive.vehicle.IVehicle/default --set %d -i %d";
+pid_t emuPid = 0;
+
+void RunServer(const std::string& serviceAddr,
+ std::shared_ptr<TestWakeupClientServiceImpl> service) {
ServerBuilder builder;
builder.AddListeningPort(serviceAddr, grpc::InsecureServerCredentials());
builder.RegisterService(service.get());
@@ -40,11 +58,224 @@
server->Wait();
}
+pid_t runCommand(const char* bashCommand) {
+ pid_t pid = fork();
+ if (pid == 0) {
+ // In child process. Put it into a separate process group so we can kill it.
+ setpgid(0, 0);
+ execl("/bin/bash", "bash", "-c", bashCommand, /*terminateArg=*/nullptr);
+ exit(0);
+ } else {
+ return pid;
+ }
+}
+
+void updateEmuStatus() {
+ if (emuPid == 0) {
+ return;
+ }
+ pid_t pid = waitpid(emuPid, nullptr, WNOHANG);
+ if (pid == emuPid) {
+ // Emu process already exited. If Emu process is still running, pid will be 0.
+ emuPid = 0;
+ }
+}
+
+bool powerOnEmu() {
+ updateEmuStatus();
+ if (emuPid != 0) {
+ printf("The emulator is already running\n");
+ return false;
+ }
+ emuPid = runCommand(COMMAND_RUN_EMU);
+ printf("Emulator started in process: %d\n", emuPid);
+ return true;
+}
+
+bool powerOn() {
+#ifdef HOST
+ return powerOnEmu();
+#else
+ printf("power on is only supported on host\n");
+ return false;
+#endif
+}
+
+const char* getSetPropCommand(int propId) {
+ int size = snprintf(nullptr, 0, COMMAND_SET_VHAL_PROP, propId, 1);
+ char* command = new char[size + 1];
+ snprintf(command, size + 1, COMMAND_SET_VHAL_PROP, propId, 1);
+ return command;
+}
+
+void powerOffEmu() {
+ updateEmuStatus();
+ if (emuPid == 0) {
+ printf("The emulator is not running\n");
+ return;
+ }
+ const char* command = getSetPropCommand(SHUTDOWN_REQUEST);
+ runCommand(command);
+ delete[] command;
+ waitpid(emuPid, nullptr, /*options=*/0);
+ emuPid = 0;
+}
+
+void powerOff() {
+#ifdef HOST
+ powerOffEmu();
+#else
+ printf("power off is only supported on host\n");
+#endif
+}
+
+void setVehicleInUse(bool vehicleInUse) {
+#ifdef HOST
+ printf("Set vehicleInUse to %d\n", vehicleInUse);
+ int value = 0;
+ if (vehicleInUse) {
+ value = 1;
+ }
+ const char* command = getSetPropCommand(VEHICLE_IN_USE);
+ runCommand(command);
+ delete[] command;
+#else
+ printf("set vehicleInUse is only supported on host\n");
+#endif
+}
+
+void help() {
+ std::cout << "Remote Access Host Test Utility" << std::endl
+ << "help:\t"
+ << "Print out this help info" << std::endl
+ << "genFakeTask start [clientID]:\t"
+ << "Start generating a fake task every 5s" << std::endl
+ << "genFakeTask stop:\t"
+ << "Stop the fake task generation" << std::endl
+ << "status:\t"
+ << "Print current status" << std::endl
+ << "power on:\t"
+ << "Power on the emulator, simulate user enters vehicle while AP is off"
+ << " (only supported on host)" << std::endl
+ << "power off:\t"
+ << "Power off the emulator, simulate user leaves vehicle"
+ << " (only supported on host)" << std::endl
+ << "inject task [clientID] [taskData]:\t"
+ << "Inject a remote task" << std::endl
+ << "set vehicleInUse:\t"
+ << "Set vehicle in use, simulate user enter vehicle while boot up for remote task "
+ << "(only supported on host)" << std::endl;
+}
+
+void parseCommand(const std::string& userInput,
+ std::shared_ptr<TestWakeupClientServiceImpl> service) {
+ if (userInput == "") {
+ // ignore empty line.
+ } else if (userInput == "help") {
+ help();
+ } else if (userInput.rfind("genFakeTask start", 0) == 0) {
+ std::string clientId;
+ std::stringstream ss;
+ ss << userInput;
+ int i = 0;
+ while (std::getline(ss, clientId, ' ')) {
+ i++;
+ if (i == 3) {
+ break;
+ }
+ }
+ if (i != 3) {
+ printf("Missing clientId, see 'help'\n");
+ return;
+ }
+ service->startGeneratingFakeTask(clientId);
+ } else if (userInput == "genFakeTask stop") {
+ service->stopGeneratingFakeTask();
+ } else if (userInput == "status") {
+ printf("isWakeupRequired: %B, isRemoteTaskConnectionAlive: %B\n",
+ service->isWakeupRequired(), service->isRemoteTaskConnectionAlive());
+ } else if (userInput == "power on") {
+ powerOn();
+ } else if (userInput == "power off") {
+ powerOff();
+ } else if (userInput.rfind("inject task", 0) == 0) {
+ std::stringstream ss;
+ ss << userInput;
+ std::string data;
+ std::string taskData;
+ std::string clientId;
+ int i = 0;
+ while (std::getline(ss, data, ' ')) {
+ i++;
+ if (i == 3) {
+ clientId = data;
+ }
+ if (i == 4) {
+ taskData = data;
+ }
+ }
+ if (taskData == "" || clientId == "") {
+ printf("Missing taskData or clientId, see 'help'\n");
+ return;
+ }
+ service->injectTask(taskData, clientId);
+ printf("Remote task with client ID: %s, data: %s injected\n", clientId.c_str(),
+ taskData.c_str());
+ } else if (userInput == "set vehicleInUse") {
+ setVehicleInUse(true);
+ } else {
+ printf("Unknown command, see 'help'\n");
+ }
+}
+
+void saHandler(int signum) {
+ if (emuPid != 0) {
+ kill(-emuPid, signum);
+ waitpid(emuPid, nullptr, /*options=*/0);
+ // Sleep for 1 seconds to allow emulator to print out logs.
+ sleep(1);
+ }
+ exit(-1);
+}
+
+class MyTestWakeupClientServiceImpl final : public TestWakeupClientServiceImpl {
+ public:
+ void wakeupApplicationProcessor() override {
+#ifdef HOST
+ if (powerOnEmu()) {
+ // If we wake up AP to execute remote task, vehicle in use should be false.
+ setVehicleInUse(false);
+ }
+#else
+ wakeupAp();
+#endif
+ };
+};
+
int main(int argc, char** argv) {
std::string serviceAddr = GRPC_SERVICE_ADDRESS;
if (argc > 1) {
serviceAddr = argv[1];
}
- RunServer(serviceAddr);
+ // Let the server thread run, we will force kill the server when we exit the program.
+ std::shared_ptr<TestWakeupClientServiceImpl> service =
+ std::make_shared<MyTestWakeupClientServiceImpl>();
+ std::thread serverThread([serviceAddr, service] { RunServer(serviceAddr, service); });
+
+ // Register the signal handler for SIGTERM and SIGINT so that we can stop the emulator before
+ // exit.
+ struct sigaction sa = {};
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = saHandler;
+ sigaction(SIGTERM, &sa, nullptr);
+ sigaction(SIGINT, &sa, nullptr);
+
+ // Start processing the user inputs.
+ std::string userInput;
+ while (true) {
+ std::cout << ">>> ";
+ std::getline(std::cin, userInput);
+ parseCommand(userInput, service);
+ }
return 0;
}
diff --git a/automotive/remoteaccess/test_grpc_server/lib/Android.bp b/automotive/remoteaccess/test_grpc_server/lib/Android.bp
index 7e95f53..8391018 100644
--- a/automotive/remoteaccess/test_grpc_server/lib/Android.bp
+++ b/automotive/remoteaccess/test_grpc_server/lib/Android.bp
@@ -26,7 +26,7 @@
cc_library_shared {
name: "ApPowerControlLib",
vendor: true,
- srcs: ["*.cpp"],
+ srcs: ["ApPowerControl.cpp"],
local_include_dirs: ["."],
export_include_dirs: ["."],
}
diff --git a/automotive/remoteaccess/test_grpc_server/lib/ApPowerControlHost.cpp b/automotive/remoteaccess/test_grpc_server/lib/ApPowerControlHost.cpp
new file mode 100644
index 0000000..a475b00
--- /dev/null
+++ b/automotive/remoteaccess/test_grpc_server/lib/ApPowerControlHost.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 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 "ApPowerControl.h"
+
+#include <cstdio>
+
+void wakeupAp() {
+ printf("Waking up application processor...\n");
+}