1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <signal.h>
18 #include <stdio.h>
19 #include <sys/wait.h>
20 #include <iostream>
21 #include <sstream>
22 #include <string>
23 #include <thread>
24 
25 #ifndef HOST
26 #include "ApPowerControl.h"
27 #endif  // #ifndef HOST
28 
29 #include "TestWakeupClientServiceImpl.h"
30 
31 #include <grpc/grpc.h>
32 #include <grpcpp/security/server_credentials.h>
33 #include <grpcpp/server.h>
34 #include <grpcpp/server_builder.h>
35 
36 namespace {
37 
38 using ::android::hardware::automotive::remoteaccess::BOOTUP_REASON_SYSTEM_ENTER_GARAGE_MODE;
39 using ::android::hardware::automotive::remoteaccess::BOOTUP_REASON_SYSTEM_REMOTE_ACCESS;
40 using ::android::hardware::automotive::remoteaccess::BOOTUP_REASON_USER_POWER_ON;
41 using ::android::hardware::automotive::remoteaccess::PowerControllerServiceImpl;
42 using ::android::hardware::automotive::remoteaccess::ServiceImpl;
43 using ::android::hardware::automotive::remoteaccess::WakeupClientServiceImpl;
44 using ::grpc::Server;
45 using ::grpc::ServerBuilder;
46 using ::grpc::ServerWriter;
47 
48 constexpr int SHUTDOWN_REQUEST = 289410889;
49 constexpr int VEHICLE_IN_USE = 287313738;
50 constexpr char COMMAND_RUN_EMU_LOCAL_IMAGE[] =
51         "source ~/.aae-toolbox/bin/bashrc && aae emulator run";
52 constexpr char COMMAND_RUN_EMU[] = "./launch_emu.sh -v \"-writable-system -selinux permissive\"";
53 constexpr char COMMAND_SET_VHAL_PROP[] =
54         "adb -s emulator-5554 wait-for-device && adb -s emulator-5554 root "
55         "&& sleep 1 && adb -s emulator-5554 wait-for-device && adb -s emulator-5554 shell "
56         "dumpsys android.hardware.automotive.vehicle.IVehicle/default --set %d -i %d";
57 
58 pid_t emuPid = 0;
59 const char* runEmuCommand = COMMAND_RUN_EMU;
60 
61 }  // namespace
62 
RunServer(const std::string & serviceAddr,std::shared_ptr<ServiceImpl> service)63 void RunServer(const std::string& serviceAddr, std::shared_ptr<ServiceImpl> service) {
64     ServerBuilder builder;
65     builder.AddListeningPort(serviceAddr, grpc::InsecureServerCredentials());
66     WakeupClientServiceImpl wakeupClientService(service.get());
67     builder.RegisterService(&wakeupClientService);
68     PowerControllerServiceImpl powerControllerService(service.get());
69     builder.RegisterService(&powerControllerService);
70     std::unique_ptr<Server> server(builder.BuildAndStart());
71     printf("Test Remote Access GRPC Server listening on %s\n", serviceAddr.c_str());
72     server->Wait();
73 }
74 
runCommand(const char * bashCommand)75 pid_t runCommand(const char* bashCommand) {
76     pid_t pid = fork();
77     if (pid == 0) {
78         // In child process. Put it into a separate process group so we can kill it.
79         setpgid(0, 0);
80         execl("/bin/bash", "bash", "-c", bashCommand, /*terminateArg=*/nullptr);
81         exit(0);
82     } else {
83         return pid;
84     }
85 }
86 
updateEmuStatus()87 void updateEmuStatus() {
88     if (emuPid == 0) {
89         return;
90     }
91     pid_t pid = waitpid(emuPid, nullptr, WNOHANG);
92     if (pid == emuPid) {
93         // Emu process already exited. If Emu process is still running, pid will be 0.
94         emuPid = 0;
95     }
96 }
97 
powerOnEmu(ServiceImpl * service,int32_t bootupReason)98 bool powerOnEmu(ServiceImpl* service, int32_t bootupReason) {
99     updateEmuStatus();
100     if (emuPid != 0) {
101         printf("The emulator is already running\n");
102         return false;
103     }
104     service->setBootupReason(bootupReason);
105     emuPid = runCommand(runEmuCommand);
106     printf("Emulator started in process: %d\n", emuPid);
107     return true;
108 }
109 
powerOn(ServiceImpl * service,int32_t bootupReason)110 bool powerOn(ServiceImpl* service, int32_t bootupReason) {
111 #ifdef HOST
112     return powerOnEmu(service, bootupReason);
113 #else
114     printf("power on is only supported on host\n");
115     return false;
116 #endif
117 }
118 
getSetPropCommand(int propId,int value)119 const char* getSetPropCommand(int propId, int value) {
120     int size = snprintf(nullptr, 0, COMMAND_SET_VHAL_PROP, propId, value);
121     char* command = new char[size + 1];
122     snprintf(command, size + 1, COMMAND_SET_VHAL_PROP, propId, value);
123     return command;
124 }
125 
getSetPropCommand(int propId)126 const char* getSetPropCommand(int propId) {
127     return getSetPropCommand(propId, /*value=*/1);
128 }
129 
powerOffEmu()130 void powerOffEmu() {
131     updateEmuStatus();
132     if (emuPid == 0) {
133         printf("The emulator is not running\n");
134         return;
135     }
136     const char* command = getSetPropCommand(SHUTDOWN_REQUEST);
137     runCommand(command);
138     delete[] command;
139     waitpid(emuPid, nullptr, /*options=*/0);
140     emuPid = 0;
141 }
142 
powerOff()143 void powerOff() {
144 #ifdef HOST
145     powerOffEmu();
146 #else
147     printf("power off is only supported on host\n");
148 #endif
149 }
150 
help()151 void help() {
152     std::cout << "Remote Access Host Test Utility" << std::endl
153               << "help:\t"
154               << "Print out this help info" << std::endl
155               << "genFakeTask start [clientID]:\t"
156               << "Start generating a fake task every 5s" << std::endl
157               << "genFakeTask stop:\t"
158               << "Stop the fake task generation" << std::endl
159               << "status:\t"
160               << "Print current status" << std::endl
161               << "power on:\t"
162               << "Power on the emulator, simulate user enters vehicle while AP is off"
163               << " (only supported on host)" << std::endl
164               << "power off:\t"
165               << "Power off the emulator, simulate user leaves vehicle"
166               << " (only supported on host)" << std::endl
167               << "inject task [clientID] [taskData]:\t"
168               << "Inject a remote task" << std::endl
169               << "set vehicleInUse:\t"
170               << "Set vehicle in use, simulate user enter vehicle while boot up for remote task "
171               << "(only supported on host)" << std::endl;
172 }
173 
parseCommand(const std::string & userInput,std::shared_ptr<ServiceImpl> service)174 void parseCommand(const std::string& userInput, std::shared_ptr<ServiceImpl> service) {
175     if (userInput == "") {
176         // ignore empty line.
177     } else if (userInput == "help") {
178         help();
179     } else if (userInput.rfind("genFakeTask start", 0) == 0) {
180         std::string clientId;
181         std::stringstream ss;
182         ss << userInput;
183         int i = 0;
184         while (std::getline(ss, clientId, ' ')) {
185             i++;
186             if (i == 3) {
187                 break;
188             }
189         }
190         if (i != 3) {
191             printf("Missing clientId, see 'help'\n");
192             return;
193         }
194         service->startGeneratingFakeTask(clientId);
195     } else if (userInput == "genFakeTask stop") {
196         service->stopGeneratingFakeTask();
197     } else if (userInput == "status") {
198         printf("isWakeupRequired: %B, isRemoteTaskConnectionAlive: %B\n",
199                service->isWakeupRequired(), service->isRemoteTaskConnectionAlive());
200     } else if (userInput == "power on") {
201         service->setVehicleInUse(true);
202         powerOn(service.get(), BOOTUP_REASON_USER_POWER_ON);
203     } else if (userInput == "power off") {
204         service->setVehicleInUse(false);
205         powerOff();
206     } else if (userInput.rfind("inject task", 0) == 0) {
207         std::stringstream ss;
208         ss << userInput;
209         std::string data;
210         std::string taskData;
211         std::string clientId;
212         int i = 0;
213         while (std::getline(ss, data, ' ')) {
214             i++;
215             if (i == 3) {
216                 clientId = data;
217             }
218             if (i == 4) {
219                 taskData = data;
220             }
221         }
222         if (taskData == "" || clientId == "") {
223             printf("Missing taskData or clientId, see 'help'\n");
224             return;
225         }
226         service->injectTask(taskData, clientId);
227         printf("Remote task with client ID: %s, data: %s injected\n", clientId.c_str(),
228                taskData.c_str());
229     } else if (userInput == "set vehicleInUse") {
230         service->setVehicleInUse(true);
231     } else {
232         printf("Unknown command, see 'help'\n");
233     }
234 }
235 
saHandler(int signum)236 void saHandler(int signum) {
237     if (emuPid != 0) {
238         kill(-emuPid, signum);
239         waitpid(emuPid, nullptr, /*options=*/0);
240         // Sleep for 1 seconds to allow emulator to print out logs.
241         sleep(1);
242     }
243     exit(-1);
244 }
245 
246 class MyServiceImpl final : public ServiceImpl {
247   public:
wakeupApplicationProcessor(int32_t bootupReason)248     void wakeupApplicationProcessor(int32_t bootupReason) override {
249 #ifdef HOST
250         powerOnEmu(this, bootupReason);
251 #else
252         wakeupAp();
253 #endif
254     };
255 };
256 
257 // Usage: TestWakeupClientServerHost [--local-image] [service_address_to_start]
main(int argc,char ** argv)258 int main(int argc, char** argv) {
259     std::string serviceAddr = GRPC_SERVICE_ADDRESS;
260     for (int i = 1; i < argc; i++) {
261         char* arg = argv[1];
262         if (strcmp(arg, "--local-image") == 0) {
263             runEmuCommand = COMMAND_RUN_EMU_LOCAL_IMAGE;
264             continue;
265         }
266         serviceAddr = arg;
267     }
268     // Let the server thread run, we will force kill the server when we exit the program.
269     std::shared_ptr<ServiceImpl> service = std::make_shared<MyServiceImpl>();
270     std::thread serverThread([serviceAddr, service] { RunServer(serviceAddr, service); });
271 
272     // Register the signal handler for SIGTERM and SIGINT so that we can stop the emulator before
273     // exit.
274     struct sigaction sa = {};
275     sigemptyset(&sa.sa_mask);
276     sa.sa_handler = saHandler;
277     sigaction(SIGTERM, &sa, nullptr);
278     sigaction(SIGINT, &sa, nullptr);
279 
280     // Start processing the user inputs.
281     std::string userInput;
282     while (true) {
283         std::cout << ">>> ";
284         std::getline(std::cin, userInput);
285         parseCommand(userInput, service);
286     }
287     return 0;
288 }
289