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 "TestWakeupClientServiceImpl.h"
18 
19 #include <android-base/stringprintf.h>
20 #include <inttypes.h>
21 #include <utils/Looper.h>
22 #include <utils/SystemClock.h>
23 #include <chrono>
24 #include <thread>
25 
26 namespace android {
27 namespace hardware {
28 namespace automotive {
29 namespace remoteaccess {
30 
31 namespace {
32 
33 using ::android::uptimeMillis;
34 using ::android::base::ScopedLockAssertion;
35 using ::android::base::StringPrintf;
36 using ::grpc::ServerContext;
37 using ::grpc::ServerWriter;
38 using ::grpc::Status;
39 
40 constexpr int64_t kTaskIntervalInMs = 5'000;
41 constexpr int64_t kTaskTimeoutInMs = 60'000;
42 
msToNs(int64_t ms)43 int64_t msToNs(int64_t ms) {
44     return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds(ms))
45             .count();
46 }
47 
sToNs(int64_t s)48 int64_t sToNs(int64_t s) {
49     return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(s)).count();
50 }
51 
52 }  // namespace
53 
generateTask(const std::string & clientId)54 GetRemoteTasksResponse FakeTaskGenerator::generateTask(const std::string& clientId) {
55     GetRemoteTasksResponse response;
56     response.set_data(reinterpret_cast<const char*>(DATA), sizeof(DATA));
57     response.set_clientid(clientId);
58     return response;
59 }
60 
TaskTimeoutMessageHandler(TaskQueue * taskQueue)61 TaskTimeoutMessageHandler::TaskTimeoutMessageHandler(TaskQueue* taskQueue)
62     : mTaskQueue(taskQueue) {}
63 
handleMessage(const android::Message & message)64 void TaskTimeoutMessageHandler::handleMessage(const android::Message& message) {
65     mTaskQueue->handleTaskTimeout();
66 }
67 
TaskQueue(android::sp<Looper> looper)68 TaskQueue::TaskQueue(android::sp<Looper> looper) {
69     mTaskTimeoutMessageHandler = android::sp<TaskTimeoutMessageHandler>::make(this);
70     mLooper = looper;
71 }
72 
maybePopOne()73 std::optional<GetRemoteTasksResponse> TaskQueue::maybePopOne() {
74     std::lock_guard<std::mutex> lockGuard(mLock);
75     if (mTasks.size() == 0) {
76         return std::nullopt;
77     }
78     TaskInfo response = std::move(mTasks.top());
79     mTasks.pop();
80     mLooper->removeMessages(mTaskTimeoutMessageHandler, response.taskId);
81     return std::move(response.taskData);
82 }
83 
add(const GetRemoteTasksResponse & task)84 void TaskQueue::add(const GetRemoteTasksResponse& task) {
85     std::lock_guard<std::mutex> lockGuard(mLock);
86     if (mStopped) {
87         return;
88     }
89     int taskId = mTaskIdCounter++;
90     mTasks.push(TaskInfo{
91             .taskId = taskId,
92             .timestampInMs = uptimeMillis(),
93             .taskData = task,
94     });
95     android::Message message(taskId);
96     mLooper->sendMessageDelayed(msToNs(kTaskTimeoutInMs), mTaskTimeoutMessageHandler, message);
97     mTasksNotEmptyCv.notify_all();
98 }
99 
waitForTask()100 void TaskQueue::waitForTask() {
101     std::unique_lock<std::mutex> lock(mLock);
102     mTasksNotEmptyCv.wait(lock, [this] {
103         ScopedLockAssertion lockAssertion(mLock);
104         return mTasks.size() > 0 || mStopped;
105     });
106 }
107 
isStopped()108 bool TaskQueue::isStopped() {
109     return mStopped;
110 }
111 
stopWait()112 void TaskQueue::stopWait() {
113     mStopped = true;
114     {
115         std::lock_guard<std::mutex> lockGuard(mLock);
116         mTasksNotEmptyCv.notify_all();
117     }
118 }
119 
isEmpty()120 bool TaskQueue::isEmpty() {
121     std::lock_guard<std::mutex> lockGuard(mLock);
122     return mTasks.size() == 0 || mStopped;
123 }
124 
handleTaskTimeout()125 void TaskQueue::handleTaskTimeout() {
126     // We know which task timed-out from the taskId in the message. However, there is no easy way
127     // to remove a specific task with the task ID from the priority_queue, so we just check from
128     // the top of the queue (which have the oldest tasks).
129     std::lock_guard<std::mutex> lockGuard(mLock);
130     int64_t now = uptimeMillis();
131     while (mTasks.size() > 0) {
132         const TaskInfo& taskInfo = mTasks.top();
133         if (taskInfo.timestampInMs + kTaskTimeoutInMs > now) {
134             break;
135         }
136         // In real implementation, this should report task failure to remote wakeup server.
137         printf("Task for client ID: %s timed-out, added at %" PRId64 " ms, now %" PRId64 " ms\n",
138                taskInfo.taskData.clientid().c_str(), taskInfo.timestampInMs, now);
139         mTasks.pop();
140     }
141 }
142 
ServiceImpl()143 ServiceImpl::ServiceImpl() {
144     mTaskScheduleMsgHandler = android::sp<TaskScheduleMsgHandler>::make(this);
145     mLooper = android::sp<Looper>::make(/*opts=*/0);
146     mLooperThread = std::thread([this] { loop(); });
147     mTaskQueue = std::make_unique<TaskQueue>(mLooper);
148 }
149 
~ServiceImpl()150 ServiceImpl::~ServiceImpl() {
151     if (mServerStopped) {
152         return;
153     }
154     stopServer();
155 }
156 
stopServer()157 void ServiceImpl::stopServer() {
158     mTaskQueue->stopWait();
159     stopGeneratingFakeTask();
160     // Set the flag so that the loop thread will exit.
161     mServerStopped = true;
162     mLooper->wake();
163     if (mLooperThread.joinable()) {
164         mLooperThread.join();
165     }
166 }
167 
loop()168 void ServiceImpl::loop() {
169     Looper::setForThread(mLooper);
170 
171     while (true) {
172         // Don't use pollAll since it might swallow wake.
173         mLooper->pollOnce(/*timeoutMillis=*/-1);
174         if (mServerStopped) {
175             return;
176         }
177     }
178 }
179 
injectTask(const std::string & taskData,const std::string & clientId)180 void ServiceImpl::injectTask(const std::string& taskData, const std::string& clientId) {
181     GetRemoteTasksResponse response;
182     response.set_data(taskData);
183     response.set_clientid(clientId);
184     injectTaskResponse(response);
185 }
186 
injectTaskResponse(const GetRemoteTasksResponse & response)187 void ServiceImpl::injectTaskResponse(const GetRemoteTasksResponse& response) {
188     printf("Receive a new task\n");
189     mTaskQueue->add(response);
190     if (mWakeupRequired) {
191         wakeupApplicationProcessor(BOOTUP_REASON_SYSTEM_REMOTE_ACCESS);
192     }
193 }
194 
startGeneratingFakeTask(const std::string & clientId)195 void ServiceImpl::startGeneratingFakeTask(const std::string& clientId) {
196     std::lock_guard<std::mutex> lockGuard(mLock);
197     if (mGeneratingFakeTask) {
198         printf("Fake task is already being generated\n");
199         return;
200     }
201     mGeneratingFakeTask = true;
202     mFakeTaskThread = std::thread([this, clientId] { fakeTaskGenerateLoop(clientId); });
203     printf("Started generating fake tasks\n");
204 }
205 
stopGeneratingFakeTask()206 void ServiceImpl::stopGeneratingFakeTask() {
207     {
208         std::lock_guard<std::mutex> lockGuard(mLock);
209         if (!mGeneratingFakeTask) {
210             printf("Fake task is not being generated, do nothing\n");
211             return;
212         }
213         mTaskLoopStoppedCv.notify_all();
214         mGeneratingFakeTask = false;
215     }
216     if (mFakeTaskThread.joinable()) {
217         mFakeTaskThread.join();
218     }
219     printf("Stopped generating fake tasks\n");
220 }
221 
fakeTaskGenerateLoop(const std::string & clientId)222 void ServiceImpl::fakeTaskGenerateLoop(const std::string& clientId) {
223     // In actual implementation, this should communicate with the remote server and receives tasks
224     // from it. Here we simulate receiving one remote task every {kTaskIntervalInMs}ms.
225     while (true) {
226         injectTaskResponse(mFakeTaskGenerator.generateTask(clientId));
227         printf("Sleeping for %" PRId64 " seconds until next task\n", kTaskIntervalInMs);
228 
229         std::unique_lock lk(mLock);
230         if (mTaskLoopStoppedCv.wait_for(lk, std::chrono::milliseconds(kTaskIntervalInMs), [this] {
231                 ScopedLockAssertion lockAssertion(mLock);
232                 return !mGeneratingFakeTask;
233             })) {
234             // If the stopped flag is set, we are quitting, exit the loop.
235             return;
236         }
237     }
238 }
239 
GetRemoteTasks(ServerContext * context,const GetRemoteTasksRequest * request,ServerWriter<GetRemoteTasksResponse> * writer)240 Status ServiceImpl::GetRemoteTasks(ServerContext* context, const GetRemoteTasksRequest* request,
241                                    ServerWriter<GetRemoteTasksResponse>* writer) {
242     printf("GetRemoteTasks called\n");
243     mRemoteTaskConnectionAlive = true;
244     while (true) {
245         mTaskQueue->waitForTask();
246 
247         if (mTaskQueue->isStopped()) {
248             // Server stopped, exit the loop.
249             printf("Server stopped exit loop\n");
250             break;
251         }
252 
253         while (true) {
254             auto maybeTask = mTaskQueue->maybePopOne();
255             if (!maybeTask.has_value()) {
256                 printf("no task left\n");
257                 // No task left, loop again and wait for another task(s).
258                 break;
259             }
260             // Loop through all the task in the queue but obtain lock for each element so we don't
261             // hold lock while writing the response.
262             printf("Sending one remote task\n");
263             const GetRemoteTasksResponse& response = maybeTask.value();
264             if (!writer->Write(response)) {
265                 // Broken stream, maybe the client is shutting down.
266                 printf("Failed to deliver remote task to remote access HAL\n");
267                 // The task failed to be sent, add it back to the queue. The order might change, but
268                 // it is okay.
269                 mTaskQueue->add(response);
270                 mRemoteTaskConnectionAlive = false;
271                 return Status::CANCELLED;
272             }
273         }
274     }
275     // Server stopped, exit the loop.
276     return Status::CANCELLED;
277 }
278 
NotifyWakeupRequired(ServerContext * context,const NotifyWakeupRequiredRequest * request,NotifyWakeupRequiredResponse * response)279 Status ServiceImpl::NotifyWakeupRequired(ServerContext* context,
280                                          const NotifyWakeupRequiredRequest* request,
281                                          NotifyWakeupRequiredResponse* response) {
282     printf("NotifyWakeupRequired called\n");
283     if (request->iswakeuprequired() && !mWakeupRequired && !mTaskQueue->isEmpty()) {
284         // If wakeup is now required and previously not required, this means we have finished
285         // shutting down the device. If there are still pending tasks, try waking up AP again
286         // to finish executing those tasks.
287         wakeupApplicationProcessor(BOOTUP_REASON_SYSTEM_REMOTE_ACCESS);
288     }
289     mWakeupRequired = request->iswakeuprequired();
290     if (mWakeupRequired) {
291         // We won't know the connection is down unless we try to send a task over. If wakeup is
292         // required, the connection is very likely already down.
293         mRemoteTaskConnectionAlive = false;
294     }
295     return Status::OK;
296 }
297 
cleanupScheduledTaskLocked(const std::string & clientId,const std::string & scheduleId)298 void ServiceImpl::cleanupScheduledTaskLocked(const std::string& clientId,
299                                              const std::string& scheduleId) {
300     mInfoByScheduleIdByClientId[clientId].erase(scheduleId);
301     if (mInfoByScheduleIdByClientId[clientId].size() == 0) {
302         mInfoByScheduleIdByClientId.erase(clientId);
303     }
304 }
305 
TaskScheduleMsgHandler(ServiceImpl * impl)306 TaskScheduleMsgHandler::TaskScheduleMsgHandler(ServiceImpl* impl) : mImpl(impl) {}
307 
handleMessage(const android::Message & message)308 void TaskScheduleMsgHandler::handleMessage(const android::Message& message) {
309     mImpl->handleAddTask(message.what);
310 }
311 
ScheduleTask(ServerContext * context,const ScheduleTaskRequest * request,ScheduleTaskResponse * response)312 Status ServiceImpl::ScheduleTask(ServerContext* context, const ScheduleTaskRequest* request,
313                                  ScheduleTaskResponse* response) {
314     std::lock_guard<std::mutex> lockGuard(mLock);
315 
316     const GrpcScheduleInfo& grpcScheduleInfo = request->scheduleinfo();
317     const std::string& scheduleId = grpcScheduleInfo.scheduleid();
318     const std::string& clientId = grpcScheduleInfo.clientid();
319     response->set_errorcode(ErrorCode::OK);
320 
321     if (mInfoByScheduleIdByClientId.find(clientId) != mInfoByScheduleIdByClientId.end() &&
322         mInfoByScheduleIdByClientId[clientId].find(scheduleId) !=
323                 mInfoByScheduleIdByClientId[clientId].end()) {
324         printf("Duplicate schedule Id: %s for client Id: %s\n", scheduleId.c_str(),
325                clientId.c_str());
326         response->set_errorcode(ErrorCode::INVALID_ARG);
327         return Status::OK;
328     }
329 
330     int64_t startTimeInEpochSeconds = grpcScheduleInfo.starttimeinepochseconds();
331     int64_t periodicInSeconds = grpcScheduleInfo.periodicinseconds();
332     int32_t count = grpcScheduleInfo.count();
333 
334     int scheduleMsgId = mScheduleMsgCounter++;
335     mInfoByScheduleIdByClientId[clientId][scheduleId] = {
336             .grpcScheduleInfo = std::make_unique<GrpcScheduleInfo>(grpcScheduleInfo),
337             .scheduleMsgId = scheduleMsgId,
338             .periodicInSeconds = periodicInSeconds,
339             .currentCount = 0,
340             .totalCount = count,
341     };
342 
343     int64_t delayInSeconds =
344             startTimeInEpochSeconds - std::chrono::duration_cast<std::chrono::seconds>(
345                                               std::chrono::system_clock::now().time_since_epoch())
346                                               .count();
347     if (delayInSeconds < 0) {
348         delayInSeconds = 0;
349     }
350 
351     printf("ScheduleTask called with client Id: %s, schedule Id: %s, delay: %" PRId64 " s\n",
352            clientId.c_str(), scheduleId.c_str(), delayInSeconds);
353 
354     mLooper->sendMessageDelayed(sToNs(delayInSeconds), mTaskScheduleMsgHandler,
355                                 android::Message(scheduleMsgId));
356 
357     return Status::OK;
358 }
359 
getScheduleInfoLocked(int scheduleMsgId,ScheduleInfo ** outScheduleInfoPtr)360 bool ServiceImpl::getScheduleInfoLocked(int scheduleMsgId, ScheduleInfo** outScheduleInfoPtr) {
361     for (auto& [_, infoByScheduleId] : mInfoByScheduleIdByClientId) {
362         for (auto& [_, scheduleInfo] : infoByScheduleId) {
363             if (scheduleInfo.scheduleMsgId == scheduleMsgId) {
364                 *outScheduleInfoPtr = &scheduleInfo;
365                 return true;
366             }
367         }
368     }
369     return false;
370 }
371 
handleAddTask(int scheduleMsgId)372 void ServiceImpl::handleAddTask(int scheduleMsgId) {
373     std::lock_guard<std::mutex> lockGuard(mLock);
374 
375     ScheduleInfo* scheduleInfoPtr;
376     bool found = getScheduleInfoLocked(scheduleMsgId, &scheduleInfoPtr);
377     if (!found) {
378         printf("The schedule msg Id: %d is not found\n", scheduleMsgId);
379         return;
380     }
381 
382     const GrpcScheduleInfo& grpcScheduleInfo = *scheduleInfoPtr->grpcScheduleInfo;
383     const std::string scheduleId = grpcScheduleInfo.scheduleid();
384     const std::string clientId = grpcScheduleInfo.clientid();
385     scheduleInfoPtr->currentCount++;
386     ScheduleTaskType taskType = grpcScheduleInfo.tasktype();
387     printf("Sending scheduled tasks for scheduleId: %s, clientId: %s, taskCount: %d, "
388            "taskType: %d\n",
389            scheduleId.c_str(), clientId.c_str(), scheduleInfoPtr->currentCount,
390            static_cast<int>(taskType));
391 
392     if (taskType == ScheduleTaskType::ENTER_GARAGE_MODE) {
393         if (mWakeupRequired) {
394             wakeupApplicationProcessor(BOOTUP_REASON_SYSTEM_ENTER_GARAGE_MODE);
395         } else {
396             printf("Ignore ENTER_GARAGE_MODE task type because the head unit is already running");
397         }
398     } else if (grpcScheduleInfo.tasktype() == ScheduleTaskType::CUSTOM) {
399         GetRemoteTasksResponse injectResponse;
400         injectResponse.set_data(grpcScheduleInfo.data().data(), grpcScheduleInfo.data().size());
401         injectResponse.set_clientid(clientId);
402         injectTaskResponse(injectResponse);
403     } else {
404         printf("Unknown task type: %d\n", static_cast<int>(taskType));
405     }
406 
407     if (scheduleInfoPtr->totalCount != 0 &&
408         scheduleInfoPtr->currentCount == scheduleInfoPtr->totalCount) {
409         // This schedule is finished.
410         cleanupScheduledTaskLocked(clientId, scheduleId);
411         return;
412     }
413 
414     // Schedule the task for the next period.
415     mLooper->sendMessageDelayed(sToNs(scheduleInfoPtr->periodicInSeconds), mTaskScheduleMsgHandler,
416                                 android::Message(scheduleMsgId));
417 }
418 
UnscheduleTask(ServerContext * context,const UnscheduleTaskRequest * request,UnscheduleTaskResponse * response)419 Status ServiceImpl::UnscheduleTask(ServerContext* context, const UnscheduleTaskRequest* request,
420                                    UnscheduleTaskResponse* response) {
421     std::lock_guard<std::mutex> lockGuard(mLock);
422 
423     const std::string& clientId = request->clientid();
424     const std::string& scheduleId = request->scheduleid();
425     printf("UnscheduleTask called with client Id: %s, schedule Id: %s\n", clientId.c_str(),
426            scheduleId.c_str());
427 
428     if (mInfoByScheduleIdByClientId.find(clientId) == mInfoByScheduleIdByClientId.end() ||
429         mInfoByScheduleIdByClientId[clientId].find(scheduleId) ==
430                 mInfoByScheduleIdByClientId[clientId].end()) {
431         printf("UnscheduleTask: no task associated with clientId: %s, scheduleId: %s\n",
432                clientId.c_str(), scheduleId.c_str());
433         return Status::OK;
434     }
435 
436     mLooper->removeMessages(mTaskScheduleMsgHandler,
437                             mInfoByScheduleIdByClientId[clientId][scheduleId].scheduleMsgId);
438     cleanupScheduledTaskLocked(clientId, scheduleId);
439     return Status::OK;
440 }
441 
UnscheduleAllTasks(ServerContext * context,const UnscheduleAllTasksRequest * request,UnscheduleAllTasksResponse * response)442 Status ServiceImpl::UnscheduleAllTasks(ServerContext* context,
443                                        const UnscheduleAllTasksRequest* request,
444                                        UnscheduleAllTasksResponse* response) {
445     std::lock_guard<std::mutex> lockGuard(mLock);
446 
447     const std::string& clientId = request->clientid();
448     printf("UnscheduleAllTasks called with client Id: %s\n", clientId.c_str());
449     if (mInfoByScheduleIdByClientId.find(clientId) == mInfoByScheduleIdByClientId.end()) {
450         printf("UnscheduleTask: no task associated with clientId: %s\n", clientId.c_str());
451         return Status::OK;
452     }
453     const auto& infoByScheduleId = mInfoByScheduleIdByClientId[clientId];
454     std::vector<int> scheduleMsgIds;
455     for (const auto& [_, scheduleInfo] : infoByScheduleId) {
456         mLooper->removeMessages(mTaskScheduleMsgHandler, /*what=*/scheduleInfo.scheduleMsgId);
457     }
458 
459     mInfoByScheduleIdByClientId.erase(clientId);
460     return Status::OK;
461 }
462 
IsTaskScheduled(ServerContext * context,const IsTaskScheduledRequest * request,IsTaskScheduledResponse * response)463 Status ServiceImpl::IsTaskScheduled(ServerContext* context, const IsTaskScheduledRequest* request,
464                                     IsTaskScheduledResponse* response) {
465     std::lock_guard<std::mutex> lockGuard(mLock);
466 
467     const std::string& clientId = request->clientid();
468     const std::string& scheduleId = request->scheduleid();
469     printf("IsTaskScheduled called with client Id: %s, scheduleId: %s\n", clientId.c_str(),
470            scheduleId.c_str());
471 
472     if (mInfoByScheduleIdByClientId.find(clientId) == mInfoByScheduleIdByClientId.end()) {
473         response->set_istaskscheduled(false);
474         return Status::OK;
475     }
476     if (mInfoByScheduleIdByClientId[clientId].find(scheduleId) ==
477         mInfoByScheduleIdByClientId[clientId].end()) {
478         response->set_istaskscheduled(false);
479         return Status::OK;
480     }
481     response->set_istaskscheduled(true);
482     return Status::OK;
483 }
484 
GetAllPendingScheduledTasks(ServerContext * context,const GetAllPendingScheduledTasksRequest * request,GetAllPendingScheduledTasksResponse * response)485 Status ServiceImpl::GetAllPendingScheduledTasks(ServerContext* context,
486                                                 const GetAllPendingScheduledTasksRequest* request,
487                                                 GetAllPendingScheduledTasksResponse* response) {
488     const std::string& clientId = request->clientid();
489     printf("GetAllPendingScheduledTasks called with client Id: %s\n", clientId.c_str());
490     response->clear_allscheduledtasks();
491     {
492         std::unique_lock lk(mLock);
493         if (mInfoByScheduleIdByClientId.find(clientId) == mInfoByScheduleIdByClientId.end()) {
494             return Status::OK;
495         }
496         for (const auto& [_, scheduleInfo] : mInfoByScheduleIdByClientId[clientId]) {
497             (*response->add_allscheduledtasks()) = *scheduleInfo.grpcScheduleInfo;
498         }
499     }
500     return Status::OK;
501 }
502 
IsVehicleInUse(ServerContext * context,const IsVehicleInUseRequest * request,IsVehicleInUseResponse * response)503 Status ServiceImpl::IsVehicleInUse(ServerContext* context, const IsVehicleInUseRequest* request,
504                                    IsVehicleInUseResponse* response) {
505     response->set_isvehicleinuse(mVehicleInUse);
506     return Status::OK;
507 }
508 
GetApPowerBootupReason(ServerContext * context,const GetApPowerBootupReasonRequest * request,GetApPowerBootupReasonResponse * response)509 Status ServiceImpl::GetApPowerBootupReason(ServerContext* context,
510                                            const GetApPowerBootupReasonRequest* request,
511                                            GetApPowerBootupReasonResponse* response) {
512     response->set_bootupreason(mBootupReason);
513     return Status::OK;
514 }
515 
isWakeupRequired()516 bool ServiceImpl::isWakeupRequired() {
517     return mWakeupRequired;
518 }
519 
isRemoteTaskConnectionAlive()520 bool ServiceImpl::isRemoteTaskConnectionAlive() {
521     return mRemoteTaskConnectionAlive;
522 }
523 
setVehicleInUse(bool vehicleInUse)524 void ServiceImpl::setVehicleInUse(bool vehicleInUse) {
525     mVehicleInUse = vehicleInUse;
526 }
527 
setBootupReason(int32_t bootupReason)528 void ServiceImpl::setBootupReason(int32_t bootupReason) {
529     mBootupReason = bootupReason;
530 }
531 
532 }  // namespace remoteaccess
533 }  // namespace automotive
534 }  // namespace hardware
535 }  // namespace android
536