Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3a129d7a authored by Yu Shan's avatar Yu Shan
Browse files

Implement a host side TestWakeupClientService.

Implement a host program that could be used to test the remote
access feature. The program will provides grpc service that the
remote access HAL running on device can talk to to get tasks. The
program will also use adb/aemu command to control the power of the
Android instance.

This CL also changes the GRPC service address for the default
remoteaccess HAL to 10.0.2.2 which points to the host's 127.0.0.1
from the emulator.

Test: m -j TestWakeupClientServerHost, run manually.
TestWakeupClientServerHost
>>> inject task [clientID] taskdata
verify emualtor boot up
verify emulator shutdown after task finish
>>> power on
verify emualtor boot up
>>> inject task [clientID] taskdata
verify emulator does not shutdown after task finish
>>> power off
>>> inject task [clientID] taskdata
verify emulator boot up
>>> set vehicleInUse
verify emulator does not shutdown after task finish
>>> power off
>>> genFakeTask start [clientID]
verify emulator starts up to execute remote tasks.
verify emulator shuts down after 300s.
verify emulator restarts after shutdown.
>>> genFakeTask stop
verify emulator shuts down after finishing last task.
Bug: 285205122

Change-Id: I22b660448d8b495cf3ced3378c1c1dc10051d87a
parent 892e1fea
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -53,7 +53,7 @@ cc_binary {
    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\"",
    ],
}

+3 −4
Original line number Diff line number Diff line
@@ -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 @@ int main(int /* argc */, char* /* argv */[]) {
                                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());
+21 −1
Original line number Diff line number Diff line
@@ -38,6 +38,26 @@ cc_binary {
    ],
    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",
    ],
}
+39 −9
Original line number Diff line number Diff line
@@ -34,11 +34,9 @@ namespace remoteaccess {
// 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 @@ class TaskQueue final {
    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 @@ class TestWakeupClientServiceImpl final : public WakeupClient::Service {
                                      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
+63 −24
Original line number Diff line number Diff line
@@ -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 @@ constexpr int64_t KTaskTimeoutInMs = 20'000;

}  // 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 @@ void TaskQueue::handleTaskTimeout() {
    }
}

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 @@ Status TestWakeupClientServiceImpl::GetRemoteTasks(ServerContext* context,
                                                   const GetRemoteTasksRequest* request,
                                                   ServerWriter<GetRemoteTasksResponse>* writer) {
    printf("GetRemoteTasks called\n");
    mRemoteTaskConnectionAlive = true;
    while (true) {
        mTaskQueue.waitForTask();

@@ -226,16 +253,19 @@ Status TestWakeupClientServiceImpl::GetRemoteTasks(ServerContext* context,
                // 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 @@ Status TestWakeupClientServiceImpl::NotifyWakeupRequired(ServerContext* context,
        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
Loading