Add remote access HAL interface and ref impl.
Add remote access HAL interface and reference implementation. This CL is a merge of multiple CLs commited in internal master. Test: Presubmit Bug: 241170646 Change-Id: I55ba98015055d779a362cac05a9f68650b5b92ab Merged-In: I332221b303274463dfa5b46d78cf0d81f6045e4b
This commit is contained in:
parent
c1c823a9c1
commit
7a5283fda1
25 changed files with 2410 additions and 0 deletions
39
automotive/remoteaccess/Android.bp
Normal file
39
automotive/remoteaccess/Android.bp
Normal file
|
@ -0,0 +1,39 @@
|
|||
// 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.
|
||||
|
||||
package {
|
||||
default_applicable_licenses: ["hardware_interfaces_license"],
|
||||
}
|
||||
|
||||
aidl_interface {
|
||||
name: "android.hardware.automotive.remoteaccess",
|
||||
vendor_available: true,
|
||||
srcs: [
|
||||
"android/hardware/automotive/remoteaccess/**/*.aidl",
|
||||
],
|
||||
stability: "vintf",
|
||||
backend: {
|
||||
cpp: {
|
||||
enabled: false,
|
||||
},
|
||||
java: {
|
||||
sdk_version: "module_current",
|
||||
min_sdk_version: "31",
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
"com.android.car.framework",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
2
automotive/remoteaccess/OWNERS
Normal file
2
automotive/remoteaccess/OWNERS
Normal file
|
@ -0,0 +1,2 @@
|
|||
ericjeong@google.com
|
||||
shanyu@google.com
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.automotive.remoteaccess;
|
||||
@VintfStability
|
||||
parcelable ApState {
|
||||
boolean isReadyForRemoteTask;
|
||||
boolean isWakeupRequired;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.automotive.remoteaccess;
|
||||
@VintfStability
|
||||
interface IRemoteAccess {
|
||||
String getDeviceId();
|
||||
String getWakeupServiceName();
|
||||
void setRemoteTaskCallback(android.hardware.automotive.remoteaccess.IRemoteTaskCallback callback);
|
||||
void clearRemoteTaskCallback();
|
||||
void notifyApStateChange(in android.hardware.automotive.remoteaccess.ApState state);
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
|
||||
// two cases:
|
||||
// 1). this is a frozen version file - do not edit this in any case.
|
||||
// 2). this is a 'current' file. If you make a backwards compatible change to
|
||||
// the interface (from the latest frozen version), the build system will
|
||||
// prompt you to update this file with `m <name>-update-api`.
|
||||
//
|
||||
// You must not make a backward incompatible change to any AIDL file built
|
||||
// with the aidl_interface module type with versions property set. The module
|
||||
// type is used to build AIDL files in a way that they can be used across
|
||||
// independently updatable components of the system. If a device is shipped
|
||||
// with such a backward incompatible change, it has a high risk of breaking
|
||||
// later when a module using the interface is updated, e.g., Mainline modules.
|
||||
|
||||
package android.hardware.automotive.remoteaccess;
|
||||
@VintfStability
|
||||
interface IRemoteTaskCallback {
|
||||
oneway void onRemoteTaskRequested(String clientId, in byte[] data);
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package android.hardware.automotive.remoteaccess;
|
||||
|
||||
@VintfStability
|
||||
parcelable ApState {
|
||||
/**
|
||||
* Whether AP (application processor) is ready to receive remote tasks.
|
||||
*
|
||||
* If this is true. AP is powered on and the car service is ready to handle
|
||||
* remote tasks.
|
||||
*/
|
||||
boolean isReadyForRemoteTask;
|
||||
/**
|
||||
* Whether AP (application processor) needs to be woken up.
|
||||
*
|
||||
* While the AP is shutting down, this will be set to false to prevent the
|
||||
* wakeup signal to interrupt the shutdown process. At the last step of the
|
||||
* shutdown process, this will be set to true so that AP will be waken
|
||||
* up when task arrives. After AP starts up, this will be set to false
|
||||
* to prevent unnecessary wakeup signal.
|
||||
*/
|
||||
boolean isWakeupRequired;
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package android.hardware.automotive.remoteaccess;
|
||||
|
||||
import android.hardware.automotive.remoteaccess.ApState;
|
||||
import android.hardware.automotive.remoteaccess.IRemoteTaskCallback;
|
||||
|
||||
/**
|
||||
* Interface representing a remote wakeup client.
|
||||
*
|
||||
* A wakeup client is a binary outside Android framework that communicates with
|
||||
* a wakeup server and receives wake up command.
|
||||
*/
|
||||
@VintfStability
|
||||
interface IRemoteAccess {
|
||||
/**
|
||||
* Gets a unique device ID that could be recognized by wake up server.
|
||||
*
|
||||
* This device ID is provisioned during car production and is registered
|
||||
* with the wake up server.
|
||||
*
|
||||
* @return a unique device ID.
|
||||
*/
|
||||
String getDeviceId();
|
||||
|
||||
/**
|
||||
* Gets the name for the remote wakeup server.
|
||||
*
|
||||
* This name will be provided to remote task server during registration
|
||||
* and used by remote task server to find the remote wakeup server to
|
||||
* use for waking up the device. This name must be pre-negotiated between
|
||||
* the remote wakeup server/client and the remote task server/client and
|
||||
* must be unique. We recommend the format to be a human readable string
|
||||
* with reverse domain name notation (reverse-DNS), e.g.
|
||||
* "com.google.vehicle.wakeup".
|
||||
*/
|
||||
String getWakeupServiceName();
|
||||
|
||||
/**
|
||||
* Sets a callback to be called when a remote task is requested.
|
||||
*
|
||||
* @param callback A callback to be called when a remote task is requested.
|
||||
*/
|
||||
void setRemoteTaskCallback(IRemoteTaskCallback callback);
|
||||
|
||||
/**
|
||||
* Clears a previously set remote task callback.
|
||||
*
|
||||
* If no callback was set, this operation is no-op.
|
||||
*/
|
||||
void clearRemoteTaskCallback();
|
||||
|
||||
/**
|
||||
* Notifies whether AP is ready to receive remote tasks.
|
||||
*
|
||||
* <p>Wakeup client should store and use this state until a new call with a
|
||||
* different state arrives.
|
||||
*
|
||||
* <p>If {@code isReadyForRemoteTask} is true, the wakeup client may send
|
||||
* the task received from the server to AP immediately.
|
||||
*
|
||||
* <p>If {@code isReadyForRemoteTask} is false, it must store the received
|
||||
* remote tasks and wait until AP is ready to receive tasks. If it takes too
|
||||
* long for AP to become ready, the task must be reported to remote task
|
||||
* server as failed. Implementation must make sure no duplicate tasks are
|
||||
* delivered to AP.
|
||||
*
|
||||
* <p>If {@code isWakeupRequired} is true, it must try to wake up AP when a
|
||||
* remote task arrives or when there are pending requests.
|
||||
*
|
||||
* <p>If {@code isWakeupRequired} is false, it must not try to wake up AP.
|
||||
*/
|
||||
void notifyApStateChange(in ApState state);
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package android.hardware.automotive.remoteaccess;
|
||||
|
||||
/**
|
||||
* The callback interface for car service to receive tasks from wakup client.
|
||||
*/
|
||||
@VintfStability
|
||||
interface IRemoteTaskCallback {
|
||||
/**
|
||||
* A callback that is called when a remote task is requested.
|
||||
*
|
||||
* @param clientId An ID to uniquely identify a remote task client.
|
||||
* @param data Opaque task data passed to the remote task client.
|
||||
*/
|
||||
oneway void onRemoteTaskRequested(String clientId, in byte[] data);
|
||||
}
|
109
automotive/remoteaccess/hal/default/Android.bp
Normal file
109
automotive/remoteaccess/hal/default/Android.bp
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package {
|
||||
default_applicable_licenses: ["Android-Apache-2.0"],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "android.hardware.automotive.remoteaccess@V1-default-service",
|
||||
vendor: true,
|
||||
vintf_fragments: ["remoteaccess-default-service.xml"],
|
||||
init_rc: ["remoteaccess-default-service.rc"],
|
||||
relative_install_path: "hw",
|
||||
srcs: ["src/RemoteAccessImpl.cpp"],
|
||||
whole_static_libs: [
|
||||
"RemoteAccessService",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
"liblog",
|
||||
"libutils",
|
||||
"libgrpc++",
|
||||
"libprotobuf-cpp-full",
|
||||
],
|
||||
defaults: [
|
||||
"vhalclient_defaults",
|
||||
],
|
||||
cflags: [
|
||||
"-Wno-unused-parameter",
|
||||
"-DGRPC_SERVICE_ADDRESS=\"localhost:50051\"",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "RemoteAccessService",
|
||||
vendor_available: true,
|
||||
local_include_dirs: ["include"],
|
||||
export_include_dirs: ["include"],
|
||||
srcs: [
|
||||
"src/RemoteAccessService.cpp",
|
||||
],
|
||||
whole_static_libs: [
|
||||
"android.hardware.automotive.remoteaccess-V1-ndk",
|
||||
"wakeup_client_protos",
|
||||
"libvhalclient",
|
||||
],
|
||||
defaults: [
|
||||
"vhalclient_defaults",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
"libcutils",
|
||||
"liblog",
|
||||
"libutils",
|
||||
"libgrpc++",
|
||||
"libprotobuf-cpp-full",
|
||||
],
|
||||
cflags: [
|
||||
"-Wno-unused-parameter",
|
||||
],
|
||||
}
|
||||
|
||||
cc_fuzz {
|
||||
name: "android.hardware.automotive.remoteaccess@V1-default-service.aidl_fuzzer",
|
||||
srcs: ["fuzzer/fuzzer.cpp"],
|
||||
whole_static_libs: [
|
||||
"RemoteAccessService",
|
||||
],
|
||||
static_libs: [
|
||||
"libgtest",
|
||||
"libgmock",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
"liblog",
|
||||
"libutils",
|
||||
"libgrpc++",
|
||||
"libprotobuf-cpp-full",
|
||||
],
|
||||
defaults: [
|
||||
"vhalclient_defaults",
|
||||
"service_fuzzer_defaults",
|
||||
],
|
||||
cflags: [
|
||||
"-Wno-unused-parameter",
|
||||
"-DGRPC_SERVICE_ADDRESS=\"localhost:50051\"",
|
||||
],
|
||||
fuzz_config: {
|
||||
cc: [
|
||||
"shanyu@google.com",
|
||||
],
|
||||
},
|
||||
}
|
101
automotive/remoteaccess/hal/default/fuzzer/fuzzer.cpp
Normal file
101
automotive/remoteaccess/hal/default/fuzzer/fuzzer.cpp
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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 <RemoteAccessService.h>
|
||||
#include <fuzzbinder/libbinder_ndk_driver.h>
|
||||
#include <fuzzer/FuzzedDataProvider.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <grpcpp/test/mock_stream.h>
|
||||
#include <wakeup_client.grpc.pb.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace remoteaccess {
|
||||
|
||||
using ::grpc::ClientAsyncReaderInterface;
|
||||
using ::grpc::ClientAsyncResponseReaderInterface;
|
||||
using ::grpc::ClientContext;
|
||||
using ::grpc::ClientReader;
|
||||
using ::grpc::ClientReaderInterface;
|
||||
using ::grpc::CompletionQueue;
|
||||
using ::grpc::Status;
|
||||
using ::grpc::testing::MockClientReader;
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
|
||||
class MockGrpcClientStub : public WakeupClient::StubInterface {
|
||||
public:
|
||||
ClientReaderInterface<GetRemoteTasksResponse>* GetRemoteTasksRaw(
|
||||
[[maybe_unused]] ClientContext* context,
|
||||
[[maybe_unused]] const GetRemoteTasksRequest& request) override {
|
||||
MockClientReader<GetRemoteTasksResponse>* mockClientReader =
|
||||
new MockClientReader<GetRemoteTasksResponse>();
|
||||
ON_CALL(*mockClientReader, Finish()).WillByDefault(Return(Status::OK));
|
||||
ON_CALL(*mockClientReader, Read(_)).WillByDefault(Return(false));
|
||||
return mockClientReader;
|
||||
}
|
||||
|
||||
Status NotifyWakeupRequired([[maybe_unused]] ClientContext* context,
|
||||
[[maybe_unused]] const NotifyWakeupRequiredRequest& request,
|
||||
[[maybe_unused]] NotifyWakeupRequiredResponse* response) {
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
// Async methods which we do not care.
|
||||
ClientAsyncReaderInterface<GetRemoteTasksResponse>* AsyncGetRemoteTasksRaw(
|
||||
[[maybe_unused]] ClientContext* context,
|
||||
[[maybe_unused]] const GetRemoteTasksRequest& request,
|
||||
[[maybe_unused]] CompletionQueue* cq, [[maybe_unused]] void* tag) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ClientAsyncReaderInterface<GetRemoteTasksResponse>* PrepareAsyncGetRemoteTasksRaw(
|
||||
[[maybe_unused]] ClientContext* context,
|
||||
[[maybe_unused]] const GetRemoteTasksRequest& request,
|
||||
[[maybe_unused]] CompletionQueue* cq) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ClientAsyncResponseReaderInterface<NotifyWakeupRequiredResponse>* AsyncNotifyWakeupRequiredRaw(
|
||||
[[maybe_unused]] ClientContext* context,
|
||||
[[maybe_unused]] const NotifyWakeupRequiredRequest& request,
|
||||
[[maybe_unused]] CompletionQueue* cq) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ClientAsyncResponseReaderInterface<NotifyWakeupRequiredResponse>*
|
||||
PrepareAsyncNotifyWakeupRequiredRaw([[maybe_unused]] ClientContext* context,
|
||||
[[maybe_unused]] const NotifyWakeupRequiredRequest& request,
|
||||
[[maybe_unused]] CompletionQueue* c) {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace remoteaccess
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
android::hardware::automotive::remoteaccess::MockGrpcClientStub stub;
|
||||
std::shared_ptr<android::hardware::automotive::remoteaccess::RemoteAccessService> service =
|
||||
ndk::SharedRefBase::make<
|
||||
android::hardware::automotive::remoteaccess::RemoteAccessService>(&stub);
|
||||
android::fuzzService(service->asBinder().get(), FuzzedDataProvider(data, size));
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* 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 <IVhalClient.h>
|
||||
#include <aidl/android/hardware/automotive/remoteaccess/ApState.h>
|
||||
#include <aidl/android/hardware/automotive/remoteaccess/BnRemoteAccess.h>
|
||||
#include <aidl/android/hardware/automotive/remoteaccess/BnRemoteTaskCallback.h>
|
||||
#include <aidl/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.h>
|
||||
#include <android-base/thread_annotations.h>
|
||||
#include <android/binder_auto_utils.h>
|
||||
#include <utils/SystemClock.h>
|
||||
#include <wakeup_client.grpc.pb.h>
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace remoteaccess {
|
||||
|
||||
// A IRemoteTaskCallback implementation for debug purpose.
|
||||
class DebugRemoteTaskCallback final
|
||||
: public aidl::android::hardware::automotive::remoteaccess::BnRemoteTaskCallback {
|
||||
public:
|
||||
DebugRemoteTaskCallback() { mStartTimeMillis = android::uptimeMillis(); };
|
||||
|
||||
ndk::ScopedAStatus onRemoteTaskRequested(const std::string& clientId,
|
||||
const std::vector<uint8_t>& data) override;
|
||||
std::string printTasks();
|
||||
|
||||
private:
|
||||
struct TaskData {
|
||||
std::string clientId;
|
||||
std::vector<uint8_t> data;
|
||||
};
|
||||
|
||||
std::mutex mLock;
|
||||
int64_t mStartTimeMillis;
|
||||
std::vector<TaskData> mTasks;
|
||||
};
|
||||
|
||||
class RemoteAccessService
|
||||
: public aidl::android::hardware::automotive::remoteaccess::BnRemoteAccess {
|
||||
public:
|
||||
explicit RemoteAccessService(WakeupClient::StubInterface* grpcStub);
|
||||
|
||||
~RemoteAccessService();
|
||||
|
||||
ndk::ScopedAStatus getDeviceId(std::string* deviceId) override;
|
||||
|
||||
ndk::ScopedAStatus getWakeupServiceName(std::string* wakeupServiceName) override;
|
||||
|
||||
ndk::ScopedAStatus setRemoteTaskCallback(
|
||||
const std::shared_ptr<
|
||||
aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback>&
|
||||
callback) override;
|
||||
|
||||
ndk::ScopedAStatus clearRemoteTaskCallback() override;
|
||||
|
||||
ndk::ScopedAStatus notifyApStateChange(
|
||||
const aidl::android::hardware::automotive::remoteaccess::ApState& newState) override;
|
||||
|
||||
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
|
||||
|
||||
private:
|
||||
// For testing.
|
||||
friend class RemoteAccessServiceUnitTest;
|
||||
|
||||
static bool checkDumpPermission();
|
||||
|
||||
WakeupClient::StubInterface* mGrpcStub;
|
||||
std::thread mThread;
|
||||
std::mutex mLock;
|
||||
std::condition_variable mCv;
|
||||
std::shared_ptr<aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback>
|
||||
mRemoteTaskCallback GUARDED_BY(mLock);
|
||||
std::unique_ptr<grpc::ClientContext> mGetRemoteTasksContext GUARDED_BY(mLock);
|
||||
// Associated with mCv to notify the task loop to stop waiting and exit.
|
||||
bool mTaskWaitStopped GUARDED_BY(mLock);
|
||||
// A mutex to make sure startTaskLoop does not overlap with stopTaskLoop.
|
||||
std::mutex mStartStopTaskLoopLock;
|
||||
bool mTaskLoopRunning GUARDED_BY(mStartStopTaskLoopLock);
|
||||
// Default wait time before retry connecting to remote access client is 10s.
|
||||
size_t mRetryWaitInMs = 10'000;
|
||||
std::shared_ptr<DebugRemoteTaskCallback> mDebugCallback;
|
||||
|
||||
void runTaskLoop();
|
||||
void maybeStartTaskLoop();
|
||||
void maybeStopTaskLoop();
|
||||
ndk::ScopedAStatus getDeviceIdWithClient(
|
||||
android::frameworks::automotive::vhal::IVhalClient& client, std::string* deviceId);
|
||||
|
||||
void setRetryWaitInMs(size_t retryWaitInMs) { mRetryWaitInMs = retryWaitInMs; }
|
||||
void dumpHelp(int fd);
|
||||
};
|
||||
|
||||
} // namespace remoteaccess
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
79
automotive/remoteaccess/hal/default/proto/Android.bp
Normal file
79
automotive/remoteaccess/hal/default/proto/Android.bp
Normal file
|
@ -0,0 +1,79 @@
|
|||
// 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.
|
||||
|
||||
package {
|
||||
// See: http://go/android-license-faq
|
||||
// A large-scale-change added 'default_applicable_licenses' to import
|
||||
// all of the 'license_kinds' from "hardware_interfaces_license"
|
||||
// to get the below license kinds:
|
||||
// SPDX-license-identifier-Apache-2.0
|
||||
default_applicable_licenses: ["hardware_interfaces_license"],
|
||||
}
|
||||
|
||||
genrule {
|
||||
name: "wakeup_client_pb_h",
|
||||
tools: [
|
||||
"aprotoc",
|
||||
"protoc-gen-grpc-cpp-plugin",
|
||||
],
|
||||
cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
|
||||
srcs: [
|
||||
"wakeup_client.proto",
|
||||
],
|
||||
out: [
|
||||
"wakeup_client.pb.h",
|
||||
"wakeup_client.grpc.pb.h",
|
||||
],
|
||||
}
|
||||
|
||||
genrule {
|
||||
name: "wakeup_client_pb_cc",
|
||||
tools: [
|
||||
"aprotoc",
|
||||
"protoc-gen-grpc-cpp-plugin",
|
||||
],
|
||||
cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)",
|
||||
srcs: [
|
||||
"wakeup_client.proto",
|
||||
],
|
||||
out: [
|
||||
"wakeup_client.pb.cc",
|
||||
"wakeup_client.grpc.pb.cc",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library_static {
|
||||
name: "wakeup_client_protos",
|
||||
vendor_available: true,
|
||||
host_supported: true,
|
||||
include_dirs: [
|
||||
"external/protobuf/src",
|
||||
],
|
||||
generated_headers: [
|
||||
"wakeup_client_pb_h",
|
||||
],
|
||||
export_generated_headers: [
|
||||
"wakeup_client_pb_h",
|
||||
],
|
||||
generated_sources: [
|
||||
"wakeup_client_pb_cc",
|
||||
],
|
||||
shared_libs: [
|
||||
"libgrpc++",
|
||||
"libprotobuf-cpp-full",
|
||||
],
|
||||
cflags: [
|
||||
"-Wno-unused-parameter",
|
||||
],
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package android.hardware.automotive.remoteaccess;
|
||||
|
||||
/**
|
||||
* Service provided by a wakeup client running on TCU.
|
||||
*/
|
||||
service WakeupClient {
|
||||
/**
|
||||
* Establish a long-live connection to receive remote tasks.
|
||||
*
|
||||
* <p>For the server, whenever a remote task arrives, if the connection is
|
||||
* alive, it will use the return stream to return a task's information.
|
||||
*
|
||||
* <p>If the connection is not alive, the server must stores the remote task
|
||||
* until a new connection is established (which means AP is ready to
|
||||
* receive remote task again) and send the stored tasks.
|
||||
*
|
||||
* <p>If the server closes the connection, the client will try to
|
||||
* reestablish the connection.
|
||||
*/
|
||||
rpc GetRemoteTasks(GetRemoteTasksRequest) returns (stream GetRemoteTasksResponse) {}
|
||||
|
||||
/**
|
||||
* Notifies whether AP is required to be waken up when remote task arrives.
|
||||
*
|
||||
* <p>Wakeup client should store and use this state until a new call with a
|
||||
* different state arrives.
|
||||
*
|
||||
* <p>If {@code isWakeupRequired} in the request is true, it must wake up AP
|
||||
* when a remote task arrives.
|
||||
*
|
||||
* <p>If {@code isWakeupRequired} in the request is false, it must not try
|
||||
* to wake up AP.
|
||||
*/
|
||||
rpc NotifyWakeupRequired(NotifyWakeupRequiredRequest) returns (NotifyWakeupRequiredResponse) {}
|
||||
}
|
||||
|
||||
message GetRemoteTasksRequest {}
|
||||
|
||||
message GetRemoteTasksResponse {
|
||||
string clientId = 1;
|
||||
bytes data = 2;
|
||||
}
|
||||
|
||||
message NotifyWakeupRequiredRequest {
|
||||
bool isWakeupRequired = 1;
|
||||
}
|
||||
|
||||
message NotifyWakeupRequiredResponse {}
|
|
@ -0,0 +1,4 @@
|
|||
service vendor.remoteaccess-default /vendor/bin/hw/android.hardware.automotive.remoteaccess@V1-default-service
|
||||
class hal
|
||||
user vehicle_network
|
||||
group system inet
|
|
@ -0,0 +1,7 @@
|
|||
<manifest version="1.0" type="device">
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.automotive.remoteaccess</name>
|
||||
<version>1</version>
|
||||
<fqname>IRemoteAccess/default</fqname>
|
||||
</hal>
|
||||
</manifest>
|
62
automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp
Normal file
62
automotive/remoteaccess/hal/default/src/RemoteAccessImpl.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "RemoteAccessImpl"
|
||||
|
||||
#include "RemoteAccessService.h"
|
||||
|
||||
#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...");
|
||||
|
||||
#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>(clientStub.get());
|
||||
|
||||
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);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!ABinderProcess_setThreadPoolMaxThreadCount(1)) {
|
||||
ALOGE("%s", "failed to set thread pool max thread count");
|
||||
exit(1);
|
||||
}
|
||||
ABinderProcess_startThreadPool();
|
||||
|
||||
ALOGI("RemoteAccess service Ready");
|
||||
|
||||
ABinderProcess_joinThreadPool();
|
||||
|
||||
ALOGW("Should not reach here");
|
||||
|
||||
return 0;
|
||||
}
|
355
automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
Normal file
355
automotive/remoteaccess/hal/default/src/RemoteAccessService.cpp
Normal file
|
@ -0,0 +1,355 @@
|
|||
/*
|
||||
* 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 "RemoteAccessService.h"
|
||||
|
||||
#include <VehicleUtils.h>
|
||||
#include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android/binder_status.h>
|
||||
#include <grpc++/grpc++.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <utils/Log.h>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace remoteaccess {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::aidl::android::hardware::automotive::remoteaccess::ApState;
|
||||
using ::aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback;
|
||||
using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
|
||||
using ::android::base::ScopedLockAssertion;
|
||||
using ::android::base::StringAppendF;
|
||||
using ::android::base::StringPrintf;
|
||||
using ::android::frameworks::automotive::vhal::IVhalClient;
|
||||
using ::android::hardware::automotive::vehicle::toInt;
|
||||
using ::grpc::ClientContext;
|
||||
using ::grpc::ClientReaderInterface;
|
||||
using ::grpc::Status;
|
||||
using ::grpc::StatusCode;
|
||||
using ::ndk::ScopedAStatus;
|
||||
|
||||
const std::string WAKEUP_SERVICE_NAME = "com.google.vehicle.wakeup";
|
||||
constexpr char COMMAND_SET_AP_STATE[] = "--set-ap-state";
|
||||
constexpr char COMMAND_START_DEBUG_CALLBACK[] = "--start-debug-callback";
|
||||
constexpr char COMMAND_STOP_DEBUG_CALLBACK[] = "--stop-debug-callback";
|
||||
constexpr char COMMAND_SHOW_TASK[] = "--show-task";
|
||||
constexpr char COMMAND_GET_DEVICE_ID[] = "--get-device-id";
|
||||
|
||||
std::vector<uint8_t> stringToBytes(const std::string& s) {
|
||||
const char* data = s.data();
|
||||
return std::vector<uint8_t>(data, data + s.size());
|
||||
}
|
||||
|
||||
ScopedAStatus rpcStatusToScopedAStatus(const Status& status, const std::string& errorMsg) {
|
||||
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
|
||||
status.error_code(), (errorMsg + ", error: " + status.error_message()).c_str());
|
||||
}
|
||||
|
||||
std::string printBytes(const std::vector<uint8_t>& bytes) {
|
||||
std::string s;
|
||||
for (size_t i = 0; i < bytes.size(); i++) {
|
||||
StringAppendF(&s, "%02x", bytes[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
bool checkBoolFlag(const char* flag) {
|
||||
return !strcmp(flag, "1") || !strcmp(flag, "0");
|
||||
}
|
||||
|
||||
void dprintErrorStatus(int fd, const char* detail, const ScopedAStatus& status) {
|
||||
dprintf(fd, "%s, code: %d, error: %s\n", detail, status.getStatus(), status.getMessage());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RemoteAccessService::RemoteAccessService(WakeupClient::StubInterface* grpcStub)
|
||||
: mGrpcStub(grpcStub){};
|
||||
|
||||
RemoteAccessService::~RemoteAccessService() {
|
||||
maybeStopTaskLoop();
|
||||
}
|
||||
|
||||
void RemoteAccessService::maybeStartTaskLoop() {
|
||||
std::lock_guard<std::mutex> lockGuard(mStartStopTaskLoopLock);
|
||||
if (mTaskLoopRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
mThread = std::thread([this]() { runTaskLoop(); });
|
||||
|
||||
mTaskLoopRunning = true;
|
||||
}
|
||||
|
||||
void RemoteAccessService::maybeStopTaskLoop() {
|
||||
std::lock_guard<std::mutex> lockGuard(mStartStopTaskLoopLock);
|
||||
if (!mTaskLoopRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lockGuard(mLock);
|
||||
// Try to stop the reading stream.
|
||||
if (mGetRemoteTasksContext) {
|
||||
mGetRemoteTasksContext->TryCancel();
|
||||
mGetRemoteTasksContext.reset();
|
||||
}
|
||||
mTaskWaitStopped = true;
|
||||
mCv.notify_all();
|
||||
}
|
||||
if (mThread.joinable()) {
|
||||
mThread.join();
|
||||
}
|
||||
|
||||
mTaskLoopRunning = false;
|
||||
}
|
||||
|
||||
void RemoteAccessService::runTaskLoop() {
|
||||
GetRemoteTasksRequest request = {};
|
||||
std::unique_ptr<ClientReaderInterface<GetRemoteTasksResponse>> reader;
|
||||
while (true) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lockGuard(mLock);
|
||||
mGetRemoteTasksContext.reset(new ClientContext());
|
||||
reader = mGrpcStub->GetRemoteTasks(mGetRemoteTasksContext.get(), request);
|
||||
}
|
||||
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()) {
|
||||
ALOGE("Failed to call onRemoteTaskRequested callback, status: %d, message: %s",
|
||||
callbackStatus.getStatus(), callbackStatus.getMessage());
|
||||
}
|
||||
}
|
||||
Status status = reader->Finish();
|
||||
|
||||
ALOGE("GetRemoteTasks stream breaks, code: %d, message: %s, sleeping for 10s and retry",
|
||||
status.error_code(), status.error_message().c_str());
|
||||
// The long lasting connection should not return. But if the server returns, retry after
|
||||
// 10s.
|
||||
{
|
||||
std::unique_lock lk(mLock);
|
||||
if (mCv.wait_for(lk, std::chrono::milliseconds(mRetryWaitInMs), [this] {
|
||||
ScopedLockAssertion lockAssertion(mLock);
|
||||
return mTaskWaitStopped;
|
||||
})) {
|
||||
// If the stopped flag is set, we are quitting, exit the loop.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScopedAStatus RemoteAccessService::getDeviceId(std::string* deviceId) {
|
||||
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||
auto vhalClient = IVhalClient::tryCreate();
|
||||
if (vhalClient == nullptr) {
|
||||
ALOGE("Failed to connect to VHAL");
|
||||
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
|
||||
/*errorCode=*/0, "Failed to connect to VHAL to get device ID");
|
||||
}
|
||||
return getDeviceIdWithClient(*vhalClient.get(), deviceId);
|
||||
#else
|
||||
// Don't use VHAL client in fuzzing since IPC is not allowed.
|
||||
return ScopedAStatus::ok();
|
||||
#endif
|
||||
}
|
||||
|
||||
ScopedAStatus RemoteAccessService::getDeviceIdWithClient(IVhalClient& vhalClient,
|
||||
std::string* deviceId) {
|
||||
auto result = vhalClient.getValueSync(
|
||||
*vhalClient.createHalPropValue(toInt(VehicleProperty::INFO_VIN)));
|
||||
if (!result.ok()) {
|
||||
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
|
||||
/*errorCode=*/0,
|
||||
("failed to get INFO_VIN from VHAL: " + result.error().message()).c_str());
|
||||
}
|
||||
*deviceId = (*result)->getStringValue();
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus RemoteAccessService::getWakeupServiceName(std::string* wakeupServiceName) {
|
||||
*wakeupServiceName = WAKEUP_SERVICE_NAME;
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus RemoteAccessService::setRemoteTaskCallback(
|
||||
const std::shared_ptr<IRemoteTaskCallback>& callback) {
|
||||
std::lock_guard<std::mutex> lockGuard(mLock);
|
||||
mRemoteTaskCallback = callback;
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus RemoteAccessService::clearRemoteTaskCallback() {
|
||||
std::lock_guard<std::mutex> lockGuard(mLock);
|
||||
mRemoteTaskCallback.reset();
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus RemoteAccessService::notifyApStateChange(const ApState& newState) {
|
||||
ClientContext context;
|
||||
NotifyWakeupRequiredRequest request = {};
|
||||
request.set_iswakeuprequired(newState.isWakeupRequired);
|
||||
NotifyWakeupRequiredResponse response = {};
|
||||
Status status = mGrpcStub->NotifyWakeupRequired(&context, request, &response);
|
||||
if (!status.ok()) {
|
||||
return rpcStatusToScopedAStatus(status, "Failed to notify isWakeupRequired");
|
||||
}
|
||||
|
||||
if (newState.isReadyForRemoteTask) {
|
||||
maybeStartTaskLoop();
|
||||
} else {
|
||||
maybeStopTaskLoop();
|
||||
}
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
bool RemoteAccessService::checkDumpPermission() {
|
||||
uid_t uid = AIBinder_getCallingUid();
|
||||
return uid == AID_ROOT || uid == AID_SHELL || uid == AID_SYSTEM;
|
||||
}
|
||||
|
||||
void RemoteAccessService::dumpHelp(int fd) {
|
||||
dprintf(fd, "%s",
|
||||
(std::string("RemoteAccess HAL debug interface, Usage: \n") + COMMAND_SET_AP_STATE +
|
||||
" [0/1](isReadyForRemoteTask) [0/1](isWakeupRequired) Set the new AP state\n" +
|
||||
COMMAND_START_DEBUG_CALLBACK +
|
||||
" Start a debug callback that will record the received tasks\n" +
|
||||
COMMAND_STOP_DEBUG_CALLBACK + " Stop the debug callback\n" + COMMAND_SHOW_TASK +
|
||||
" Show tasks received by debug callback\n" + COMMAND_GET_DEVICE_ID +
|
||||
" Get device id\n")
|
||||
.c_str());
|
||||
}
|
||||
|
||||
binder_status_t RemoteAccessService::dump(int fd, const char** args, uint32_t numArgs) {
|
||||
if (!checkDumpPermission()) {
|
||||
dprintf(fd, "Caller must be root, system or shell\n");
|
||||
return STATUS_PERMISSION_DENIED;
|
||||
}
|
||||
|
||||
if (numArgs == 0) {
|
||||
dumpHelp(fd);
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
if (!strcmp(args[0], COMMAND_SET_AP_STATE)) {
|
||||
if (numArgs < 3) {
|
||||
dumpHelp(fd);
|
||||
return STATUS_OK;
|
||||
}
|
||||
ApState apState = {};
|
||||
const char* remoteTaskFlag = args[1];
|
||||
if (!strcmp(remoteTaskFlag, "1") && !strcmp(remoteTaskFlag, "0")) {
|
||||
dumpHelp(fd);
|
||||
return STATUS_OK;
|
||||
}
|
||||
if (!checkBoolFlag(args[1])) {
|
||||
dumpHelp(fd);
|
||||
return STATUS_OK;
|
||||
}
|
||||
if (!strcmp(args[1], "1")) {
|
||||
apState.isReadyForRemoteTask = true;
|
||||
}
|
||||
if (!checkBoolFlag(args[2])) {
|
||||
dumpHelp(fd);
|
||||
return STATUS_OK;
|
||||
}
|
||||
if (!strcmp(args[2], "1")) {
|
||||
apState.isWakeupRequired = true;
|
||||
}
|
||||
auto status = notifyApStateChange(apState);
|
||||
if (!status.isOk()) {
|
||||
dprintErrorStatus(fd, "Failed to set AP state", status);
|
||||
} else {
|
||||
dprintf(fd, "successfully set the new AP state\n");
|
||||
}
|
||||
} else if (!strcmp(args[0], COMMAND_START_DEBUG_CALLBACK)) {
|
||||
mDebugCallback = ndk::SharedRefBase::make<DebugRemoteTaskCallback>();
|
||||
setRemoteTaskCallback(mDebugCallback);
|
||||
dprintf(fd, "Debug callback registered\n");
|
||||
} else if (!strcmp(args[0], COMMAND_STOP_DEBUG_CALLBACK)) {
|
||||
if (mDebugCallback) {
|
||||
mDebugCallback.reset();
|
||||
}
|
||||
clearRemoteTaskCallback();
|
||||
dprintf(fd, "Debug callback unregistered\n");
|
||||
} else if (!strcmp(args[0], COMMAND_SHOW_TASK)) {
|
||||
if (mDebugCallback) {
|
||||
dprintf(fd, "%s", mDebugCallback->printTasks().c_str());
|
||||
} else {
|
||||
dprintf(fd, "Debug callback is not currently used, use \"%s\" first.\n",
|
||||
COMMAND_START_DEBUG_CALLBACK);
|
||||
}
|
||||
} else if (!strcmp(args[0], COMMAND_GET_DEVICE_ID)) {
|
||||
std::string deviceId;
|
||||
auto status = getDeviceId(&deviceId);
|
||||
if (!status.isOk()) {
|
||||
dprintErrorStatus(fd, "Failed to get device ID", status);
|
||||
} else {
|
||||
dprintf(fd, "Device Id: %s\n", deviceId.c_str());
|
||||
}
|
||||
} else {
|
||||
dumpHelp(fd);
|
||||
}
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
ScopedAStatus DebugRemoteTaskCallback::onRemoteTaskRequested(const std::string& clientId,
|
||||
const std::vector<uint8_t>& data) {
|
||||
std::lock_guard<std::mutex> lockGuard(mLock);
|
||||
mTasks.push_back({
|
||||
.clientId = clientId,
|
||||
.data = data,
|
||||
});
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
std::string DebugRemoteTaskCallback::printTasks() {
|
||||
std::lock_guard<std::mutex> lockGuard(mLock);
|
||||
std::string s = StringPrintf("Received %zu tasks in %f seconds", mTasks.size(),
|
||||
(android::uptimeMillis() - mStartTimeMillis) / 1000.);
|
||||
for (size_t i = 0; i < mTasks.size(); i++) {
|
||||
StringAppendF(&s, "Client Id: %s, Data: %s\n", mTasks[i].clientId.c_str(),
|
||||
printBytes(mTasks[i].data).c_str());
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace remoteaccess
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
47
automotive/remoteaccess/hal/default/test/Android.bp
Normal file
47
automotive/remoteaccess/hal/default/test/Android.bp
Normal file
|
@ -0,0 +1,47 @@
|
|||
// 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.
|
||||
|
||||
package {
|
||||
default_applicable_licenses: ["Android-Apache-2.0"],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "RemoteAccessServiceUnitTest",
|
||||
vendor: true,
|
||||
srcs: ["*.cpp"],
|
||||
whole_static_libs: [
|
||||
"RemoteAccessService",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
"liblog",
|
||||
"libutils",
|
||||
"libgrpc++",
|
||||
"libprotobuf-cpp-full",
|
||||
],
|
||||
// libgrpc++.so is installed as root, require root to access it.
|
||||
require_root: true,
|
||||
static_libs: [
|
||||
"libgtest",
|
||||
"libgmock",
|
||||
],
|
||||
defaults: [
|
||||
"vhalclient_defaults",
|
||||
],
|
||||
cflags: [
|
||||
"-Wno-unused-parameter",
|
||||
],
|
||||
test_suites: ["device-tests"],
|
||||
}
|
|
@ -0,0 +1,375 @@
|
|||
/*
|
||||
* 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 "RemoteAccessService.h"
|
||||
|
||||
#include <AidlHalPropValue.h>
|
||||
#include <IVhalClient.h>
|
||||
#include <aidl/android/hardware/automotive/remoteaccess/ApState.h>
|
||||
#include <aidl/android/hardware/automotive/remoteaccess/BnRemoteTaskCallback.h>
|
||||
#include <aidl/android/hardware/automotive/vehicle/VehiclePropValue.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <grpcpp/test/mock_stream.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <wakeup_client.grpc.pb.h>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace remoteaccess {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::android::base::ScopedLockAssertion;
|
||||
using ::android::frameworks::automotive::vhal::AidlHalPropValue;
|
||||
using ::android::frameworks::automotive::vhal::IHalPropConfig;
|
||||
using ::android::frameworks::automotive::vhal::IHalPropValue;
|
||||
using ::android::frameworks::automotive::vhal::ISubscriptionCallback;
|
||||
using ::android::frameworks::automotive::vhal::ISubscriptionClient;
|
||||
using ::android::frameworks::automotive::vhal::IVhalClient;
|
||||
|
||||
using ::aidl::android::hardware::automotive::remoteaccess::ApState;
|
||||
using ::aidl::android::hardware::automotive::remoteaccess::BnRemoteTaskCallback;
|
||||
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
|
||||
|
||||
using ::grpc::ClientAsyncReaderInterface;
|
||||
using ::grpc::ClientAsyncResponseReaderInterface;
|
||||
using ::grpc::ClientContext;
|
||||
using ::grpc::ClientReader;
|
||||
using ::grpc::ClientReaderInterface;
|
||||
using ::grpc::CompletionQueue;
|
||||
using ::grpc::Status;
|
||||
using ::grpc::testing::MockClientReader;
|
||||
using ::ndk::ScopedAStatus;
|
||||
using ::testing::_;
|
||||
using ::testing::DoAll;
|
||||
using ::testing::Return;
|
||||
using ::testing::SetArgPointee;
|
||||
|
||||
constexpr char kTestVin[] = "test_VIN";
|
||||
|
||||
} // namespace
|
||||
|
||||
class MockGrpcClientStub : public WakeupClient::StubInterface {
|
||||
public:
|
||||
MOCK_METHOD(ClientReaderInterface<GetRemoteTasksResponse>*, GetRemoteTasksRaw,
|
||||
(ClientContext * context, const GetRemoteTasksRequest& request));
|
||||
MOCK_METHOD(Status, NotifyWakeupRequired,
|
||||
(ClientContext * context, const NotifyWakeupRequiredRequest& request,
|
||||
NotifyWakeupRequiredResponse* response));
|
||||
// Async methods which we do not care.
|
||||
MOCK_METHOD(ClientAsyncReaderInterface<GetRemoteTasksResponse>*, AsyncGetRemoteTasksRaw,
|
||||
(ClientContext * context, const GetRemoteTasksRequest& request, CompletionQueue* cq,
|
||||
void* tag));
|
||||
MOCK_METHOD(ClientAsyncReaderInterface<GetRemoteTasksResponse>*, PrepareAsyncGetRemoteTasksRaw,
|
||||
(ClientContext * context, const GetRemoteTasksRequest& request,
|
||||
CompletionQueue* cq));
|
||||
MOCK_METHOD(ClientAsyncResponseReaderInterface<NotifyWakeupRequiredResponse>*,
|
||||
AsyncNotifyWakeupRequiredRaw,
|
||||
(ClientContext * context, const NotifyWakeupRequiredRequest& request,
|
||||
CompletionQueue* cq));
|
||||
MOCK_METHOD(ClientAsyncResponseReaderInterface<NotifyWakeupRequiredResponse>*,
|
||||
PrepareAsyncNotifyWakeupRequiredRaw,
|
||||
(ClientContext * context, const NotifyWakeupRequiredRequest& request,
|
||||
CompletionQueue* cq));
|
||||
};
|
||||
|
||||
class FakeVhalClient final : public android::frameworks::automotive::vhal::IVhalClient {
|
||||
public:
|
||||
template <class T>
|
||||
using VhalClientResult = android::hardware::automotive::vehicle::VhalResult<T>;
|
||||
|
||||
inline bool isAidlVhal() { return true; }
|
||||
|
||||
VhalClientResult<std::unique_ptr<IHalPropValue>> getValueSync(
|
||||
const IHalPropValue& requestValue) override {
|
||||
auto propValue = std::make_unique<AidlHalPropValue>(requestValue.getPropId());
|
||||
propValue->setStringValue(kTestVin);
|
||||
return propValue;
|
||||
}
|
||||
|
||||
std::unique_ptr<IHalPropValue> createHalPropValue(int32_t propId) override {
|
||||
return std::make_unique<AidlHalPropValue>(propId);
|
||||
}
|
||||
|
||||
// Functions we do not care.
|
||||
std::unique_ptr<IHalPropValue> createHalPropValue([[maybe_unused]] int32_t propId,
|
||||
[[maybe_unused]] int32_t areaId) override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void getValue([[maybe_unused]] const IHalPropValue& requestValue,
|
||||
[[maybe_unused]] std::shared_ptr<GetValueCallbackFunc> callback) override {}
|
||||
|
||||
void setValue([[maybe_unused]] const IHalPropValue& requestValue,
|
||||
[[maybe_unused]] std::shared_ptr<SetValueCallbackFunc> callback) override {}
|
||||
|
||||
VhalClientResult<void> setValueSync([[maybe_unused]] const IHalPropValue& requestValue) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VhalClientResult<void> addOnBinderDiedCallback(
|
||||
[[maybe_unused]] std::shared_ptr<OnBinderDiedCallbackFunc> callback) override {
|
||||
return {};
|
||||
}
|
||||
|
||||
VhalClientResult<void> removeOnBinderDiedCallback(
|
||||
[[maybe_unused]] std::shared_ptr<OnBinderDiedCallbackFunc> callback) override {
|
||||
return {};
|
||||
}
|
||||
|
||||
VhalClientResult<std::vector<std::unique_ptr<IHalPropConfig>>> getAllPropConfigs() override {
|
||||
return std::vector<std::unique_ptr<IHalPropConfig>>();
|
||||
}
|
||||
|
||||
VhalClientResult<std::vector<std::unique_ptr<IHalPropConfig>>> getPropConfigs(
|
||||
[[maybe_unused]] std::vector<int32_t> propIds) override {
|
||||
return std::vector<std::unique_ptr<IHalPropConfig>>();
|
||||
}
|
||||
|
||||
std::unique_ptr<ISubscriptionClient> getSubscriptionClient(
|
||||
[[maybe_unused]] std::shared_ptr<ISubscriptionCallback> callback) override {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class FakeRemoteTaskCallback : public BnRemoteTaskCallback {
|
||||
public:
|
||||
ScopedAStatus onRemoteTaskRequested(const std::string& clientId,
|
||||
const std::vector<uint8_t>& data) override {
|
||||
std::lock_guard<std::mutex> lockGuard(mLock);
|
||||
mDataByClientId[clientId] = data;
|
||||
mTaskCount++;
|
||||
mCv.notify_all();
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> getData(const std::string& clientId) { return mDataByClientId[clientId]; }
|
||||
|
||||
bool wait(size_t taskCount, size_t timeoutInSec) {
|
||||
std::unique_lock<std::mutex> lock(mLock);
|
||||
return mCv.wait_for(lock, std::chrono::seconds(timeoutInSec), [taskCount, this] {
|
||||
ScopedLockAssertion lockAssertion(mLock);
|
||||
return mTaskCount >= taskCount;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mLock;
|
||||
std::unordered_map<std::string, std::vector<uint8_t>> mDataByClientId GUARDED_BY(mLock);
|
||||
size_t mTaskCount GUARDED_BY(mLock) = 0;
|
||||
std::condition_variable mCv;
|
||||
};
|
||||
|
||||
class RemoteAccessServiceUnitTest : public ::testing::Test {
|
||||
public:
|
||||
virtual void SetUp() override {
|
||||
mGrpcWakeupClientStub = std::make_unique<MockGrpcClientStub>();
|
||||
mService = ndk::SharedRefBase::make<RemoteAccessService>(mGrpcWakeupClientStub.get());
|
||||
}
|
||||
|
||||
MockGrpcClientStub* getGrpcWakeupClientStub() { return mGrpcWakeupClientStub.get(); }
|
||||
|
||||
RemoteAccessService* getService() { return mService.get(); }
|
||||
|
||||
void setRetryWaitInMs(size_t retryWaitInMs) { mService->setRetryWaitInMs(retryWaitInMs); }
|
||||
|
||||
ScopedAStatus getDeviceIdWithClient(IVhalClient& vhalClient, std::string* deviceId) {
|
||||
return mService->getDeviceIdWithClient(vhalClient, deviceId);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<MockGrpcClientStub> mGrpcWakeupClientStub;
|
||||
std::shared_ptr<RemoteAccessService> mService;
|
||||
};
|
||||
|
||||
TEST_F(RemoteAccessServiceUnitTest, TestGetWakeupServiceName) {
|
||||
std::string serviceName;
|
||||
|
||||
ScopedAStatus status = getService()->getWakeupServiceName(&serviceName);
|
||||
|
||||
EXPECT_TRUE(status.isOk());
|
||||
EXPECT_EQ(serviceName, "com.google.vehicle.wakeup");
|
||||
}
|
||||
|
||||
TEST_F(RemoteAccessServiceUnitTest, TestNotifyApStateChangeWakeupRequired) {
|
||||
bool isWakeupRequired = false;
|
||||
EXPECT_CALL(*getGrpcWakeupClientStub(), NotifyWakeupRequired)
|
||||
.WillOnce([&isWakeupRequired]([[maybe_unused]] ClientContext* context,
|
||||
const NotifyWakeupRequiredRequest& request,
|
||||
[[maybe_unused]] NotifyWakeupRequiredResponse* response) {
|
||||
isWakeupRequired = request.iswakeuprequired();
|
||||
return Status();
|
||||
});
|
||||
|
||||
ApState newState = {
|
||||
.isWakeupRequired = true,
|
||||
};
|
||||
ScopedAStatus status = getService()->notifyApStateChange(newState);
|
||||
|
||||
EXPECT_TRUE(status.isOk());
|
||||
EXPECT_TRUE(isWakeupRequired);
|
||||
}
|
||||
|
||||
TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasks) {
|
||||
GetRemoteTasksResponse response1;
|
||||
std::vector<uint8_t> testData = {0xde, 0xad, 0xbe, 0xef};
|
||||
response1.set_clientid("1");
|
||||
response1.set_data(testData.data(), testData.size());
|
||||
GetRemoteTasksResponse response2;
|
||||
response2.set_clientid("2");
|
||||
std::shared_ptr<FakeRemoteTaskCallback> callback =
|
||||
ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
|
||||
|
||||
ON_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw)
|
||||
.WillByDefault(
|
||||
[response1, response2]([[maybe_unused]] ClientContext* context,
|
||||
[[maybe_unused]] const GetRemoteTasksRequest& request) {
|
||||
// mockReader ownership will be transferred to the client so we don't own it
|
||||
// here.
|
||||
MockClientReader<GetRemoteTasksResponse>* mockClientReader =
|
||||
new MockClientReader<GetRemoteTasksResponse>();
|
||||
EXPECT_CALL(*mockClientReader, Finish()).WillOnce(Return(Status::OK));
|
||||
EXPECT_CALL(*mockClientReader, Read(_))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(response1), Return(true)))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(response2), Return(true)))
|
||||
.WillRepeatedly(Return(false));
|
||||
return mockClientReader;
|
||||
});
|
||||
|
||||
getService()->setRemoteTaskCallback(callback);
|
||||
// Start the long live connection to receive tasks.
|
||||
ApState newState = {
|
||||
.isReadyForRemoteTask = true,
|
||||
};
|
||||
ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk());
|
||||
|
||||
ASSERT_TRUE(callback->wait(/*taskCount=*/2, /*timeoutInSec=*/10))
|
||||
<< "Did not receive enough tasks";
|
||||
EXPECT_EQ(callback->getData("1"), testData);
|
||||
EXPECT_EQ(callback->getData("2"), std::vector<uint8_t>());
|
||||
}
|
||||
|
||||
TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasksRetryConnection) {
|
||||
GetRemoteTasksResponse response;
|
||||
std::shared_ptr<FakeRemoteTaskCallback> callback =
|
||||
ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
|
||||
|
||||
ON_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw)
|
||||
.WillByDefault([response]([[maybe_unused]] ClientContext* context,
|
||||
[[maybe_unused]] const GetRemoteTasksRequest& request) {
|
||||
// mockReader ownership will be transferred to the client so we don't own it here.
|
||||
MockClientReader<GetRemoteTasksResponse>* mockClientReader =
|
||||
new MockClientReader<GetRemoteTasksResponse>();
|
||||
EXPECT_CALL(*mockClientReader, Finish()).WillOnce(Return(Status::OK));
|
||||
// Connection fails after receiving one task. Should retry after some time.
|
||||
EXPECT_CALL(*mockClientReader, Read(_))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(response), Return(true)))
|
||||
.WillRepeatedly(Return(false));
|
||||
return mockClientReader;
|
||||
});
|
||||
|
||||
getService()->setRemoteTaskCallback(callback);
|
||||
setRetryWaitInMs(100);
|
||||
// Start the long live connection to receive tasks.
|
||||
ApState newState = {
|
||||
.isReadyForRemoteTask = true,
|
||||
};
|
||||
ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk());
|
||||
|
||||
ASSERT_TRUE(callback->wait(/*taskCount=*/2, /*timeoutInSec=*/10))
|
||||
<< "Did not receive enough tasks";
|
||||
}
|
||||
|
||||
TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasksDefaultNotReady) {
|
||||
GetRemoteTasksResponse response1;
|
||||
std::vector<uint8_t> testData = {0xde, 0xad, 0xbe, 0xef};
|
||||
response1.set_clientid("1");
|
||||
response1.set_data(testData.data(), testData.size());
|
||||
GetRemoteTasksResponse response2;
|
||||
response2.set_clientid("2");
|
||||
std::shared_ptr<FakeRemoteTaskCallback> callback =
|
||||
ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
|
||||
|
||||
EXPECT_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw).Times(0);
|
||||
|
||||
// Default state is not ready for remote tasks, so no callback will be called.
|
||||
getService()->setRemoteTaskCallback(callback);
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
TEST_F(RemoteAccessServiceUnitTest, TestGetRemoteTasksNotReadyAfterReady) {
|
||||
GetRemoteTasksResponse response1;
|
||||
std::vector<uint8_t> testData = {0xde, 0xad, 0xbe, 0xef};
|
||||
response1.set_clientid("1");
|
||||
response1.set_data(testData.data(), testData.size());
|
||||
GetRemoteTasksResponse response2;
|
||||
response2.set_clientid("2");
|
||||
std::shared_ptr<FakeRemoteTaskCallback> callback =
|
||||
ndk::SharedRefBase::make<FakeRemoteTaskCallback>();
|
||||
|
||||
ON_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw)
|
||||
.WillByDefault(
|
||||
[response1, response2]([[maybe_unused]] ClientContext* context,
|
||||
[[maybe_unused]] const GetRemoteTasksRequest& request) {
|
||||
// mockReader ownership will be transferred to the client so we don't own it
|
||||
// here.
|
||||
MockClientReader<GetRemoteTasksResponse>* mockClientReader =
|
||||
new MockClientReader<GetRemoteTasksResponse>();
|
||||
EXPECT_CALL(*mockClientReader, Finish()).WillOnce(Return(Status::OK));
|
||||
EXPECT_CALL(*mockClientReader, Read(_))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(response1), Return(true)))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(response2), Return(true)))
|
||||
.WillRepeatedly(Return(false));
|
||||
return mockClientReader;
|
||||
});
|
||||
// Should only be called once when is is ready for remote task.
|
||||
EXPECT_CALL(*getGrpcWakeupClientStub(), GetRemoteTasksRaw).Times(1);
|
||||
|
||||
getService()->setRemoteTaskCallback(callback);
|
||||
setRetryWaitInMs(100);
|
||||
// Start the long live connection to receive tasks.
|
||||
ApState newState = {
|
||||
.isReadyForRemoteTask = true,
|
||||
};
|
||||
ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk());
|
||||
ASSERT_TRUE(callback->wait(/*taskCount=*/2, /*timeoutInSec=*/10))
|
||||
<< "Did not receive enough tasks";
|
||||
|
||||
// Stop the long live connection.
|
||||
newState.isReadyForRemoteTask = false;
|
||||
ASSERT_TRUE(getService()->notifyApStateChange(newState).isOk());
|
||||
|
||||
// Wait for the retry delay, but the loop should already exit.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(150));
|
||||
}
|
||||
|
||||
TEST_F(RemoteAccessServiceUnitTest, testGetDeviceId) {
|
||||
std::string deviceId;
|
||||
|
||||
FakeVhalClient vhalClient;
|
||||
|
||||
ASSERT_TRUE(getDeviceIdWithClient(vhalClient, &deviceId).isOk());
|
||||
ASSERT_EQ(deviceId, kTestVin);
|
||||
}
|
||||
|
||||
} // namespace remoteaccess
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
282
automotive/remoteaccess/test_grpc_server/README.md
Normal file
282
automotive/remoteaccess/test_grpc_server/README.md
Normal file
|
@ -0,0 +1,282 @@
|
|||
# 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. The
|
||||
test server does not communicate with any actual network server. It has the
|
||||
following behavior:
|
||||
|
||||
* It starts a GRPC server on 'DGRPC_SERVICE_ADDRESS' compile flag which is
|
||||
localhost:50051. The GRPC server provides the service according to
|
||||
hardware/interfaces/automotive/remoteaccess/hal/default/proto/wakeup_client.proto.
|
||||
|
||||
In real implementation, DGRPC_SERVICE_ADDRESS can be specified to any IP
|
||||
address where the TCU can be exposed to Application Processor. The default
|
||||
remote access HAL implementation
|
||||
(hardware/interfaces/automotive/remoteaccess/hal/default/Android.bp) also
|
||||
uses DGRPC_SERVICE_ADDRESS to find this GRPC server, so it must have the
|
||||
same IP address.
|
||||
|
||||
* It generates a fake task using FakeTaskGenerator every 'kTaskIntervalInMs' ms.
|
||||
|
||||
In real implementation, it should receive task from the remote server.
|
||||
|
||||
* Each fake task has an increasing unique client ID. The task data is always
|
||||
what's defined for 'DATA' variable.
|
||||
|
||||
In real implementation, the client ID and task data should come from the
|
||||
remote server.
|
||||
|
||||
* The generated tasks are put into a task queue which is a priority queue sorted
|
||||
by task received time.
|
||||
|
||||
In real implementation, if the server provides a task timestamp, then this
|
||||
queue can be sorted by that task timestamp instead.
|
||||
|
||||
* When the Application processor is started, the remote access HAL running on
|
||||
Android will call 'GetRemoteTasks' to establish a long-live connection. This
|
||||
connection is used to deliver all task data from remote wakeup client to
|
||||
remote access HAL, which eventually to car service and applications.
|
||||
|
||||
When the 'GetRemoteTasks' is called, the wakeup client must send all the
|
||||
pending tasks through the 'ServerWriter'. If no task is pending, then it must
|
||||
block and wait for a new task to arrive.
|
||||
|
||||
If one task data fails to be sent through the channel, it likely means
|
||||
the other side (Application processor) is shutting down or has closed the
|
||||
channel. The wakeup client must put the task back to the pending queue and
|
||||
wait for a new 'GetRemoteTasks' request to retry sending the task.
|
||||
|
||||
* When a new task arrives, if 'WakeupRequired' is true, then try to wakeup
|
||||
the Application Processor by sending a specific CAN message. It is possible that
|
||||
the waking up is already in progress. This is okay since Vehicle Processor
|
||||
should ignore wakeup message if a wakeup is already in progress.
|
||||
|
||||
* When 'WakeupRequired' is updated from false to true, if there are unexpired
|
||||
pending tasks in the task queue, try to wakeup Application Processor.
|
||||
|
||||
This is to handle the situation when a task arrives while the device is
|
||||
shutting down. During the device shutdown, the channel to deliver the remote
|
||||
tasks to Application Processor is shutdown so the new task will be added to the
|
||||
task queue. 'WakeupRequired' will be set to false to prevent the wakeup
|
||||
message preventing the shutdown. After the shutdown is complete,
|
||||
'WakeupRequired' will be set to true and this wakeup client must try to wake
|
||||
up the device again to execute the pending tasks.
|
||||
|
||||
* Every pending task has a timeout: 'KTaskTimeoutInMs'. If the pending task
|
||||
is not delivered to remote access HAL before the timeout (through
|
||||
GetRemoteTasks), the task timed out and a warning message is logged.
|
||||
|
||||
In real implementation, this kTaskTimeoutInMs has to be set long enough to
|
||||
allow an Android bootup to happen. 20s is a reasonable value. When a task
|
||||
timed out, the wakeup client should also report to remote task server about
|
||||
the task timeout failure.
|
||||
|
||||
## How to build the test wakeup client
|
||||
|
||||
* Under android root: `make -j TestWakeupClientServer`
|
||||
|
||||
## How to push the test wakeup client to a TCU which runs Android.
|
||||
|
||||
* Make the target device writable:
|
||||
|
||||
`adb root`
|
||||
|
||||
`adb remount`
|
||||
|
||||
`adb reboot`
|
||||
|
||||
`adb root`
|
||||
|
||||
`adb remount`
|
||||
|
||||
* Under android root: `cd $ANDROID_PRODUCT_OUT`
|
||||
|
||||
* `adb push vendor/bin/TestWakeupClientServer /vendor/bin`
|
||||
|
||||
* `adb shell`
|
||||
|
||||
* `su`
|
||||
|
||||
* `/vendor/bin/TestWakeupClientServer`
|
||||
|
||||
## How to build and test the test wakeup client using one car emulator.
|
||||
|
||||
In this test setup we will use one google car emulator
|
||||
(sdk_car_x86_64-userdebug). We assume both the TCU and the remote access HAL
|
||||
runs on the same Android system, and they communicate through local loopback
|
||||
interface.
|
||||
|
||||
* Under android root, `source build/envsetup.sh`
|
||||
|
||||
* `lunch sdk_car_x86_64-userdebug`
|
||||
|
||||
* `m -j`
|
||||
|
||||
* Run the emulator, the '-read-only' flag is required to run multiple instances:
|
||||
|
||||
`emulator -writable-system -read-only`
|
||||
|
||||
* The android lunch target: sdk_car_x86_64-userdebug and
|
||||
cf_x86_64_auto-userdebug already contains the default remote access HAL. For
|
||||
other lunch target, you can add the default remote access HAL by adding
|
||||
'android.hardware.automotive.remoteaccess@V1-default-service' to
|
||||
'PRODUCT_PACKAGES' variable in mk file, see `device/generic/car/common/car.mk`
|
||||
as example.
|
||||
|
||||
To verify whether remote access HAL is running, you can use the following
|
||||
command to check:
|
||||
|
||||
`dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default`
|
||||
|
||||
* Make the target device writable:
|
||||
|
||||
`adb root`
|
||||
|
||||
`adb remount`
|
||||
|
||||
`adb reboot`
|
||||
|
||||
`adb root`
|
||||
|
||||
`adb remount`
|
||||
|
||||
* `make -j TestWakeupClientServer`
|
||||
|
||||
* `adb push $ANDROID_PRODUCT_OUT/vendor/bin/TestWakeupClientServer /vendor/bin`
|
||||
|
||||
* `adb shell`
|
||||
|
||||
* `su`
|
||||
|
||||
* `/vendor/bin/TestWakeupClientServer`
|
||||
|
||||
* Remote access HAL should start by default when the car emulator starts. Now
|
||||
the test wake up client should also be running and generating fake tasks.
|
||||
|
||||
Start a new adb shell session by
|
||||
|
||||
`adb shell`
|
||||
|
||||
`su`
|
||||
|
||||
* Issue the command to start a simple debug callback that will capture all the
|
||||
received tasks at the remote access HAL side:
|
||||
|
||||
`dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default --start-debug-callback`
|
||||
|
||||
* Issue the following debug command to remote access HAL to establish the
|
||||
communication channel between it and the test wakeup client. This command
|
||||
also notifies that wakeup is not required:
|
||||
|
||||
`dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default --set-ap-state 1 0`
|
||||
|
||||
* Wait for a while, issue the following command to show the received fake tasks:
|
||||
|
||||
`dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default --show-task`
|
||||
|
||||
You should expect to see some received tasks printed out.
|
||||
|
||||
* Simulate the Application Processor is shutting down by issuing the following
|
||||
command:
|
||||
|
||||
`dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default --set-ap-state 0 0`
|
||||
|
||||
* Wait for a while, issue the following command to show received tasks again:
|
||||
|
||||
`dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default --show-task`
|
||||
|
||||
You should expect to see no new tasks received since remote access HAL already
|
||||
closed the communication channel.
|
||||
|
||||
* Simulate the Application Processor is already shutdown and wake up is required
|
||||
now:
|
||||
|
||||
`dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default --set-ap-state 0 1`
|
||||
|
||||
Now you should expect to see the test wakeup client printing out messages
|
||||
that it is trying to wake up application processor.
|
||||
|
||||
* Simulate the Application Processor is waken up:
|
||||
|
||||
`dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default --set-ap-state 1 0`
|
||||
|
||||
* A new communication channel should have been established and all pending
|
||||
non-expired tasks should be delivered to the remote access HAL.
|
||||
|
||||
`dumpsys android.hardware.automotive.remoteaccess.IRemoteAccess/default --show-task`
|
||||
|
||||
* Now you can issue `ctrl c` on the first adb shell to stop the test wakeup
|
||||
client.
|
||||
|
||||
## How to build and test the test wakeup client using two car emulators.
|
||||
|
||||
In this test case, we are going to use two car emulators, one as the
|
||||
Application Processor, one as the TCU.
|
||||
|
||||
* Change the IP address to allow IP communication between different emulator
|
||||
instances. For detail about why we change it this way, see [interconnecting
|
||||
emulator instance](https://developer.android.com/studio/run/emulator-networking#connecting).
|
||||
|
||||
Change 'DGRPC_SERVICE_ADDRESS' in `test_grpc_server/Android.bp` to
|
||||
`10.0.2.15:50051`.
|
||||
|
||||
Change `DGRPC_SERVICE_ADDRESS` in 'hal/defaut/Android.bp' to
|
||||
`10.0.2.2:50051`.
|
||||
|
||||
* Under android root: `source build/envsetup.sh`
|
||||
|
||||
* `lunch sdk_car_x86_64-userdebug`
|
||||
|
||||
* `m -j`
|
||||
|
||||
* Start one car emulator as TCU
|
||||
|
||||
`emulator -writable-system -read-only`
|
||||
|
||||
* Start a new shell session. Connect to the emulator's console,
|
||||
see [Start and stop a console session](https://developer.android.com/studio/run/emulator-console#console-session)
|
||||
for detail.
|
||||
|
||||
`telnet localhost 5554`
|
||||
|
||||
* `auth auth_token` where auth_token must match the contents of the
|
||||
`.emulator_console_auth_token` file.
|
||||
|
||||
* `redir add tcp:50051:50051`
|
||||
|
||||
* Exit the telnet session
|
||||
|
||||
Make the target device writable:
|
||||
|
||||
`adb root`
|
||||
|
||||
`adb remount`
|
||||
|
||||
`adb reboot`
|
||||
|
||||
`adb root`
|
||||
|
||||
`adb remount`
|
||||
|
||||
* `make -j TestWakeupClientServer`
|
||||
|
||||
* `adb push $ANDROID_PRODUCT_OUT/vendor/bin/TestWakeupClientServer /vendor/bin`
|
||||
|
||||
* `adb shell`
|
||||
|
||||
* `su`
|
||||
|
||||
* `/vendor/bin/TestWakeupClientServer`
|
||||
|
||||
* Start a new shell, start another car emulator as the Application Processor:
|
||||
|
||||
`emulator -writable-system -read-only`
|
||||
|
||||
* Connect to adb shell for the application processor:
|
||||
|
||||
`adb -s emulator-5556 shell`
|
||||
|
||||
`su`
|
||||
|
||||
* Follow the test instructions for one car emulator using the 'dumpsys'
|
||||
commands.
|
42
automotive/remoteaccess/test_grpc_server/impl/Android.bp
Normal file
42
automotive/remoteaccess/test_grpc_server/impl/Android.bp
Normal file
|
@ -0,0 +1,42 @@
|
|||
// 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.
|
||||
|
||||
package {
|
||||
// See: http://go/android-license-faq
|
||||
// A large-scale-change added 'default_applicable_licenses' to import
|
||||
// all of the 'license_kinds' from "hardware_interfaces_license"
|
||||
// to get the below license kinds:
|
||||
// SPDX-license-identifier-Apache-2.0
|
||||
default_applicable_licenses: ["hardware_interfaces_license"],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "TestWakeupClientServer",
|
||||
vendor: true,
|
||||
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=\"localhost:50051\"",
|
||||
],
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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 <utils/Looper.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};
|
||||
};
|
||||
|
||||
struct TaskInfo {
|
||||
// This is unique per-task. Note that a task might be popped and put back into the task queue,
|
||||
// it will have a new task ID but the same clientId in the task data.
|
||||
int taskId;
|
||||
int64_t timestampInMs;
|
||||
GetRemoteTasksResponse taskData;
|
||||
};
|
||||
|
||||
struct TaskInfoComparator {
|
||||
// We want the smallest timestamp and smallest task ID on top.
|
||||
bool operator()(const TaskInfo& l, const TaskInfo& r) {
|
||||
return l.timestampInMs > r.timestampInMs ||
|
||||
(l.timestampInMs == r.timestampInMs && l.taskId > r.taskId);
|
||||
}
|
||||
};
|
||||
|
||||
// forward-declaration.
|
||||
class TaskQueue;
|
||||
|
||||
class TaskTimeoutMessageHandler final : public android::MessageHandler {
|
||||
public:
|
||||
TaskTimeoutMessageHandler(TaskQueue* taskQueue);
|
||||
void handleMessage(const android::Message& message) override;
|
||||
|
||||
private:
|
||||
TaskQueue* mTaskQueue;
|
||||
};
|
||||
|
||||
// TaskQueue is thread-safe.
|
||||
class TaskQueue final {
|
||||
public:
|
||||
TaskQueue();
|
||||
~TaskQueue();
|
||||
|
||||
void add(const GetRemoteTasksResponse& response);
|
||||
std::optional<GetRemoteTasksResponse> maybePopOne();
|
||||
void waitForTask();
|
||||
void stopWait();
|
||||
void handleTaskTimeout();
|
||||
bool isEmpty();
|
||||
|
||||
private:
|
||||
std::thread mCheckTaskTimeoutThread;
|
||||
std::mutex mLock;
|
||||
std::priority_queue<TaskInfo, std::vector<TaskInfo>, TaskInfoComparator> mTasks
|
||||
GUARDED_BY(mLock);
|
||||
// A variable to notify mTasks is not empty.
|
||||
std::condition_variable mTasksNotEmptyCv;
|
||||
bool mStopped GUARDED_BY(mLock);
|
||||
android::sp<Looper> mLooper;
|
||||
android::sp<TaskTimeoutMessageHandler> mTaskTimeoutMessageHandler;
|
||||
std::atomic<int> mTaskIdCounter = 0;
|
||||
|
||||
void checkForTestTimeoutLoop();
|
||||
void waitForTaskWithLock(std::unique_lock<std::mutex>& lock);
|
||||
};
|
||||
|
||||
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;
|
||||
// Whether wakeup AP is required for executing tasks.
|
||||
std::atomic<bool> mWakeupRequired = false;
|
||||
std::mutex mLock;
|
||||
bool mServerStopped GUARDED_BY(mLock);
|
||||
|
||||
// Thread-safe. For test impl only.
|
||||
FakeTaskGenerator mFakeTaskGenerator;
|
||||
// Thread-sfae.
|
||||
TaskQueue mTaskQueue;
|
||||
|
||||
void fakeTaskGenerateLoop();
|
||||
|
||||
void wakeupApplicationProcessor();
|
||||
};
|
||||
|
||||
} // namespace remoteaccess
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
* 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 <inttypes.h>
|
||||
#include <utils/Looper.h>
|
||||
#include <utils/SystemClock.h>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace remoteaccess {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::android::uptimeMillis;
|
||||
using ::android::base::ScopedLockAssertion;
|
||||
using ::android::base::StringPrintf;
|
||||
using ::grpc::ServerContext;
|
||||
using ::grpc::ServerWriter;
|
||||
using ::grpc::Status;
|
||||
|
||||
constexpr int kTaskIntervalInMs = 5'000;
|
||||
constexpr int64_t KTaskTimeoutInMs = 20'000;
|
||||
|
||||
} // 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;
|
||||
}
|
||||
|
||||
TaskTimeoutMessageHandler::TaskTimeoutMessageHandler(TaskQueue* taskQueue)
|
||||
: mTaskQueue(taskQueue) {}
|
||||
|
||||
void TaskTimeoutMessageHandler::handleMessage(const android::Message& message) {
|
||||
mTaskQueue->handleTaskTimeout();
|
||||
}
|
||||
|
||||
TaskQueue::TaskQueue() {
|
||||
mTaskTimeoutMessageHandler = android::sp<TaskTimeoutMessageHandler>::make(this);
|
||||
mLooper = Looper::prepare(/*opts=*/0);
|
||||
mCheckTaskTimeoutThread = std::thread([this] { checkForTestTimeoutLoop(); });
|
||||
}
|
||||
|
||||
TaskQueue::~TaskQueue() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lockGuard(mLock);
|
||||
mStopped = true;
|
||||
}
|
||||
while (true) {
|
||||
// Remove all pending timeout handlers from queue.
|
||||
if (!maybePopOne().has_value()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mCheckTaskTimeoutThread.joinable()) {
|
||||
mCheckTaskTimeoutThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<GetRemoteTasksResponse> TaskQueue::maybePopOne() {
|
||||
std::lock_guard<std::mutex> lockGuard(mLock);
|
||||
if (mTasks.size() == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
TaskInfo response = std::move(mTasks.top());
|
||||
mTasks.pop();
|
||||
mLooper->removeMessages(mTaskTimeoutMessageHandler, response.taskId);
|
||||
return std::move(response.taskData);
|
||||
}
|
||||
|
||||
void TaskQueue::add(const GetRemoteTasksResponse& task) {
|
||||
std::lock_guard<std::mutex> lockGuard(mLock);
|
||||
if (mStopped) {
|
||||
return;
|
||||
}
|
||||
int taskId = mTaskIdCounter++;
|
||||
mTasks.push(TaskInfo{
|
||||
.taskId = taskId,
|
||||
.timestampInMs = uptimeMillis(),
|
||||
.taskData = task,
|
||||
});
|
||||
android::Message message(taskId);
|
||||
mLooper->sendMessageDelayed(KTaskTimeoutInMs * 1000, mTaskTimeoutMessageHandler, message);
|
||||
mTasksNotEmptyCv.notify_all();
|
||||
}
|
||||
|
||||
void TaskQueue::waitForTask() {
|
||||
std::unique_lock<std::mutex> lock(mLock);
|
||||
waitForTaskWithLock(lock);
|
||||
}
|
||||
|
||||
void TaskQueue::waitForTaskWithLock(std::unique_lock<std::mutex>& lock) {
|
||||
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();
|
||||
}
|
||||
|
||||
bool TaskQueue::isEmpty() {
|
||||
std::lock_guard<std::mutex> lockGuard(mLock);
|
||||
return mTasks.size() == 0 || mStopped;
|
||||
}
|
||||
|
||||
void TaskQueue::checkForTestTimeoutLoop() {
|
||||
Looper::setForThread(mLooper);
|
||||
|
||||
while (true) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mLock);
|
||||
if (mStopped) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mLooper->pollAll(/*timeoutMillis=*/-1);
|
||||
}
|
||||
}
|
||||
|
||||
void TaskQueue::handleTaskTimeout() {
|
||||
// We know which task timed-out from the taskId in the message. However, there is no easy way
|
||||
// to remove a specific task with the task ID from the priority_queue, so we just check from
|
||||
// the top of the queue (which have the oldest tasks).
|
||||
std::lock_guard<std::mutex> lockGuard(mLock);
|
||||
int64_t now = uptimeMillis();
|
||||
while (mTasks.size() > 0) {
|
||||
const TaskInfo& taskInfo = mTasks.top();
|
||||
if (taskInfo.timestampInMs + KTaskTimeoutInMs > now) {
|
||||
break;
|
||||
}
|
||||
// In real implementation, this should report task failure to remote wakeup server.
|
||||
printf("Task for client ID: %s timed-out, added at %" PRId64 " ms, now %" PRId64 " ms",
|
||||
taskInfo.taskData.clientid().c_str(), taskInfo.timestampInMs, now);
|
||||
mTasks.pop();
|
||||
}
|
||||
}
|
||||
|
||||
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 {kTaskIntervalInMs}ms.
|
||||
while (true) {
|
||||
mTaskQueue.add(mFakeTaskGenerator.generateTask());
|
||||
printf("Received a new task\n");
|
||||
if (mWakeupRequired) {
|
||||
wakeupApplicationProcessor();
|
||||
}
|
||||
|
||||
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] {
|
||||
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) {
|
||||
printf("GetRemoteTasks called\n");
|
||||
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.
|
||||
printf("Failed to deliver remote task to remote access HAL\n");
|
||||
// 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) {
|
||||
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
|
||||
// to finish executing those tasks.
|
||||
wakeupApplicationProcessor();
|
||||
}
|
||||
mWakeupRequired = request->iswakeuprequired();
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
void TestWakeupClientServiceImpl::wakeupApplicationProcessor() {
|
||||
printf("Waking up application processor...\n");
|
||||
// TODO(b/254547153): Send can bus message using socket CAN once we know what the message is.
|
||||
}
|
||||
|
||||
} // namespace remoteaccess
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
47
automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
Normal file
47
automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
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());
|
||||
printf("Test Remote Access GRPC Server listening on %s\n", serverAddress.c_str());
|
||||
server->Wait();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
RunServer();
|
||||
return 0;
|
||||
}
|
|
@ -112,6 +112,13 @@
|
|||
<regex-instance>.*</regex-instance>
|
||||
</interface>
|
||||
</hal>
|
||||
<hal format="aidl" optional="true">
|
||||
<name>android.hardware.automotive.remoteaccess</name>
|
||||
<interface>
|
||||
<name>IRemoteAccess</name>
|
||||
<regex-instance>.*</regex-instance>
|
||||
</interface>
|
||||
</hal>
|
||||
<hal format="hidl" optional="true">
|
||||
<name>android.hardware.automotive.vehicle</name>
|
||||
<version>2.0</version>
|
||||
|
|
Loading…
Reference in a new issue