mirror of https://github.com/grpc/grpc.git
parent
3cdb133525
commit
4ffcdd4ab7
98 changed files with 1 additions and 13312 deletions
@ -1,125 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPCPP_CREATE_CHANNEL_BINDER_H |
||||
#define GRPCPP_CREATE_CHANNEL_BINDER_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_ANDROID |
||||
|
||||
#include <grpcpp/channel.h> |
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
#include <grpcpp/support/channel_arguments.h> |
||||
#include <jni.h> |
||||
|
||||
#include <memory> |
||||
|
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
|
||||
/// EXPERIMENTAL Create a new \a Channel based on binder transport. The package
|
||||
/// name and class name will be used identify the specific application component
|
||||
/// to connect to.
|
||||
///
|
||||
/// \param jni_env_void Pointer to a JNIEnv structure
|
||||
/// \param context The context that we will use to invoke \a bindService See
|
||||
/// https://developer.android.com/reference/android/content/Context#bindService(android.content.Intent,%20android.content.ServiceConnection,%20int)
|
||||
/// for detail.
|
||||
/// \param package_name Package name of the component to be connected to
|
||||
/// \param class_name Class name of the component to be connected to
|
||||
/// \param security_policy Used for checking if remote component is allowed to
|
||||
/// connect
|
||||
std::shared_ptr<grpc::Channel> CreateBinderChannel( |
||||
void* jni_env_void, jobject context, absl::string_view package_name, |
||||
absl::string_view class_name, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
|
||||
/// EXPERIMENTAL Create a new \a Channel based on binder transport. The package
|
||||
/// name and class name will be used identify the specific application component
|
||||
/// to connect to.
|
||||
///
|
||||
/// \param jni_env_void Pointer to a JNIEnv structure
|
||||
/// \param context The context that we will use to invoke \a bindService See
|
||||
/// https://developer.android.com/reference/android/content/Context#bindService(android.content.Intent,%20android.content.ServiceConnection,%20int)
|
||||
/// for detail.
|
||||
/// \param package_name Package name of the component to be connected to
|
||||
/// \param class_name Class name of the component to be connected to
|
||||
/// \param security_policy Used for checking if remote component is allowed to
|
||||
/// connect
|
||||
/// \param args Options for channel creation.
|
||||
std::shared_ptr<grpc::Channel> CreateCustomBinderChannel( |
||||
void* jni_env_void, jobject context, absl::string_view package_name, |
||||
absl::string_view class_name, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy, |
||||
const ChannelArguments& args); |
||||
|
||||
/// EXPERIMENTAL Create a new \a Channel based on binder transport.
|
||||
///
|
||||
/// \param jni_env_void Pointer to a JNIEnv structure
|
||||
/// \param context The context that we will use to invoke \a bindService See
|
||||
/// https://developer.android.com/reference/android/content/Context#bindService(android.content.Intent,%20android.content.ServiceConnection,%20int)
|
||||
/// for detail.
|
||||
/// \param uri An URI that can be parsed as an `Intent` with
|
||||
/// https://developer.android.com/reference/android/content/Intent#parseUri(java.lang.String,%20int)
|
||||
/// \param security_policy Used for checking if remote component is allowed to
|
||||
/// connect
|
||||
std::shared_ptr<grpc::Channel> CreateBinderChannel( |
||||
void* jni_env_void, jobject context, absl::string_view uri, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
|
||||
/// EXPERIMENTAL Create a new \a Channel based on binder transport.
|
||||
///
|
||||
/// \param jni_env_void Pointer to a JNIEnv structure
|
||||
/// \param context The context that we will use to invoke \a bindService See
|
||||
/// https://developer.android.com/reference/android/content/Context#bindService(android.content.Intent,%20android.content.ServiceConnection,%20int)
|
||||
/// for detail.
|
||||
/// \param uri An URI that can be parsed as an `Intent` with
|
||||
/// https://developer.android.com/reference/android/content/Intent#parseUri(java.lang.String,%20int)
|
||||
/// \param security_policy Used for checking if remote component is allowed to
|
||||
/// connect
|
||||
/// \param args Options for channel creation.
|
||||
std::shared_ptr<grpc::Channel> CreateCustomBinderChannel( |
||||
void* jni_env_void, jobject context, absl::string_view uri, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy, |
||||
const ChannelArguments& args); |
||||
|
||||
/// EXPERIMENTAL Finds internal binder transport Java code. To create channels
|
||||
/// in threads created in native code, it is required to call this function
|
||||
/// once beforehand in a thread that is not created in native code.
|
||||
/// See
|
||||
/// https://developer.android.com/training/articles/perf-jni#faq:-why-didnt-findclass-find-my-class
|
||||
/// for details of this limitation.
|
||||
/// Returns true when the initialization is successful.
|
||||
bool InitializeBinderChannelJavaClass(void* jni_env_void); |
||||
|
||||
/// EXPERIMENTAL Alternative version of `InitializeBinderChannelJavaClass(void*
|
||||
/// jni_env_void)`. This version used a user-specified function to find the
|
||||
/// required internal Java class. When a class is found, the `class_finder`
|
||||
/// function should return a local reference to the class (jclass type). The
|
||||
/// returned jclass will then be used to create global reference for gRPC to use
|
||||
/// it later. After that, gRPC will DeleteLocalRef the returned local reference.
|
||||
bool InitializeBinderChannelJavaClass( |
||||
void* jni_env_void, std::function<void*(std::string)> class_finder); |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
|
||||
#endif |
||||
|
||||
#endif // GRPCPP_CREATE_CHANNEL_BINDER_H
|
@ -1,43 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPCPP_SECURITY_BINDER_CREDENTIALS_H |
||||
#define GRPCPP_SECURITY_BINDER_CREDENTIALS_H |
||||
|
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
#include <grpcpp/security/server_credentials.h> |
||||
|
||||
#include <memory> |
||||
|
||||
namespace grpc { |
||||
|
||||
class ChannelCredentials; |
||||
|
||||
namespace experimental { |
||||
|
||||
/// EXPERIMENTAL Builds Binder ServerCredentials.
|
||||
///
|
||||
/// This should be used along with `binder:` URI scheme. The path in the URI can
|
||||
/// later be used to access the server's endpoint binder.
|
||||
/// Note that calling \a ServerBuilder::AddListeningPort() with Binder
|
||||
/// ServerCredentials in a non-supported environment will make the subsequent
|
||||
/// call to \a ServerBuilder::BuildAndStart() return a null pointer.
|
||||
std::shared_ptr<grpc::ServerCredentials> BinderServerCredentials( |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
|
||||
#endif // GRPCPP_SECURITY_BINDER_CREDENTIALS_H
|
@ -1,82 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPCPP_SECURITY_BINDER_SECURITY_POLICY_H |
||||
#define GRPCPP_SECURITY_BINDER_SECURITY_POLICY_H |
||||
|
||||
#include <memory> |
||||
|
||||
#ifdef GPR_ANDROID |
||||
|
||||
#include <jni.h> |
||||
|
||||
#endif |
||||
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
namespace binder { |
||||
|
||||
// EXPERIMENTAL Determinines if a connection is allowed to be
|
||||
// established on Android. See https://source.android.com/security/app-sandbox
|
||||
// for more info about UID.
|
||||
class SecurityPolicy { |
||||
public: |
||||
virtual ~SecurityPolicy() = default; |
||||
// Returns true if the UID is authorized to connect.
|
||||
// Must return the same value for the same inputs so callers can safely cache
|
||||
// the result.
|
||||
virtual bool IsAuthorized(int uid) = 0; |
||||
}; |
||||
|
||||
// EXPERIMENTAL Allows all connection. Anything on the Android device will be
|
||||
// able to connect, use with caution!
|
||||
class UntrustedSecurityPolicy : public SecurityPolicy { |
||||
public: |
||||
UntrustedSecurityPolicy(); |
||||
~UntrustedSecurityPolicy() override; |
||||
bool IsAuthorized(int uid) override; |
||||
}; |
||||
|
||||
// EXPERIMENTAL Only allows the connections from processes with the same UID. In
|
||||
// most cases this means "from the same APK".
|
||||
class InternalOnlySecurityPolicy : public SecurityPolicy { |
||||
public: |
||||
InternalOnlySecurityPolicy(); |
||||
~InternalOnlySecurityPolicy() override; |
||||
bool IsAuthorized(int uid) override; |
||||
}; |
||||
|
||||
#ifdef GPR_ANDROID |
||||
|
||||
// EXPERIMENTAL Only allows the connections from the APK that have the same
|
||||
// signature.
|
||||
class SameSignatureSecurityPolicy : public SecurityPolicy { |
||||
public: |
||||
// `context` is required for getting PackageManager Java class
|
||||
SameSignatureSecurityPolicy(JavaVM* jvm, jobject context); |
||||
~SameSignatureSecurityPolicy() override; |
||||
bool IsAuthorized(int uid) override; |
||||
|
||||
private: |
||||
JavaVM* jvm_; |
||||
jobject context_; |
||||
}; |
||||
|
||||
#endif |
||||
|
||||
} // namespace binder
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
|
||||
#endif // GRPCPP_SECURITY_BINDER_SECURITY_POLICY_H
|
@ -1,10 +0,0 @@ |
||||
# Binder transport for cross process IPC on Android |
||||
|
||||
EXPERIMENTAL. API stability not guaranteed. |
||||
|
||||
This transport implements |
||||
[BinderChannel for native cross-process communication on Android](https://github.com/grpc/proposal/blob/master/L73-java-binderchannel.md) and enables C++/Java cross-process communication on Android with gRPC. |
||||
|
||||
Tests: https://github.com/grpc/grpc/tree/master/test/core/transport/binder/ |
||||
|
||||
Example apps: https://github.com/grpc/grpc/tree/master/examples/android/binder/java/io/grpc/binder/cpp |
@ -1,134 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include "src/core/ext/transport/binder/client/binder_connector.h" |
||||
#include "src/core/lib/iomgr/port.h" |
||||
|
||||
#ifdef GRPC_HAVE_UNIX_SOCKET |
||||
#ifdef GPR_WINDOWS |
||||
// clang-format off
|
||||
#include <ws2def.h> |
||||
#include <afunix.h> |
||||
// clang-format on
|
||||
#else |
||||
#include <sys/un.h> |
||||
#endif // GPR_WINDOWS
|
||||
#endif |
||||
|
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
|
||||
#include <functional> |
||||
#include <map> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "src/core/client_channel/connector.h" |
||||
#include "src/core/client_channel/subchannel.h" |
||||
#include "src/core/ext/transport/binder/client/endpoint_binder_pool.h" |
||||
#include "src/core/ext/transport/binder/client/security_policy_setting.h" |
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
|
||||
namespace { |
||||
|
||||
// TODO(mingcl): Currently this does no error handling and assumes the
|
||||
// connection always succeeds in reasonable amount of time.
|
||||
class BinderConnector : public grpc_core::SubchannelConnector { |
||||
public: |
||||
BinderConnector() {} |
||||
~BinderConnector() override {} |
||||
void Connect(const Args& args, Result* result, |
||||
grpc_closure* notify) override { |
||||
#ifdef GRPC_HAVE_UNIX_SOCKET |
||||
{ |
||||
struct sockaddr_un* un = |
||||
reinterpret_cast<struct sockaddr_un*>(args.address->addr); |
||||
// length of identifier, including null terminator
|
||||
size_t id_length = args.address->len - sizeof(un->sun_family); |
||||
// The c-style string at least will have a null terminator, and the
|
||||
// connection id itself should not be empty
|
||||
CHECK_GE(id_length, 2u); |
||||
// Make sure there is null terminator at the expected location before
|
||||
// reading from it
|
||||
CHECK_EQ(un->sun_path[id_length - 1], '\0'); |
||||
conn_id_ = un->sun_path; |
||||
} |
||||
#else |
||||
CHECK(0); |
||||
#endif |
||||
LOG(INFO) << "BinderConnector " << this << " conn_id_ = " << conn_id_; |
||||
|
||||
args_ = args; |
||||
CHECK_EQ(notify_, nullptr); |
||||
CHECK_NE(notify, nullptr); |
||||
notify_ = notify; |
||||
result_ = result; |
||||
|
||||
Ref().release(); // Ref held by the following callback
|
||||
|
||||
grpc_binder::GetEndpointBinderPool()->GetEndpointBinder( |
||||
conn_id_, |
||||
std::bind(&BinderConnector::OnConnected, this, std::placeholders::_1)); |
||||
} |
||||
|
||||
void OnConnected(std::unique_ptr<grpc_binder::Binder> endpoint_binder) { |
||||
CHECK(endpoint_binder != nullptr); |
||||
grpc_core::Transport* transport = grpc_create_binder_transport_client( |
||||
std::move(endpoint_binder), |
||||
grpc_binder::GetSecurityPolicySetting()->Get(conn_id_)); |
||||
CHECK_NE(transport, nullptr); |
||||
result_->channel_args = args_.channel_args; |
||||
result_->transport = transport; |
||||
|
||||
CHECK_NE(notify_, nullptr); |
||||
// ExecCtx is required here for running grpc_closure because this callback
|
||||
// might be invoked from non-gRPC code
|
||||
if (grpc_core::ExecCtx::Get() == nullptr) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, notify_, absl::OkStatus()); |
||||
} else { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, notify_, absl::OkStatus()); |
||||
} |
||||
|
||||
Unref(); // Was referenced in BinderConnector::Connect
|
||||
} |
||||
void Shutdown(grpc_error_handle /*error*/) override {} |
||||
|
||||
private: |
||||
Args args_; |
||||
grpc_closure* notify_ = nullptr; |
||||
Result* result_ = nullptr; |
||||
|
||||
std::string conn_id_; |
||||
}; |
||||
|
||||
} // namespace
|
||||
|
||||
namespace grpc_core { |
||||
|
||||
RefCountedPtr<Subchannel> BinderClientChannelFactory::CreateSubchannel( |
||||
const grpc_resolved_address& address, const ChannelArgs& args) { |
||||
LOG(INFO) << "BinderClientChannelFactory creating subchannel " << this; |
||||
return Subchannel::Create( |
||||
MakeOrphanable<BinderConnector>(), address, |
||||
args.Set(GRPC_ARG_DEFAULT_AUTHORITY, "binder.authority")); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_NO_BINDER
|
@ -1,41 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_BINDER_CONNECTOR_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_BINDER_CONNECTOR_H |
||||
|
||||
#include <grpc/impl/grpc_types.h> |
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpcpp/channel.h> |
||||
#include <grpcpp/support/channel_arguments.h> |
||||
|
||||
#include <memory> |
||||
#include <utility> |
||||
|
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/strings/strip.h" |
||||
#include "src/core/client_channel/client_channel_factory.h" |
||||
#include "src/core/client_channel/client_channel_filter.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
class BinderClientChannelFactory : public ClientChannelFactory { |
||||
public: |
||||
RefCountedPtr<Subchannel> CreateSubchannel( |
||||
const grpc_resolved_address& address, const ChannelArgs& args) override; |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_BINDER_CONNECTOR_H
|
@ -1,231 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <grpcpp/create_channel_binder.h> |
||||
|
||||
// The interface is only defined if GPR_ANDROID is defined, because some
|
||||
// arguments requires JNI.
|
||||
// Furthermore, the interface is non-phony only when
|
||||
// GPR_SUPPORT_BINDER_TRANSPORT is true because actual implementation of binder
|
||||
// transport requires newer version of NDK API
|
||||
|
||||
#ifdef GPR_ANDROID |
||||
|
||||
#include <grpc/grpc.h> |
||||
#include <grpc/grpc_posix.h> |
||||
|
||||
#include "src/core/util/crash.h" |
||||
|
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpcpp/impl/grpc_library.h> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/memory/memory.h" |
||||
#include "absl/strings/substitute.h" |
||||
#include "absl/time/clock.h" |
||||
#include "absl/time/time.h" |
||||
#include "src/core/client_channel/client_channel_filter.h" |
||||
#include "src/core/ext/transport/binder/client/channel_create_impl.h" |
||||
#include "src/core/ext/transport/binder/client/connection_id_generator.h" |
||||
#include "src/core/ext/transport/binder/client/endpoint_binder_pool.h" |
||||
#include "src/core/ext/transport/binder/client/jni_utils.h" |
||||
#include "src/core/ext/transport/binder/client/security_policy_setting.h" |
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder_android.h" |
||||
#include "src/core/lib/channel/channel_args.h" |
||||
#include "src/core/lib/config/core_configuration.h" |
||||
#include "src/core/lib/surface/channel.h" |
||||
#include "src/core/lib/transport/transport.h" |
||||
#include "src/cpp/client/create_channel_internal.h" |
||||
|
||||
namespace { |
||||
// grpc.io.action.BIND is the standard action name for binding to binder
|
||||
// transport server.
|
||||
const char* kStandardActionName = "grpc.io.action.BIND"; |
||||
} // namespace
|
||||
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
|
||||
std::shared_ptr<grpc::Channel> CreateBinderChannel( |
||||
void* jni_env_void, jobject context, absl::string_view package_name, |
||||
absl::string_view class_name, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) { |
||||
return CreateCustomBinderChannel(jni_env_void, context, package_name, |
||||
class_name, security_policy, |
||||
ChannelArguments()); |
||||
} |
||||
|
||||
std::shared_ptr<grpc::Channel> CreateCustomBinderChannel( |
||||
void* jni_env_void, jobject context, absl::string_view package_name, |
||||
absl::string_view class_name, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy, |
||||
const ChannelArguments& args) { |
||||
return CreateCustomBinderChannel( |
||||
jni_env_void, context, |
||||
absl::Substitute("android-app://$0#Intent;action=$1;component=$0/$2;end", |
||||
package_name, kStandardActionName, class_name), |
||||
security_policy, args); |
||||
} |
||||
|
||||
std::shared_ptr<grpc::Channel> CreateBinderChannel( |
||||
void* jni_env_void, jobject context, absl::string_view uri, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) { |
||||
return CreateCustomBinderChannel(jni_env_void, context, uri, security_policy, |
||||
ChannelArguments()); |
||||
} |
||||
|
||||
std::shared_ptr<grpc::Channel> CreateCustomBinderChannel( |
||||
void* jni_env_void, jobject context, absl::string_view uri, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy, |
||||
const ChannelArguments& args) { |
||||
grpc_init(); |
||||
|
||||
CHECK_NE(jni_env_void, nullptr); |
||||
CHECK_NE(security_policy, nullptr); |
||||
|
||||
// Generate an unique connection ID that identifies this connection (Useful
|
||||
// for mapping connection between Java and C++ code).
|
||||
std::string connection_id = |
||||
grpc_binder::GetConnectionIdGenerator()->Generate(uri); |
||||
|
||||
LOG(ERROR) << "connection id is " << connection_id; |
||||
|
||||
// After invoking this Java method, Java code will put endpoint binder into
|
||||
// `EndpointBinderPool` after the connection succeeds
|
||||
// TODO(mingcl): Consider if we want to delay the connection establishment
|
||||
// until SubchannelConnector start establishing connection. For now we don't
|
||||
// see any benifits doing that.
|
||||
grpc_binder::TryEstablishConnectionWithUri(static_cast<JNIEnv*>(jni_env_void), |
||||
context, uri, connection_id); |
||||
|
||||
grpc_binder::GetSecurityPolicySetting()->Set(connection_id, security_policy); |
||||
|
||||
// Set server URI to a URI that contains connection id. The URI will be used
|
||||
// by subchannel connector to obtain correct endpoint binder from
|
||||
// `EndpointBinderPool`.
|
||||
string server_uri = "binder:" + connection_id; |
||||
|
||||
grpc_channel_args channel_args; |
||||
args.SetChannelArgs(&channel_args); |
||||
|
||||
return CreateChannelInternal( |
||||
"", |
||||
grpc::internal::CreateClientBinderChannelImpl(server_uri, &channel_args), |
||||
std::vector< |
||||
std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>()); |
||||
} |
||||
|
||||
bool InitializeBinderChannelJavaClass(void* jni_env_void) { |
||||
return grpc_binder::FindNativeConnectionHelper( |
||||
static_cast<JNIEnv*>(jni_env_void)) != nullptr; |
||||
} |
||||
|
||||
bool InitializeBinderChannelJavaClass( |
||||
void* jni_env_void, std::function<void*(std::string)> class_finder) { |
||||
return grpc_binder::FindNativeConnectionHelper( |
||||
static_cast<JNIEnv*>(jni_env_void), class_finder) != nullptr; |
||||
} |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
|
||||
#else // !GPR_SUPPORT_BINDER_TRANSPORT
|
||||
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
|
||||
std::shared_ptr<grpc::Channel> CreateBinderChannel( |
||||
void*, jobject, absl::string_view, absl::string_view, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy>) { |
||||
LOG(ERROR) << "This APK is compiled with Android API level = " |
||||
<< __ANDROID_API__ |
||||
<< ", which is not supported. See port_platform.h for supported " |
||||
"versions."; |
||||
CHECK(0); |
||||
return {}; |
||||
} |
||||
|
||||
std::shared_ptr<grpc::Channel> CreateCustomBinderChannel( |
||||
void*, jobject, absl::string_view, absl::string_view, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy>, |
||||
const ChannelArguments&) { |
||||
LOG(ERROR) << "This APK is compiled with Android API level = " |
||||
<< __ANDROID_API__ |
||||
<< ", which is not supported. See port_platform.h for supported " |
||||
"versions."; |
||||
CHECK(0); |
||||
return {}; |
||||
} |
||||
|
||||
std::shared_ptr<grpc::Channel> CreateBinderChannel( |
||||
void*, jobject, absl::string_view, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy>) { |
||||
LOG(ERROR) << "This APK is compiled with Android API level = " |
||||
<< __ANDROID_API__ |
||||
<< ", which is not supported. See port_platform.h for supported " |
||||
"versions."; |
||||
CHECK(0); |
||||
return {}; |
||||
} |
||||
|
||||
std::shared_ptr<grpc::Channel> CreateCustomBinderChannel( |
||||
void*, jobject, absl::string_view, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy>, |
||||
const ChannelArguments&) { |
||||
LOG(ERROR) << "This APK is compiled with Android API level = " |
||||
<< __ANDROID_API__ |
||||
<< ", which is not supported. See port_platform.h for supported " |
||||
"versions."; |
||||
CHECK(0); |
||||
return {}; |
||||
} |
||||
|
||||
bool InitializeBinderChannelJavaClass(void* jni_env_void) { |
||||
LOG(ERROR) << "This APK is compiled with Android API level = " |
||||
<< __ANDROID_API__ |
||||
<< ", which is not supported. See port_platform.h for supported " |
||||
"versions."; |
||||
CHECK(0); |
||||
return {}; |
||||
} |
||||
|
||||
bool InitializeBinderChannelJavaClass( |
||||
void* jni_env_void, std::function<void*(std::string)> class_finder) { |
||||
LOG(ERROR) << "This APK is compiled with Android API level = " |
||||
<< __ANDROID_API__ |
||||
<< ", which is not supported. See port_platform.h for supported " |
||||
"versions."; |
||||
CHECK(0); |
||||
return {}; |
||||
} |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
|
||||
#endif // GPR_SUPPORT_BINDER_TRANSPORT
|
||||
|
||||
#endif // GPR_ANDROID
|
||||
|
||||
#endif // GRPC_NO_BINDER
|
@ -1,93 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "src/core/ext/transport/binder/client/channel_create_impl.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <memory> |
||||
#include <utility> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "src/core/ext/transport/binder/client/binder_connector.h" |
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/lib/channel/channel_args.h" |
||||
#include "src/core/lib/config/core_configuration.h" |
||||
#include "src/core/lib/surface/channel.h" |
||||
#include "src/core/lib/surface/channel_create.h" |
||||
|
||||
namespace { |
||||
|
||||
grpc_core::BinderClientChannelFactory* g_factory; |
||||
gpr_once g_factory_once = GPR_ONCE_INIT; |
||||
|
||||
void FactoryInit() { g_factory = new grpc_core::BinderClientChannelFactory(); } |
||||
} // namespace
|
||||
|
||||
namespace grpc { |
||||
namespace internal { |
||||
|
||||
grpc_channel* CreateDirectBinderChannelImplForTesting( |
||||
std::unique_ptr<grpc_binder::Binder> endpoint_binder, |
||||
const grpc_channel_args* args, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
|
||||
grpc_core::Transport* transport = grpc_create_binder_transport_client( |
||||
std::move(endpoint_binder), security_policy); |
||||
CHECK_NE(transport, nullptr); |
||||
|
||||
auto channel_args = grpc_core::CoreConfiguration::Get() |
||||
.channel_args_preconditioning() |
||||
.PreconditionChannelArgs(args) |
||||
.Set(GRPC_ARG_DEFAULT_AUTHORITY, "binder.authority"); |
||||
auto channel = |
||||
grpc_core::ChannelCreate("binder_target_placeholder", channel_args, |
||||
GRPC_CLIENT_DIRECT_CHANNEL, transport); |
||||
// TODO(mingcl): Handle error properly
|
||||
CHECK(channel.ok()); |
||||
grpc_channel_args_destroy(args); |
||||
return channel->release()->c_ptr(); |
||||
} |
||||
|
||||
grpc_channel* CreateClientBinderChannelImpl(std::string target, |
||||
const grpc_channel_args* args) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
|
||||
gpr_once_init(&g_factory_once, FactoryInit); |
||||
|
||||
auto channel_args = grpc_core::CoreConfiguration::Get() |
||||
.channel_args_preconditioning() |
||||
.PreconditionChannelArgs(args) |
||||
.SetObject(g_factory); |
||||
|
||||
auto channel = grpc_core::ChannelCreate(target, channel_args, |
||||
GRPC_CLIENT_CHANNEL, nullptr); |
||||
|
||||
if (!channel.ok()) { |
||||
return grpc_lame_client_channel_create( |
||||
target.c_str(), static_cast<grpc_status_code>(channel.status().code()), |
||||
"Failed to create binder channel"); |
||||
} |
||||
|
||||
return channel->release()->c_ptr(); |
||||
} |
||||
|
||||
} // namespace internal
|
||||
} // namespace grpc
|
||||
#endif |
@ -1,42 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_CHANNEL_CREATE_IMPL_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_CHANNEL_CREATE_IMPL_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
|
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/lib/channel/channel_args.h" |
||||
|
||||
namespace grpc { |
||||
namespace internal { |
||||
|
||||
// Creates a GRPC_CLIENT_DIRECT_CHANNEL channel from endpoint binder
|
||||
// At this moment this is only used for testing.
|
||||
grpc_channel* CreateDirectBinderChannelImplForTesting( |
||||
std::unique_ptr<grpc_binder::Binder> endpoint_binder, |
||||
const grpc_channel_args* args, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
|
||||
// Creates a GRPC_CLIENT_CHANNEL channel
|
||||
grpc_channel* CreateClientBinderChannelImpl(std::string target, |
||||
const grpc_channel_args* args); |
||||
|
||||
} // namespace internal
|
||||
} // namespace grpc
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_CHANNEL_CREATE_IMPL_H
|
@ -1,69 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "src/core/ext/transport/binder/client/connection_id_generator.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/strings/str_cat.h" |
||||
|
||||
namespace { |
||||
// Make sure `s` does not contain characters other than numbers, alphabets,
|
||||
// period and underscore
|
||||
std::string Normalize(absl::string_view str_view) { |
||||
std::string s = std::string(str_view); |
||||
for (size_t i = 0; i < s.length(); i++) { |
||||
if (!isalnum(s[i]) && s[i] != '.') { |
||||
s[i] = '_'; |
||||
} |
||||
} |
||||
return s; |
||||
} |
||||
|
||||
// Remove prefix of the string if the string is longer than len
|
||||
std::string StripToLength(const std::string& s, size_t len) { |
||||
if (s.length() > len) { |
||||
return s.substr(s.length() - len, len); |
||||
} |
||||
return s; |
||||
} |
||||
} // namespace
|
||||
|
||||
namespace grpc_binder { |
||||
|
||||
std::string ConnectionIdGenerator::Generate(absl::string_view uri) { |
||||
// reserve some room for serial number
|
||||
const size_t kReserveForNumbers = 15; |
||||
std::string s = |
||||
StripToLength(Normalize(uri), kPathLengthLimit - kReserveForNumbers); |
||||
std::string ret; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
// Insert a hyphen before serial number
|
||||
ret = absl::StrCat(s, "-", ++count_); |
||||
} |
||||
CHECK_LT(ret.length(), kPathLengthLimit); |
||||
return ret; |
||||
} |
||||
|
||||
ConnectionIdGenerator* GetConnectionIdGenerator() { |
||||
static ConnectionIdGenerator* cig = new ConnectionIdGenerator(); |
||||
return cig; |
||||
} |
||||
|
||||
} // namespace grpc_binder
|
||||
#endif |
@ -1,50 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_CONNECTION_ID_GENERATOR_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_CONNECTION_ID_GENERATOR_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <map> |
||||
|
||||
#include "absl/strings/string_view.h" |
||||
#include "src/core/util/sync.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
// Generates somewhat human-readable unique identifiers from package name and
|
||||
// class name. We will generate a Id that only contains unreserved URI
|
||||
// characters (uppercase and lowercase letters, decimal digits, hyphen, period,
|
||||
// underscore, and tilde).
|
||||
class ConnectionIdGenerator { |
||||
public: |
||||
std::string Generate(absl::string_view uri); |
||||
|
||||
private: |
||||
// Our generated Id need to fit in unix socket path length limit. We use 100
|
||||
// here to be safe.
|
||||
const size_t kPathLengthLimit = 100; |
||||
|
||||
grpc_core::Mutex m_; |
||||
// Every generated identifier will followed by the value of this counter to
|
||||
// make sure every generated id is unique.
|
||||
int count_ ABSL_GUARDED_BY(m_); |
||||
}; |
||||
|
||||
ConnectionIdGenerator* GetConnectionIdGenerator(); |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_CONNECTION_ID_GENERATOR_H
|
@ -1,114 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "src/core/ext/transport/binder/client/endpoint_binder_pool.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include "src/core/ext/transport/binder/client/jni_utils.h" |
||||
|
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
|
||||
#include <jni.h> |
||||
|
||||
#include "src/core/ext/transport/binder/wire_format/binder_android.h" |
||||
|
||||
extern "C" { |
||||
// Adds endpoint binder to binder pool when Java notify us that the endpoint
|
||||
// binder is ready. This is called from GrpcBinderConnection.java
|
||||
JNIEXPORT void JNICALL |
||||
Java_io_grpc_binder_cpp_GrpcBinderConnection_notifyConnected__Ljava_lang_String_2Landroid_os_IBinder_2( |
||||
JNIEnv* jni_env, jobject, jstring conn_id_jstring, jobject ibinder) { |
||||
jboolean isCopy; |
||||
const char* conn_id = jni_env->GetStringUTFChars(conn_id_jstring, &isCopy); |
||||
LOG(INFO) << __func__ << " invoked with conn_id = " << conn_id; |
||||
CHECK_NE(ibinder, nullptr); |
||||
grpc_binder::ndk_util::SpAIBinder aibinder = |
||||
grpc_binder::FromJavaBinder(jni_env, ibinder); |
||||
LOG(INFO) << __func__ << " got aibinder = " << aibinder.get(); |
||||
auto b = std::make_unique<grpc_binder::BinderAndroid>(aibinder); |
||||
CHECK(b != nullptr); |
||||
grpc_binder::GetEndpointBinderPool()->AddEndpointBinder(conn_id, |
||||
std::move(b)); |
||||
if (isCopy == JNI_TRUE) { |
||||
jni_env->ReleaseStringUTFChars(conn_id_jstring, conn_id); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif // GPR_SUPPORT_BINDER_TRANSPORT
|
||||
|
||||
namespace grpc_binder { |
||||
|
||||
void EndpointBinderPool::GetEndpointBinder( |
||||
std::string conn_id, |
||||
std::function<void(std::unique_ptr<grpc_binder::Binder>)> cb) { |
||||
LOG(INFO) << "EndpointBinder requested. conn_id = " << conn_id; |
||||
std::unique_ptr<grpc_binder::Binder> b; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
if (binder_map_.count(conn_id)) { |
||||
b = std::move(binder_map_[conn_id]); |
||||
binder_map_.erase(conn_id); |
||||
CHECK(b != nullptr); |
||||
} else { |
||||
if (pending_requests_.count(conn_id) != 0) { |
||||
LOG(ERROR) << "Duplicate GetEndpointBinder requested. conn_id = " |
||||
<< conn_id; |
||||
return; |
||||
} |
||||
pending_requests_[conn_id] = std::move(cb); |
||||
return; |
||||
} |
||||
} |
||||
CHECK(b != nullptr); |
||||
cb(std::move(b)); |
||||
} |
||||
|
||||
void EndpointBinderPool::AddEndpointBinder( |
||||
std::string conn_id, std::unique_ptr<grpc_binder::Binder> b) { |
||||
LOG(INFO) << "EndpointBinder added. conn_id = " << conn_id; |
||||
CHECK(b != nullptr); |
||||
// cb will be set in the following block if there is a pending callback
|
||||
std::function<void(std::unique_ptr<grpc_binder::Binder>)> cb = nullptr; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
if (binder_map_.count(conn_id) != 0) { |
||||
LOG(ERROR) << "EndpointBinder already in the pool. conn_id = " << conn_id; |
||||
return; |
||||
} |
||||
if (pending_requests_.count(conn_id)) { |
||||
cb = std::move(pending_requests_[conn_id]); |
||||
pending_requests_.erase(conn_id); |
||||
} else { |
||||
binder_map_[conn_id] = std::move(b); |
||||
b = nullptr; |
||||
} |
||||
} |
||||
if (cb != nullptr) { |
||||
cb(std::move(b)); |
||||
} |
||||
} |
||||
|
||||
EndpointBinderPool* GetEndpointBinderPool() { |
||||
static EndpointBinderPool* p = new EndpointBinderPool(); |
||||
return p; |
||||
} |
||||
} // namespace grpc_binder
|
||||
#endif |
@ -1,64 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_ENDPOINT_BINDER_POOL_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_ENDPOINT_BINDER_POOL_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <functional> |
||||
#include <string> |
||||
|
||||
#include "absl/container/flat_hash_map.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/util/sync.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
// This class serves as a buffer of endpoint binders between C++ and
|
||||
// Java. `AddEndpointBinder` will be indirectly invoked by Java code, and
|
||||
// `GetEndpointBinder` is for C++ code to register callback to get endpoint
|
||||
// binder when become available. This simplifies JNI related threading issues
|
||||
// since both side only need to interact with this buffer in non-blocking
|
||||
// manner and avoids cross-language callbacks.
|
||||
class EndpointBinderPool { |
||||
public: |
||||
// Invokes the callback when the binder corresponding to the conn_id become
|
||||
// available. If the binder is already available, invokes the callback
|
||||
// immediately.
|
||||
// Ownership of the endpoint binder will be transferred to the callback
|
||||
// function and it will be removed from the pool
|
||||
void GetEndpointBinder( |
||||
std::string conn_id, |
||||
std::function<void(std::unique_ptr<grpc_binder::Binder>)> cb); |
||||
|
||||
// Add an endpoint binder to the pool
|
||||
void AddEndpointBinder(std::string conn_id, |
||||
std::unique_ptr<grpc_binder::Binder> b); |
||||
|
||||
private: |
||||
grpc_core::Mutex m_; |
||||
absl::flat_hash_map<std::string, std::unique_ptr<grpc_binder::Binder>> |
||||
binder_map_ ABSL_GUARDED_BY(m_); |
||||
absl::flat_hash_map<std::string, |
||||
std::function<void(std::unique_ptr<grpc_binder::Binder>)>> |
||||
pending_requests_ ABSL_GUARDED_BY(m_); |
||||
}; |
||||
|
||||
// Returns the singleton
|
||||
EndpointBinderPool* GetEndpointBinderPool(); |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_ENDPOINT_BINDER_POOL_H
|
@ -1,138 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "src/core/ext/transport/binder/client/jni_utils.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include "src/core/util/crash.h" |
||||
|
||||
#if defined(ANDROID) || defined(__ANDROID__) |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
jclass FindNativeConnectionHelper(JNIEnv* env) { |
||||
return FindNativeConnectionHelper( |
||||
env, [env](std::string cl) { return env->FindClass(cl.c_str()); }); |
||||
} |
||||
|
||||
jclass FindNativeConnectionHelper( |
||||
JNIEnv* env, std::function<void*(std::string)> class_finder) { |
||||
auto do_find = [env, class_finder]() { |
||||
jclass cl = static_cast<jclass>( |
||||
class_finder("io/grpc/binder/cpp/NativeConnectionHelper")); |
||||
if (cl == nullptr) { |
||||
return cl; |
||||
} |
||||
jclass global_cl = static_cast<jclass>(env->NewGlobalRef(cl)); |
||||
env->DeleteLocalRef(cl); |
||||
CHECK_NE(global_cl, nullptr); |
||||
return global_cl; |
||||
}; |
||||
static jclass connection_helper_class = do_find(); |
||||
if (connection_helper_class != nullptr) { |
||||
return connection_helper_class; |
||||
} |
||||
// Some possible reasons:
|
||||
// * There is no Java class in the call stack and this is not invoked
|
||||
// from JNI_OnLoad
|
||||
// * The APK does not correctly depends on the helper class, or the
|
||||
// class get shrinked
|
||||
LOG(ERROR) |
||||
<< "Cannot find binder transport Java helper class. Did you invoke " |
||||
"grpc::experimental::InitializeBinderChannelJavaClass correctly " |
||||
"beforehand? Did the APK correctly include the connection helper " |
||||
"class (i.e depends on build target " |
||||
"src/core/ext/transport/binder/java/io/grpc/binder/" |
||||
"cpp:connection_helper) ?"; |
||||
// TODO(mingcl): Maybe it is worth to try again so the failure can be fixed
|
||||
// by invoking this function again at a different thread.
|
||||
return nullptr; |
||||
} |
||||
|
||||
void TryEstablishConnection(JNIEnv* env, jobject application, |
||||
absl::string_view pkg, absl::string_view cls, |
||||
absl::string_view action_name, |
||||
absl::string_view conn_id) { |
||||
std::string method = "tryEstablishConnection"; |
||||
std::string type = |
||||
"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/" |
||||
"lang/String;Ljava/lang/String;)V"; |
||||
|
||||
jclass cl = FindNativeConnectionHelper(env); |
||||
if (cl == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
jmethodID mid = env->GetStaticMethodID(cl, method.c_str(), type.c_str()); |
||||
if (mid == nullptr) { |
||||
LOG(ERROR) << "No method id " << method; |
||||
} |
||||
|
||||
env->CallStaticVoidMethod(cl, mid, application, |
||||
env->NewStringUTF(std::string(pkg).c_str()), |
||||
env->NewStringUTF(std::string(cls).c_str()), |
||||
env->NewStringUTF(std::string(action_name).c_str()), |
||||
env->NewStringUTF(std::string(conn_id).c_str())); |
||||
} |
||||
|
||||
void TryEstablishConnectionWithUri(JNIEnv* env, jobject application, |
||||
absl::string_view uri, |
||||
absl::string_view conn_id) { |
||||
std::string method = "tryEstablishConnectionWithUri"; |
||||
std::string type = |
||||
"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V"; |
||||
|
||||
jclass cl = FindNativeConnectionHelper(env); |
||||
if (cl == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
jmethodID mid = env->GetStaticMethodID(cl, method.c_str(), type.c_str()); |
||||
if (mid == nullptr) { |
||||
LOG(ERROR) << "No method id " << method; |
||||
} |
||||
|
||||
env->CallStaticVoidMethod(cl, mid, application, |
||||
env->NewStringUTF(std::string(uri).c_str()), |
||||
env->NewStringUTF(std::string(conn_id).c_str())); |
||||
} |
||||
|
||||
bool IsSignatureMatch(JNIEnv* env, jobject context, int uid1, int uid2) { |
||||
const std::string method = "isSignatureMatch"; |
||||
const std::string type = "(Landroid/content/Context;II)Z"; |
||||
|
||||
jclass cl = FindNativeConnectionHelper(env); |
||||
if (cl == nullptr) { |
||||
return false; |
||||
} |
||||
|
||||
jmethodID mid = env->GetStaticMethodID(cl, method.c_str(), type.c_str()); |
||||
if (mid == nullptr) { |
||||
LOG(ERROR) << "No method id " << method; |
||||
} |
||||
|
||||
jboolean result = env->CallStaticBooleanMethod(cl, mid, context, uid1, uid2); |
||||
return result == JNI_TRUE; |
||||
} |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif |
||||
#endif |
@ -1,57 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_JNI_UTILS_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_JNI_UTILS_H |
||||
|
||||
#if defined(ANDROID) || defined(__ANDROID__) |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
#include <jni.h> |
||||
|
||||
#include <functional> |
||||
#include <string> |
||||
|
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
// Finds NativeConnectionHelper Java class and caches it. This is useful because
|
||||
// FindClass only works when there is a Java class in the call stack. Typically
|
||||
// user might want to call this once in a place that is called from Java (ex.
|
||||
// JNI_OnLoad) so subsequent BinderTransport code can find Java class
|
||||
jclass FindNativeConnectionHelper(JNIEnv* env); |
||||
|
||||
jclass FindNativeConnectionHelper( |
||||
JNIEnv* env, std::function<void*(std::string)> class_finder); |
||||
|
||||
// Calls Java method NativeConnectionHelper.tryEstablishConnection
|
||||
void TryEstablishConnection(JNIEnv* env, jobject application, |
||||
absl::string_view pkg, absl::string_view cls, |
||||
absl::string_view action_name, |
||||
absl::string_view conn_id); |
||||
|
||||
void TryEstablishConnectionWithUri(JNIEnv* env, jobject application, |
||||
absl::string_view uri, |
||||
absl::string_view conn_id); |
||||
|
||||
// Calls Java method NativeConnectionHelper.isSignatureMatch.
|
||||
// Will also return false if failed to invoke Java.
|
||||
bool IsSignatureMatch(JNIEnv* env, jobject context, int uid1, int uid2); |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif |
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_JNI_UTILS_H
|
@ -1,47 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 <grpc/support/port_platform.h> |
||||
|
||||
#include "absl/log/check.h" |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include "src/core/ext/transport/binder/client/security_policy_setting.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
void SecurityPolicySetting::Set( |
||||
absl::string_view connection_id, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) { |
||||
grpc_core::MutexLock l(&m_); |
||||
CHECK_EQ(security_policy_map_.count(std::string(connection_id)), 0u); |
||||
security_policy_map_[std::string(connection_id)] = security_policy; |
||||
} |
||||
|
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
SecurityPolicySetting::Get(absl::string_view connection_id) { |
||||
grpc_core::MutexLock l(&m_); |
||||
CHECK_NE(security_policy_map_.count(std::string(connection_id)), 0u); |
||||
return security_policy_map_[std::string(connection_id)]; |
||||
} |
||||
|
||||
SecurityPolicySetting* GetSecurityPolicySetting() { |
||||
static SecurityPolicySetting* s = new SecurityPolicySetting(); |
||||
return s; |
||||
} |
||||
|
||||
} // namespace grpc_binder
|
||||
#endif |
@ -1,49 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_SECURITY_POLICY_SETTING_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_SECURITY_POLICY_SETTING_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
|
||||
#include "absl/container/flat_hash_map.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "src/core/util/sync.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
// A singleton class for setting security setting for each connection. This is
|
||||
// required because we cannot pass security policy shared pointers around using
|
||||
// gRPC arguments, we can only pass connection_id around as part of URI
|
||||
class SecurityPolicySetting { |
||||
public: |
||||
void Set(absl::string_view connection_id, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> Get( |
||||
absl::string_view connection_id); |
||||
|
||||
private: |
||||
grpc_core::Mutex m_; |
||||
absl::flat_hash_map< |
||||
std::string, std::shared_ptr<grpc::experimental::binder::SecurityPolicy>> |
||||
security_policy_map_ ABSL_GUARDED_BY(m_); |
||||
}; |
||||
|
||||
SecurityPolicySetting* GetSecurityPolicySetting(); |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_SECURITY_POLICY_SETTING_H
|
@ -1,30 +0,0 @@ |
||||
# Copyright 2021 gRPC authors. |
||||
# |
||||
# 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. |
||||
|
||||
# copybara: Import internal android_library rule here |
||||
|
||||
licenses(["notice"]) |
||||
|
||||
android_library( |
||||
name = "connection_helper", |
||||
srcs = [ |
||||
"GrpcBinderConnection.java", |
||||
"GrpcCppServerBuilder.java", |
||||
"NativeConnectionHelper.java", |
||||
], |
||||
visibility = ["//visibility:public"], |
||||
deps = [ |
||||
# copybara: Add proguard dependency here |
||||
], |
||||
) |
@ -1,104 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 io.grpc.binder.cpp; |
||||
|
||||
import static android.content.Intent.URI_ANDROID_APP_SCHEME; |
||||
import static android.content.Intent.URI_INTENT_SCHEME; |
||||
|
||||
import android.annotation.TargetApi; |
||||
import android.content.ComponentName; |
||||
import android.content.Context; |
||||
import android.content.Intent; |
||||
import android.content.ServiceConnection; |
||||
import android.os.IBinder; |
||||
import android.util.Log; |
||||
import java.net.URISyntaxException; |
||||
|
||||
/* Handles the binder connection state with OnDeviceServer server */ |
||||
public class GrpcBinderConnection implements ServiceConnection { |
||||
private static final String logTag = "GrpcBinderConnection"; |
||||
|
||||
private Context mContext; |
||||
private IBinder mService; |
||||
|
||||
// A string that identifies this service connection
|
||||
private final String mConnId; |
||||
|
||||
public GrpcBinderConnection(Context context, String connId) { |
||||
mContext = context; |
||||
mConnId = connId; |
||||
} |
||||
|
||||
@Override |
||||
public void onNullBinding(ComponentName className) { |
||||
// TODO(mingcl): Notify C++ that the connection is never going to happen
|
||||
Log.e(logTag, "Service returned null IBinder. mConnId = " + mConnId); |
||||
} |
||||
|
||||
@Override |
||||
public void onServiceConnected(ComponentName className, IBinder service) { |
||||
Log.e(logTag, "Service has connected. mConnId = " + mConnId); |
||||
if (service == null) { |
||||
// This should not happen since onNullBinding should be invoked instead
|
||||
throw new IllegalArgumentException("service was null"); |
||||
} |
||||
synchronized (this) { |
||||
mService = service; |
||||
} |
||||
notifyConnected(mConnId, mService); |
||||
} |
||||
|
||||
@Override |
||||
public void onServiceDisconnected(ComponentName className) { |
||||
Log.e(logTag, "Service has disconnected. mConnId = " + mConnId); |
||||
} |
||||
|
||||
public void tryConnect(String pkg, String cls, String action_name) { |
||||
Intent intent = new Intent(action_name); |
||||
ComponentName compName = new ComponentName(pkg, cls); |
||||
intent.setComponent(compName); |
||||
tryConnect(intent); |
||||
} |
||||
|
||||
@TargetApi(22) |
||||
public void tryConnect(String uri) { |
||||
// Try connect with an URI that can be parsed as intent.
|
||||
try { |
||||
tryConnect(Intent.parseUri(uri, URI_ANDROID_APP_SCHEME | URI_INTENT_SCHEME)); |
||||
} catch (URISyntaxException e) { |
||||
Log.e(logTag, "Unable to parse the Uri: " + uri); |
||||
} |
||||
} |
||||
|
||||
private void tryConnect(Intent intent) { |
||||
synchronized (this) { |
||||
// Will return true if the system is in the process of bringing up a service that your client
|
||||
// has permission to bind to; false if the system couldn't find the service or if your client
|
||||
// doesn't have permission to bind to it
|
||||
boolean result = mContext.bindService(intent, this, Context.BIND_AUTO_CREATE); |
||||
if (result) { |
||||
Log.e(logTag, "bindService returns ok"); |
||||
} else { |
||||
Log.e( |
||||
logTag, |
||||
"bindService failed. Maybe the system couldn't find the service or the" |
||||
+ " client doesn't have permission to bind to it."); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Calls a function defined in endpoint_binder_pool.cc
|
||||
private static native void notifyConnected(String connId, IBinder service); |
||||
} |
@ -1,40 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 io.grpc.binder.cpp; |
||||
|
||||
import android.os.IBinder; |
||||
import android.util.Log; |
||||
|
||||
/* EXPERIMENTAL. Provides a interface to get endpoint binder from C++ */ |
||||
public class GrpcCppServerBuilder { |
||||
private static final String logTag = "GrpcCppServerBuilder"; |
||||
|
||||
public static IBinder GetEndpointBinder(String uri) { |
||||
String scheme = "binder:"; |
||||
if (uri.startsWith(scheme)) { |
||||
String path = uri.substring(scheme.length()); |
||||
// TODO(mingcl): Consider if we would like to make sure the path only contain valid
|
||||
// characters here
|
||||
IBinder ibinder = GetEndpointBinderInternal(path); |
||||
Log.e(logTag, "Returning binder=" + ibinder + " for URI=" + uri); |
||||
return ibinder; |
||||
} else { |
||||
Log.e(logTag, "URI " + uri + " does not start with 'binder:'"); |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
private static native IBinder GetEndpointBinderInternal(String conn_id); |
||||
} |
@ -1,71 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 io.grpc.binder.cpp; |
||||
|
||||
import android.content.Context; |
||||
import android.content.pm.PackageManager; |
||||
import android.os.Parcel; |
||||
import android.util.Log; |
||||
// copybara: Import proguard UsedByNative annotation here
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* This class will be invoked by gRPC binder transport internal implementation (from |
||||
* src/core/ext/transport/binder/client/jni_utils.cc) to perform operations that are only possible |
||||
* in Java |
||||
*/ |
||||
// copybara: Add @UsedByNative("jni_utils.cc")
|
||||
final class NativeConnectionHelper { |
||||
// Maps connection id to GrpcBinderConnection instances
|
||||
static Map<String, GrpcBinderConnection> connectionIdToGrpcBinderConnectionMap = new HashMap<>(); |
||||
|
||||
// copybara: Add @UsedByNative("jni_utils.cc")
|
||||
static void tryEstablishConnection( |
||||
Context context, String pkg, String cls, String actionName, String connId) { |
||||
// TODO(mingcl): Assert that connId is unique
|
||||
connectionIdToGrpcBinderConnectionMap.put(connId, new GrpcBinderConnection(context, connId)); |
||||
connectionIdToGrpcBinderConnectionMap.get(connId).tryConnect(pkg, cls, actionName); |
||||
} |
||||
|
||||
// copybara: Add @UsedByNative("jni_utils.cc")
|
||||
static void tryEstablishConnectionWithUri(Context context, String uri, String connId) { |
||||
// TODO(mingcl): Assert that connId is unique
|
||||
connectionIdToGrpcBinderConnectionMap.put(connId, new GrpcBinderConnection(context, connId)); |
||||
connectionIdToGrpcBinderConnectionMap.get(connId).tryConnect(uri); |
||||
} |
||||
|
||||
// Returns true if the packages signature of the 2 UIDs match.
|
||||
// `context` is used to get PackageManager.
|
||||
// Suppress unnecessary internal warnings related to checkSignatures compatibility issue.
|
||||
// BinderTransport code is only used on newer Android platform versions so this is fine.
|
||||
@SuppressWarnings("CheckSignatures") |
||||
// copybara: Add @UsedByNative("jni_utils.cc")
|
||||
static boolean isSignatureMatch(Context context, int uid1, int uid2) { |
||||
int result = context.getPackageManager().checkSignatures(uid1, uid2); |
||||
if (result == PackageManager.SIGNATURE_MATCH) { |
||||
return true; |
||||
} |
||||
Log.e( |
||||
"NativeConnectionHelper", |
||||
"Signatures does not match. checkSignature return value = " + result); |
||||
return false; |
||||
} |
||||
|
||||
// copybara: Add @UsedByNative("jni_utils.cc")
|
||||
static Parcel getEmptyParcel() { |
||||
return Parcel.obtain(); |
||||
} |
||||
} |
@ -1,106 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
|
||||
#ifdef GPR_ANDROID |
||||
|
||||
#include <jni.h> |
||||
#include <unistd.h> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "src/core/ext/transport/binder/client/jni_utils.h" |
||||
#include "src/core/util/crash.h" |
||||
|
||||
#endif |
||||
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
namespace binder { |
||||
|
||||
UntrustedSecurityPolicy::UntrustedSecurityPolicy() = default; |
||||
|
||||
UntrustedSecurityPolicy::~UntrustedSecurityPolicy() = default; |
||||
|
||||
bool UntrustedSecurityPolicy::IsAuthorized(int) { return true; }; |
||||
|
||||
InternalOnlySecurityPolicy::InternalOnlySecurityPolicy() = default; |
||||
|
||||
InternalOnlySecurityPolicy::~InternalOnlySecurityPolicy() = default; |
||||
|
||||
#ifdef GPR_ANDROID |
||||
bool InternalOnlySecurityPolicy::IsAuthorized(int uid) { |
||||
return static_cast<uid_t>(uid) == getuid(); |
||||
} |
||||
#else |
||||
bool InternalOnlySecurityPolicy::IsAuthorized(int) { return false; } |
||||
#endif |
||||
|
||||
#ifdef GPR_ANDROID |
||||
|
||||
namespace { |
||||
JNIEnv* GetEnv(JavaVM* vm) { |
||||
if (vm == nullptr) return nullptr; |
||||
|
||||
JNIEnv* result = nullptr; |
||||
jint attach = vm->AttachCurrentThread(&result, nullptr); |
||||
|
||||
CHECK(JNI_OK == attach); |
||||
CHECK_NE(result, nullptr); |
||||
return result; |
||||
} |
||||
} // namespace
|
||||
|
||||
SameSignatureSecurityPolicy::SameSignatureSecurityPolicy(JavaVM* jvm, |
||||
jobject context) |
||||
: jvm_(jvm) { |
||||
CHECK_NE(jvm, nullptr); |
||||
CHECK_NE(context, nullptr); |
||||
|
||||
JNIEnv* env = GetEnv(jvm_); |
||||
|
||||
// Make sure the context is still valid when IsAuthorized() is called
|
||||
context_ = env->NewGlobalRef(context); |
||||
CHECK_NE(context_, nullptr); |
||||
} |
||||
|
||||
SameSignatureSecurityPolicy::~SameSignatureSecurityPolicy() { |
||||
JNIEnv* env = GetEnv(jvm_); |
||||
env->DeleteLocalRef(context_); |
||||
} |
||||
|
||||
bool SameSignatureSecurityPolicy::IsAuthorized(int uid) { |
||||
JNIEnv* env = GetEnv(jvm_); |
||||
bool result = grpc_binder::IsSignatureMatch(env, context_, getuid(), uid); |
||||
if (result) { |
||||
LOG(INFO) << "uid " << getuid() << " and uid " << uid |
||||
<< " passed SameSignature check"; |
||||
} else { |
||||
LOG(ERROR) << "uid " << getuid() << " and uid " << uid |
||||
<< " failed SameSignature check"; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
#endif |
||||
|
||||
} // namespace binder
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
#endif |
@ -1,40 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_SECURITY_POLICY_SECURITY_POLICY_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_SECURITY_POLICY_SECURITY_POLICY_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
namespace binder { |
||||
|
||||
// This interface is for determining if a connection is allowed to be
|
||||
// established on Android. See https://source.android.com/security/app-sandbox
|
||||
// for more info about UID.
|
||||
class SecurityPolicy { |
||||
public: |
||||
virtual ~SecurityPolicy() = default; |
||||
// Returns true if the UID is authorized to connect.
|
||||
// Must return the same value for the same inputs so callers can safely cache
|
||||
// the result.
|
||||
virtual bool IsAuthorized(int uid) = 0; |
||||
}; |
||||
|
||||
} // namespace binder
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_SECURITY_POLICY_SECURITY_POLICY_H
|
@ -1,249 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "src/core/ext/transport/binder/server/binder_server.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <grpc/grpc.h> |
||||
|
||||
#include <memory> |
||||
#include <string> |
||||
#include <utility> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/memory/memory.h" |
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
#include "src/core/ext/transport/binder/utils/ndk_binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder_android.h" |
||||
#include "src/core/lib/iomgr/exec_ctx.h" |
||||
#include "src/core/lib/transport/error_utils.h" |
||||
#include "src/core/server/server.h" |
||||
|
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
|
||||
#include <jni.h> |
||||
|
||||
extern "C" { |
||||
|
||||
// This will be invoked from
|
||||
// src/core/ext/transport/binder/java/io/grpc/binder/cpp/GrpcCppServerBuilder.java
|
||||
JNIEXPORT jobject JNICALL |
||||
Java_io_grpc_binder_cpp_GrpcCppServerBuilder_GetEndpointBinderInternal__Ljava_lang_String_2( |
||||
JNIEnv* jni_env, jobject, jstring conn_id_jstring) { |
||||
grpc_binder::ndk_util::AIBinder* ai_binder = nullptr; |
||||
|
||||
{ |
||||
// This block is the scope of conn_id c-string
|
||||
jboolean isCopy; |
||||
const char* conn_id = jni_env->GetStringUTFChars(conn_id_jstring, &isCopy); |
||||
ai_binder = static_cast<grpc_binder::ndk_util::AIBinder*>( |
||||
grpc_get_endpoint_binder(std::string(conn_id))); |
||||
if (ai_binder == nullptr) { |
||||
LOG(ERROR) << "Cannot find endpoint binder with connection id = " |
||||
<< conn_id; |
||||
} |
||||
if (isCopy == JNI_TRUE) { |
||||
jni_env->ReleaseStringUTFChars(conn_id_jstring, conn_id); |
||||
} |
||||
} |
||||
|
||||
if (ai_binder == nullptr) { |
||||
return nullptr; |
||||
} |
||||
|
||||
return grpc_binder::ndk_util::AIBinder_toJavaBinder(jni_env, ai_binder); |
||||
} |
||||
} |
||||
|
||||
#endif |
||||
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
namespace binder { |
||||
|
||||
void* GetEndpointBinder(const std::string& service) { |
||||
return grpc_get_endpoint_binder(service); |
||||
} |
||||
|
||||
void AddEndpointBinder(const std::string& service, void* endpoint_binder) { |
||||
grpc_add_endpoint_binder(service, endpoint_binder); |
||||
} |
||||
|
||||
void RemoveEndpointBinder(const std::string& service) { |
||||
grpc_remove_endpoint_binder(service); |
||||
} |
||||
|
||||
} // namespace binder
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
|
||||
static absl::flat_hash_map<std::string, void*>* g_endpoint_binder_pool = |
||||
nullptr; |
||||
|
||||
namespace { |
||||
|
||||
grpc_core::Mutex* GetBinderPoolMutex() { |
||||
static grpc_core::Mutex* mu = new grpc_core::Mutex(); |
||||
return mu; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
void grpc_add_endpoint_binder(const std::string& service, |
||||
void* endpoint_binder) { |
||||
grpc_core::MutexLock lock(GetBinderPoolMutex()); |
||||
if (g_endpoint_binder_pool == nullptr) { |
||||
g_endpoint_binder_pool = new absl::flat_hash_map<std::string, void*>(); |
||||
} |
||||
(*g_endpoint_binder_pool)[service] = endpoint_binder; |
||||
} |
||||
|
||||
void grpc_remove_endpoint_binder(const std::string& service) { |
||||
grpc_core::MutexLock lock(GetBinderPoolMutex()); |
||||
if (g_endpoint_binder_pool == nullptr) { |
||||
return; |
||||
} |
||||
g_endpoint_binder_pool->erase(service); |
||||
} |
||||
|
||||
void* grpc_get_endpoint_binder(const std::string& service) { |
||||
grpc_core::MutexLock lock(GetBinderPoolMutex()); |
||||
if (g_endpoint_binder_pool == nullptr) { |
||||
return nullptr; |
||||
} |
||||
auto iter = g_endpoint_binder_pool->find(service); |
||||
return iter == g_endpoint_binder_pool->end() ? nullptr : iter->second; |
||||
} |
||||
|
||||
namespace grpc_core { |
||||
|
||||
class BinderServerListener : public Server::ListenerInterface { |
||||
public: |
||||
BinderServerListener( |
||||
Server* server, std::string addr, BinderTxReceiverFactory factory, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) |
||||
: server_(server), |
||||
addr_(std::move(addr)), |
||||
factory_(std::move(factory)), |
||||
security_policy_(security_policy) {} |
||||
|
||||
void Start(Server* /*server*/, |
||||
const std::vector<grpc_pollset*>* /*pollsets*/) override { |
||||
tx_receiver_ = factory_( |
||||
[this](transaction_code_t code, grpc_binder::ReadableParcel* parcel, |
||||
int uid) { return OnSetupTransport(code, parcel, uid); }); |
||||
endpoint_binder_ = tx_receiver_->GetRawBinder(); |
||||
grpc_add_endpoint_binder(addr_, endpoint_binder_); |
||||
} |
||||
|
||||
channelz::ListenSocketNode* channelz_listen_socket_node() const override { |
||||
return nullptr; |
||||
} |
||||
|
||||
void SetOnDestroyDone(grpc_closure* on_destroy_done) override { |
||||
on_destroy_done_ = on_destroy_done; |
||||
} |
||||
|
||||
void Orphan() override { Unref(); } |
||||
|
||||
~BinderServerListener() override { |
||||
ExecCtx::Get()->Flush(); |
||||
if (on_destroy_done_) { |
||||
ExecCtx::Run(DEBUG_LOCATION, on_destroy_done_, absl::OkStatus()); |
||||
ExecCtx::Get()->Flush(); |
||||
} |
||||
grpc_remove_endpoint_binder(addr_); |
||||
} |
||||
|
||||
private: |
||||
absl::Status OnSetupTransport(transaction_code_t code, |
||||
grpc_binder::ReadableParcel* parcel, int uid) { |
||||
ExecCtx exec_ctx; |
||||
if (static_cast<grpc_binder::BinderTransportTxCode>(code) != |
||||
grpc_binder::BinderTransportTxCode::SETUP_TRANSPORT) { |
||||
return absl::InvalidArgumentError("Not a SETUP_TRANSPORT request"); |
||||
} |
||||
|
||||
LOG(INFO) << "BinderServerListener calling uid = " << uid; |
||||
if (!security_policy_->IsAuthorized(uid)) { |
||||
// TODO(mingcl): For now we just ignore this unauthorized
|
||||
// SETUP_TRANSPORT transaction and ghost the client. Check if we should
|
||||
// send back a SHUTDOWN_TRANSPORT in this case.
|
||||
return absl::PermissionDeniedError( |
||||
"UID " + std::to_string(uid) + |
||||
" is not allowed to connect to this " |
||||
"server according to security policy."); |
||||
} |
||||
|
||||
int version; |
||||
absl::Status status = parcel->ReadInt32(&version); |
||||
if (!status.ok()) { |
||||
return status; |
||||
} |
||||
LOG(INFO) << "BinderTransport client protocol version = " << version; |
||||
// TODO(mingcl): Make sure we only give client a version that is not newer
|
||||
// than the version they specify. For now, we always tell client that we
|
||||
// only support version=1.
|
||||
std::unique_ptr<grpc_binder::Binder> client_binder{}; |
||||
status = parcel->ReadBinder(&client_binder); |
||||
if (!status.ok()) { |
||||
return status; |
||||
} |
||||
if (!client_binder) { |
||||
return absl::InvalidArgumentError("NULL binder read from the parcel"); |
||||
} |
||||
client_binder->Initialize(); |
||||
// Finish the second half of SETUP_TRANSPORT in
|
||||
// grpc_create_binder_transport_server().
|
||||
Transport* server_transport = grpc_create_binder_transport_server( |
||||
std::move(client_binder), security_policy_); |
||||
CHECK(server_transport); |
||||
grpc_error_handle error = server_->SetupTransport( |
||||
server_transport, nullptr, server_->channel_args(), nullptr); |
||||
return grpc_error_to_absl_status(error); |
||||
} |
||||
|
||||
Server* server_; |
||||
grpc_closure* on_destroy_done_ = nullptr; |
||||
std::string addr_; |
||||
BinderTxReceiverFactory factory_; |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy_; |
||||
void* endpoint_binder_ = nullptr; |
||||
std::unique_ptr<grpc_binder::TransactionReceiver> tx_receiver_; |
||||
}; |
||||
|
||||
bool AddBinderPort(const std::string& addr, grpc_server* server, |
||||
BinderTxReceiverFactory factory, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) { |
||||
// TODO(mingcl): Check if the addr is valid here after binder address resolver
|
||||
// related code are merged.
|
||||
const std::string kBinderUriScheme = "binder:"; |
||||
if (addr.compare(0, kBinderUriScheme.size(), kBinderUriScheme) != 0) { |
||||
return false; |
||||
} |
||||
std::string conn_id = addr.substr(kBinderUriScheme.size()); |
||||
Server* core_server = Server::FromC(server); |
||||
core_server->AddListener(MakeOrphanable<BinderServerListener>( |
||||
core_server, conn_id, std::move(factory), security_policy)); |
||||
return true; |
||||
} |
||||
|
||||
} // namespace grpc_core
|
||||
#endif |
@ -1,65 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_SERVER_BINDER_SERVER_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_SERVER_BINDER_SERVER_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
|
||||
#include <string> |
||||
|
||||
#include "absl/container/flat_hash_map.h" |
||||
#include "absl/status/status.h" |
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/lib/iomgr/exec_ctx.h" |
||||
#include "src/core/lib/transport/error_utils.h" |
||||
#include "src/core/server/server.h" |
||||
|
||||
// TODO(waynetu): This is part of the public API and should be moved to the
|
||||
// include/ folder.
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
namespace binder { |
||||
|
||||
void* GetEndpointBinder(const std::string& service); |
||||
void AddEndpointBinder(const std::string& service, void* endpoint_binder); |
||||
void RemoveEndpointBinder(const std::string& service); |
||||
|
||||
} // namespace binder
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
|
||||
void grpc_add_endpoint_binder(const std::string& service, |
||||
void* endpoint_binder); |
||||
void grpc_remove_endpoint_binder(const std::string& service); |
||||
void* grpc_get_endpoint_binder(const std::string& service); |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// Consume a callback, produce a transaction listener. This is used to perform
|
||||
// testing in non-Android environments where the actual binder is not available.
|
||||
using BinderTxReceiverFactory = |
||||
std::function<std::unique_ptr<grpc_binder::TransactionReceiver>( |
||||
grpc_binder::TransactionReceiver::OnTransactCb)>; |
||||
|
||||
bool AddBinderPort(const std::string& addr, grpc_server* server, |
||||
BinderTxReceiverFactory factory, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_SERVER_BINDER_SERVER_H
|
@ -1,71 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 <grpc/support/port_platform.h> |
||||
|
||||
#include "absl/log/check.h" |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
#include <grpcpp/security/server_credentials.h> |
||||
|
||||
#include "src/core/ext/transport/binder/server/binder_server.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder_android.h" |
||||
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
|
||||
namespace { |
||||
|
||||
class BinderServerCredentialsImpl final : public ServerCredentials { |
||||
public: |
||||
explicit BinderServerCredentialsImpl( |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) |
||||
: ServerCredentials(nullptr), security_policy_(security_policy) {} |
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
int AddPortToServer(const std::string& addr, grpc_server* server) override { |
||||
return grpc_core::AddBinderPort( |
||||
std::string(addr), server, |
||||
[](grpc_binder::TransactionReceiver::OnTransactCb transact_cb) { |
||||
return std::make_unique<grpc_binder::TransactionReceiverAndroid>( |
||||
nullptr, std::move(transact_cb)); |
||||
}, |
||||
security_policy_); |
||||
} |
||||
#else |
||||
int AddPortToServer(const std::string& /*addr*/, |
||||
grpc_server* /*server*/) override { |
||||
return 0; |
||||
} |
||||
#endif // GPR_SUPPORT_BINDER_TRANSPORT
|
||||
|
||||
private: |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy_; |
||||
}; |
||||
|
||||
} // namespace
|
||||
|
||||
std::shared_ptr<ServerCredentials> BinderServerCredentials( |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) { |
||||
CHECK_NE(security_policy, nullptr); |
||||
return std::shared_ptr<ServerCredentials>( |
||||
new BinderServerCredentialsImpl(security_policy)); |
||||
} |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
#endif |
@ -1,117 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_TRANSPORT_BINDER_STREAM_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_TRANSPORT_BINDER_STREAM_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
|
||||
struct RecvInitialMetadataArgs { |
||||
grpc_binder_stream* stream; |
||||
grpc_binder_transport* transport; |
||||
int tx_code; |
||||
absl::StatusOr<grpc_binder::Metadata> initial_metadata; |
||||
}; |
||||
|
||||
struct RecvMessageArgs { |
||||
grpc_binder_stream* stream; |
||||
grpc_binder_transport* transport; |
||||
int tx_code; |
||||
absl::StatusOr<std::string> message; |
||||
}; |
||||
|
||||
struct RecvTrailingMetadataArgs { |
||||
grpc_binder_stream* stream; |
||||
grpc_binder_transport* transport; |
||||
int tx_code; |
||||
absl::StatusOr<grpc_binder::Metadata> trailing_metadata; |
||||
int status; |
||||
}; |
||||
|
||||
struct RegisterStreamArgs { |
||||
grpc_binder_stream* stream; |
||||
grpc_binder_transport* transport; |
||||
}; |
||||
|
||||
// TODO(mingcl): Figure out if we want to use class instead of struct here
|
||||
struct grpc_binder_stream { |
||||
// server_data will be null for client, and for server it will be whatever
|
||||
// passed in to the accept_stream_fn callback by client.
|
||||
grpc_binder_stream(grpc_binder_transport* t, grpc_stream_refcount* refcount, |
||||
const void* /*server_data*/, grpc_core::Arena* arena, |
||||
int tx_code, bool is_client) |
||||
: t(t), |
||||
refcount(refcount), |
||||
arena(arena), |
||||
tx_code(tx_code), |
||||
is_client(is_client), |
||||
is_closed(false) { |
||||
recv_initial_metadata_args.stream = this; |
||||
recv_initial_metadata_args.transport = t; |
||||
recv_message_args.stream = this; |
||||
recv_message_args.transport = t; |
||||
recv_trailing_metadata_args.stream = this; |
||||
recv_trailing_metadata_args.transport = t; |
||||
} |
||||
|
||||
~grpc_binder_stream() { |
||||
if (destroy_stream_then_closure != nullptr) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, destroy_stream_then_closure, |
||||
absl::OkStatus()); |
||||
} |
||||
} |
||||
|
||||
int GetTxCode() const { return tx_code; } |
||||
|
||||
grpc_binder_transport* t; |
||||
grpc_stream_refcount* refcount; |
||||
grpc_core::Arena* arena; |
||||
int tx_code; |
||||
const bool is_client; |
||||
bool is_closed; |
||||
|
||||
grpc_closure* destroy_stream_then_closure = nullptr; |
||||
grpc_closure destroy_stream; |
||||
|
||||
// The reason why this stream is cancelled and closed.
|
||||
grpc_error_handle cancel_self_error; |
||||
|
||||
grpc_closure recv_initial_metadata_closure; |
||||
RecvInitialMetadataArgs recv_initial_metadata_args; |
||||
grpc_closure recv_message_closure; |
||||
RecvMessageArgs recv_message_args; |
||||
grpc_closure recv_trailing_metadata_closure; |
||||
RecvTrailingMetadataArgs recv_trailing_metadata_args; |
||||
|
||||
grpc_closure register_stream_closure; |
||||
RegisterStreamArgs register_stream_args; |
||||
|
||||
// We store these fields passed from op batch, in order to access them through
|
||||
// grpc_binder_stream
|
||||
grpc_metadata_batch* recv_initial_metadata; |
||||
grpc_closure* recv_initial_metadata_ready = nullptr; |
||||
bool* trailing_metadata_available = nullptr; |
||||
absl::optional<grpc_core::SliceBuffer>* recv_message; |
||||
grpc_closure* recv_message_ready = nullptr; |
||||
bool* call_failed_before_recv_message = nullptr; |
||||
grpc_metadata_batch* recv_trailing_metadata; |
||||
grpc_closure* recv_trailing_metadata_finished = nullptr; |
||||
|
||||
bool trailing_metadata_sent = false; |
||||
bool need_to_call_trailing_metadata_callback = false; |
||||
}; |
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_TRANSPORT_BINDER_STREAM_H
|
@ -1,758 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <cstdint> |
||||
#include <memory> |
||||
#include <string> |
||||
#include <utility> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/memory/memory.h" |
||||
#include "absl/strings/str_cat.h" |
||||
#include "absl/strings/substitute.h" |
||||
#include "src/core/ext/transport/binder/transport/binder_stream.h" |
||||
#include "src/core/ext/transport/binder/utils/transport_stream_receiver.h" |
||||
#include "src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_reader.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_reader_impl.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_writer.h" |
||||
#include "src/core/lib/event_engine/default_event_engine.h" |
||||
#include "src/core/lib/iomgr/exec_ctx.h" |
||||
#include "src/core/lib/slice/slice_internal.h" |
||||
#include "src/core/lib/transport/error_utils.h" |
||||
#include "src/core/lib/transport/metadata_batch.h" |
||||
#include "src/core/lib/transport/transport.h" |
||||
#include "src/core/util/crash.h" |
||||
|
||||
#ifndef NDEBUG |
||||
static void grpc_binder_stream_ref(grpc_binder_stream* s, const char* reason) { |
||||
grpc_stream_ref(s->refcount, reason); |
||||
} |
||||
static void grpc_binder_stream_unref(grpc_binder_stream* s, |
||||
const char* reason) { |
||||
grpc_stream_unref(s->refcount, reason); |
||||
} |
||||
static void grpc_binder_ref_transport(grpc_binder_transport* t, |
||||
const char* reason, const char* file, |
||||
int line) { |
||||
t->refs.Ref(grpc_core::DebugLocation(file, line), reason); |
||||
} |
||||
static void grpc_binder_unref_transport(grpc_binder_transport* t, |
||||
const char* reason, const char* file, |
||||
int line) { |
||||
if (t->refs.Unref(grpc_core::DebugLocation(file, line), reason)) { |
||||
delete t; |
||||
} |
||||
} |
||||
#else |
||||
static void grpc_binder_stream_ref(grpc_binder_stream* s) { |
||||
grpc_stream_ref(s->refcount); |
||||
} |
||||
static void grpc_binder_stream_unref(grpc_binder_stream* s) { |
||||
grpc_stream_unref(s->refcount); |
||||
} |
||||
static void grpc_binder_ref_transport(grpc_binder_transport* t) { |
||||
t->refs.Ref(); |
||||
} |
||||
static void grpc_binder_unref_transport(grpc_binder_transport* t) { |
||||
if (t->refs.Unref()) { |
||||
delete t; |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
#ifndef NDEBUG |
||||
#define GRPC_BINDER_STREAM_REF(stream, reason) \ |
||||
grpc_binder_stream_ref(stream, reason) |
||||
#define GRPC_BINDER_STREAM_UNREF(stream, reason) \ |
||||
grpc_binder_stream_unref(stream, reason) |
||||
#define GRPC_BINDER_REF_TRANSPORT(t, r) \ |
||||
grpc_binder_ref_transport(t, r, __FILE__, __LINE__) |
||||
#define GRPC_BINDER_UNREF_TRANSPORT(t, r) \ |
||||
grpc_binder_unref_transport(t, r, __FILE__, __LINE__) |
||||
#else |
||||
#define GRPC_BINDER_STREAM_REF(stream, reason) grpc_binder_stream_ref(stream) |
||||
#define GRPC_BINDER_STREAM_UNREF(stream, reason) \ |
||||
grpc_binder_stream_unref(stream) |
||||
#define GRPC_BINDER_REF_TRANSPORT(t, r) grpc_binder_ref_transport(t) |
||||
#define GRPC_BINDER_UNREF_TRANSPORT(t, r) grpc_binder_unref_transport(t) |
||||
#endif |
||||
|
||||
static void register_stream_locked(void* arg, grpc_error_handle /*error*/) { |
||||
RegisterStreamArgs* args = static_cast<RegisterStreamArgs*>(arg); |
||||
args->transport->registered_stream[args->stream->GetTxCode()] = args->stream; |
||||
} |
||||
|
||||
void grpc_binder_transport::InitStream(grpc_stream* gs, |
||||
grpc_stream_refcount* refcount, |
||||
const void* server_data, |
||||
grpc_core::Arena* arena) { |
||||
LOG(INFO) << __func__ << " = " << this << " " << gs << " " << refcount << " " |
||||
<< server_data << " " << arena; |
||||
// Note that this function is not locked and may be invoked concurrently
|
||||
new (gs) grpc_binder_stream(this, refcount, server_data, arena, |
||||
NewStreamTxCode(), is_client); |
||||
|
||||
// `grpc_binder_transport::registered_stream` should only be updated in
|
||||
// combiner
|
||||
grpc_binder_stream* stream = reinterpret_cast<grpc_binder_stream*>(gs); |
||||
stream->register_stream_args.stream = stream; |
||||
stream->register_stream_args.transport = this; |
||||
grpc_core::ExecCtx exec_ctx; |
||||
combiner->Run(GRPC_CLOSURE_INIT(&stream->register_stream_closure, |
||||
register_stream_locked, |
||||
&stream->register_stream_args, nullptr), |
||||
absl::OkStatus()); |
||||
} |
||||
|
||||
static void AssignMetadata(grpc_metadata_batch* mb, |
||||
const grpc_binder::Metadata& md) { |
||||
mb->Clear(); |
||||
for (auto& p : md) { |
||||
mb->Append(p.first, grpc_core::Slice::FromCopiedString(p.second), |
||||
[&](absl::string_view error, const grpc_core::Slice&) { |
||||
VLOG(2) << "Failed to parse metadata: " |
||||
<< "key=" << p.first << " error=" << error; |
||||
}); |
||||
} |
||||
} |
||||
|
||||
static void cancel_stream_locked(grpc_binder_transport* transport, |
||||
grpc_binder_stream* stream, |
||||
grpc_error_handle error) { |
||||
LOG(INFO) << "cancel_stream_locked"; |
||||
if (!stream->is_closed) { |
||||
CHECK(stream->cancel_self_error.ok()); |
||||
stream->is_closed = true; |
||||
stream->cancel_self_error = error; |
||||
transport->transport_stream_receiver->CancelStream(stream->tx_code); |
||||
transport->registered_stream.erase(stream->tx_code); |
||||
if (stream->recv_initial_metadata_ready != nullptr) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, |
||||
stream->recv_initial_metadata_ready, error); |
||||
stream->recv_initial_metadata_ready = nullptr; |
||||
stream->recv_initial_metadata = nullptr; |
||||
stream->trailing_metadata_available = nullptr; |
||||
} |
||||
if (stream->recv_message_ready != nullptr) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, stream->recv_message_ready, |
||||
error); |
||||
stream->recv_message_ready = nullptr; |
||||
stream->recv_message->reset(); |
||||
stream->recv_message = nullptr; |
||||
stream->call_failed_before_recv_message = nullptr; |
||||
} |
||||
if (stream->recv_trailing_metadata_finished != nullptr) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, |
||||
stream->recv_trailing_metadata_finished, error); |
||||
stream->recv_trailing_metadata_finished = nullptr; |
||||
stream->recv_trailing_metadata = nullptr; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static bool ContainsAuthorityAndPath(const grpc_binder::Metadata& metadata) { |
||||
bool has_authority = false; |
||||
bool has_path = false; |
||||
for (const auto& kv : metadata) { |
||||
if (kv.first == ":authority") { |
||||
has_authority = true; |
||||
} |
||||
if (kv.first == ":path") { |
||||
has_path = true; |
||||
} |
||||
} |
||||
return has_authority && has_path; |
||||
} |
||||
|
||||
static void recv_initial_metadata_locked(void* arg, |
||||
grpc_error_handle /*error*/) { |
||||
RecvInitialMetadataArgs* args = static_cast<RecvInitialMetadataArgs*>(arg); |
||||
grpc_binder_stream* stream = args->stream; |
||||
|
||||
LOG(INFO) << "recv_initial_metadata_locked is_client = " << stream->is_client |
||||
<< " is_closed = " << stream->is_closed; |
||||
|
||||
if (!stream->is_closed) { |
||||
grpc_error_handle error = [&] { |
||||
CHECK(stream->recv_initial_metadata); |
||||
CHECK(stream->recv_initial_metadata_ready); |
||||
if (!args->initial_metadata.ok()) { |
||||
LOG(ERROR) << "Failed to parse initial metadata"; |
||||
return absl_status_to_grpc_error(args->initial_metadata.status()); |
||||
} |
||||
if (!stream->is_client) { |
||||
// For server, we expect :authority and :path in initial metadata.
|
||||
if (!ContainsAuthorityAndPath(*args->initial_metadata)) { |
||||
return GRPC_ERROR_CREATE( |
||||
"Missing :authority or :path in initial metadata"); |
||||
} |
||||
} |
||||
AssignMetadata(stream->recv_initial_metadata, *args->initial_metadata); |
||||
return absl::OkStatus(); |
||||
}(); |
||||
if (stream->t->registered_method_matcher_cb != nullptr) { |
||||
stream->t->registered_method_matcher_cb( |
||||
stream->t->accept_stream_user_data, stream->recv_initial_metadata); |
||||
} |
||||
grpc_closure* cb = stream->recv_initial_metadata_ready; |
||||
stream->recv_initial_metadata_ready = nullptr; |
||||
stream->recv_initial_metadata = nullptr; |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, cb, error); |
||||
} |
||||
GRPC_BINDER_STREAM_UNREF(stream, "recv_initial_metadata"); |
||||
} |
||||
|
||||
static void recv_message_locked(void* arg, grpc_error_handle /*error*/) { |
||||
RecvMessageArgs* args = static_cast<RecvMessageArgs*>(arg); |
||||
grpc_binder_stream* stream = args->stream; |
||||
|
||||
LOG(INFO) << "recv_message_locked is_client = " << stream->is_client |
||||
<< " is_closed = " << stream->is_closed; |
||||
|
||||
if (!stream->is_closed) { |
||||
grpc_error_handle error = [&] { |
||||
CHECK(stream->recv_message); |
||||
CHECK(stream->recv_message_ready); |
||||
if (!args->message.ok()) { |
||||
LOG(ERROR) << "Failed to receive message"; |
||||
if (args->message.status().message() == |
||||
grpc_binder::TransportStreamReceiver:: |
||||
kGrpcBinderTransportCancelledGracefully) { |
||||
LOG(ERROR) << "message cancelled gracefully"; |
||||
// Cancelled because we've already received trailing metadata.
|
||||
// It's not an error in this case.
|
||||
return absl::OkStatus(); |
||||
} else { |
||||
return absl_status_to_grpc_error(args->message.status()); |
||||
} |
||||
} |
||||
grpc_core::SliceBuffer buf; |
||||
buf.Append(grpc_core::Slice(grpc_slice_from_cpp_string(*args->message))); |
||||
*stream->recv_message = std::move(buf); |
||||
return absl::OkStatus(); |
||||
}(); |
||||
|
||||
if (!error.ok() && stream->call_failed_before_recv_message != nullptr) { |
||||
*stream->call_failed_before_recv_message = true; |
||||
} |
||||
grpc_closure* cb = stream->recv_message_ready; |
||||
stream->recv_message_ready = nullptr; |
||||
stream->recv_message = nullptr; |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, cb, error); |
||||
} |
||||
|
||||
GRPC_BINDER_STREAM_UNREF(stream, "recv_message"); |
||||
} |
||||
|
||||
static void recv_trailing_metadata_locked(void* arg, |
||||
grpc_error_handle /*error*/) { |
||||
RecvTrailingMetadataArgs* args = static_cast<RecvTrailingMetadataArgs*>(arg); |
||||
grpc_binder_stream* stream = args->stream; |
||||
|
||||
LOG(INFO) << "recv_trailing_metadata_locked is_client = " << stream->is_client |
||||
<< " is_closed = " << stream->is_closed; |
||||
|
||||
if (!stream->is_closed) { |
||||
grpc_error_handle error = [&] { |
||||
CHECK(stream->recv_trailing_metadata); |
||||
CHECK(stream->recv_trailing_metadata_finished); |
||||
if (!args->trailing_metadata.ok()) { |
||||
LOG(ERROR) << "Failed to receive trailing metadata"; |
||||
return absl_status_to_grpc_error(args->trailing_metadata.status()); |
||||
} |
||||
if (!stream->is_client) { |
||||
// Client will not send non-empty trailing metadata.
|
||||
if (!args->trailing_metadata.value().empty()) { |
||||
LOG(ERROR) << "Server receives non-empty trailing metadata."; |
||||
return absl::CancelledError(); |
||||
} |
||||
} else { |
||||
AssignMetadata(stream->recv_trailing_metadata, |
||||
*args->trailing_metadata); |
||||
// Append status to metadata
|
||||
// TODO(b/192208695): See if we can avoid to manually put status
|
||||
// code into the header
|
||||
LOG(INFO) << "status = " << args->status; |
||||
stream->recv_trailing_metadata->Set( |
||||
grpc_core::GrpcStatusMetadata(), |
||||
static_cast<grpc_status_code>(args->status)); |
||||
} |
||||
return absl::OkStatus(); |
||||
}(); |
||||
|
||||
if (stream->is_client || stream->trailing_metadata_sent) { |
||||
grpc_closure* cb = stream->recv_trailing_metadata_finished; |
||||
stream->recv_trailing_metadata_finished = nullptr; |
||||
stream->recv_trailing_metadata = nullptr; |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, cb, error); |
||||
} else { |
||||
// According to transport explaineer - "Server extra: This op shouldn't
|
||||
// actually be considered complete until the server has also sent trailing
|
||||
// metadata to provide the other side with final status"
|
||||
//
|
||||
// We haven't sent trailing metadata yet, so we have to delay completing
|
||||
// the recv_trailing_metadata callback.
|
||||
stream->need_to_call_trailing_metadata_callback = true; |
||||
} |
||||
} |
||||
GRPC_BINDER_STREAM_UNREF(stream, "recv_trailing_metadata"); |
||||
} |
||||
|
||||
namespace grpc_binder { |
||||
namespace { |
||||
|
||||
class MetadataEncoder { |
||||
public: |
||||
MetadataEncoder(bool is_client, Transaction* tx, Metadata* init_md) |
||||
: is_client_(is_client), tx_(tx), init_md_(init_md) {} |
||||
|
||||
void Encode(const grpc_core::Slice& key_slice, |
||||
const grpc_core::Slice& value_slice) { |
||||
absl::string_view key = key_slice.as_string_view(); |
||||
absl::string_view value = value_slice.as_string_view(); |
||||
init_md_->emplace_back(std::string(key), std::string(value)); |
||||
} |
||||
|
||||
void Encode(grpc_core::HttpPathMetadata, const grpc_core::Slice& value) { |
||||
// TODO(b/192208403): Figure out if it is correct to simply drop '/'
|
||||
// prefix and treat it as rpc method name
|
||||
CHECK(value[0] == '/'); |
||||
std::string path = std::string(value.as_string_view().substr(1)); |
||||
|
||||
// Only client send method ref.
|
||||
CHECK(is_client_); |
||||
tx_->SetMethodRef(path); |
||||
} |
||||
|
||||
void Encode(grpc_core::GrpcStatusMetadata, grpc_status_code status) { |
||||
LOG(INFO) << "send trailing metadata status = " << status; |
||||
tx_->SetStatus(status); |
||||
} |
||||
|
||||
template <typename Trait> |
||||
void Encode(Trait, const typename Trait::ValueType& value) { |
||||
init_md_->emplace_back(std::string(Trait::key()), |
||||
std::string(Trait::Encode(value).as_string_view())); |
||||
} |
||||
|
||||
private: |
||||
const bool is_client_; |
||||
Transaction* const tx_; |
||||
Metadata* const init_md_; |
||||
}; |
||||
|
||||
} // namespace
|
||||
} // namespace grpc_binder
|
||||
|
||||
static void accept_stream_locked(void* gt, grpc_error_handle /*error*/) { |
||||
grpc_binder_transport* transport = static_cast<grpc_binder_transport*>(gt); |
||||
if (transport->accept_stream_fn) { |
||||
LOG(INFO) << "Accepting a stream"; |
||||
// must pass in a non-null value.
|
||||
(*transport->accept_stream_fn)(transport->accept_stream_user_data, |
||||
transport, transport); |
||||
} else { |
||||
++transport->accept_stream_fn_called_count_; |
||||
LOG(INFO) << "accept_stream_fn not set, current count = " |
||||
<< transport->accept_stream_fn_called_count_; |
||||
} |
||||
} |
||||
|
||||
static void perform_stream_op_locked(void* stream_op, |
||||
grpc_error_handle /*error*/) { |
||||
grpc_transport_stream_op_batch* op = |
||||
static_cast<grpc_transport_stream_op_batch*>(stream_op); |
||||
grpc_binder_stream* stream = |
||||
static_cast<grpc_binder_stream*>(op->handler_private.extra_arg); |
||||
grpc_binder_transport* transport = stream->t; |
||||
if (op->cancel_stream) { |
||||
// TODO(waynetu): Is this true?
|
||||
CHECK(!op->send_initial_metadata && !op->send_message && |
||||
!op->send_trailing_metadata && !op->recv_initial_metadata && |
||||
!op->recv_message && !op->recv_trailing_metadata); |
||||
LOG(INFO) << "cancel_stream is_client = " << stream->is_client; |
||||
if (!stream->is_client) { |
||||
// Send trailing metadata to inform the other end about the cancellation,
|
||||
// regardless if we'd already done that or not.
|
||||
auto cancel_tx = std::make_unique<grpc_binder::Transaction>( |
||||
stream->GetTxCode(), transport->is_client); |
||||
cancel_tx->SetSuffix(grpc_binder::Metadata{}); |
||||
cancel_tx->SetStatus(1); |
||||
(void)transport->wire_writer->RpcCall(std::move(cancel_tx)); |
||||
} |
||||
cancel_stream_locked(transport, stream, |
||||
op->payload->cancel_stream.cancel_error); |
||||
if (op->on_complete != nullptr) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, op->on_complete, |
||||
absl::OkStatus()); |
||||
} |
||||
GRPC_BINDER_STREAM_UNREF(stream, "perform_stream_op"); |
||||
return; |
||||
} |
||||
|
||||
if (stream->is_closed) { |
||||
if (op->send_message) { |
||||
// Reset the send_message payload to prevent memory leaks.
|
||||
op->payload->send_message.send_message->Clear(); |
||||
} |
||||
if (op->recv_initial_metadata) { |
||||
grpc_core::ExecCtx::Run( |
||||
DEBUG_LOCATION, |
||||
op->payload->recv_initial_metadata.recv_initial_metadata_ready, |
||||
stream->cancel_self_error); |
||||
} |
||||
if (op->recv_message) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, |
||||
op->payload->recv_message.recv_message_ready, |
||||
stream->cancel_self_error); |
||||
} |
||||
if (op->recv_trailing_metadata) { |
||||
grpc_core::ExecCtx::Run( |
||||
DEBUG_LOCATION, |
||||
op->payload->recv_trailing_metadata.recv_trailing_metadata_ready, |
||||
stream->cancel_self_error); |
||||
} |
||||
if (op->on_complete != nullptr) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, op->on_complete, |
||||
stream->cancel_self_error); |
||||
} |
||||
GRPC_BINDER_STREAM_UNREF(stream, "perform_stream_op"); |
||||
return; |
||||
} |
||||
|
||||
int tx_code = stream->tx_code; |
||||
auto tx = |
||||
std::make_unique<grpc_binder::Transaction>(tx_code, transport->is_client); |
||||
|
||||
if (op->send_initial_metadata) { |
||||
LOG(INFO) << "send_initial_metadata"; |
||||
grpc_binder::Metadata init_md; |
||||
auto batch = op->payload->send_initial_metadata.send_initial_metadata; |
||||
|
||||
grpc_binder::MetadataEncoder encoder(transport->is_client, tx.get(), |
||||
&init_md); |
||||
batch->Encode(&encoder); |
||||
tx->SetPrefix(init_md); |
||||
} |
||||
if (op->send_message) { |
||||
LOG(INFO) << "send_message"; |
||||
tx->SetData(op->payload->send_message.send_message->JoinIntoString()); |
||||
} |
||||
|
||||
if (op->send_trailing_metadata) { |
||||
LOG(INFO) << "send_trailing_metadata"; |
||||
auto batch = op->payload->send_trailing_metadata.send_trailing_metadata; |
||||
grpc_binder::Metadata trailing_metadata; |
||||
|
||||
grpc_binder::MetadataEncoder encoder(transport->is_client, tx.get(), |
||||
&trailing_metadata); |
||||
batch->Encode(&encoder); |
||||
|
||||
// TODO(mingcl): Will we ever has key-value pair here? According to
|
||||
// wireformat client suffix data is always empty.
|
||||
tx->SetSuffix(trailing_metadata); |
||||
} |
||||
if (op->recv_initial_metadata) { |
||||
LOG(INFO) << "recv_initial_metadata"; |
||||
stream->recv_initial_metadata_ready = |
||||
op->payload->recv_initial_metadata.recv_initial_metadata_ready; |
||||
stream->recv_initial_metadata = |
||||
op->payload->recv_initial_metadata.recv_initial_metadata; |
||||
stream->trailing_metadata_available = |
||||
op->payload->recv_initial_metadata.trailing_metadata_available; |
||||
GRPC_BINDER_STREAM_REF(stream, "recv_initial_metadata"); |
||||
transport->transport_stream_receiver->RegisterRecvInitialMetadata( |
||||
tx_code, [tx_code, stream, transport]( |
||||
absl::StatusOr<grpc_binder::Metadata> initial_metadata) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
stream->recv_initial_metadata_args.tx_code = tx_code; |
||||
stream->recv_initial_metadata_args.initial_metadata = |
||||
std::move(initial_metadata); |
||||
transport->combiner->Run( |
||||
GRPC_CLOSURE_INIT(&stream->recv_initial_metadata_closure, |
||||
recv_initial_metadata_locked, |
||||
&stream->recv_initial_metadata_args, nullptr), |
||||
absl::OkStatus()); |
||||
}); |
||||
} |
||||
if (op->recv_message) { |
||||
LOG(INFO) << "recv_message"; |
||||
stream->recv_message_ready = op->payload->recv_message.recv_message_ready; |
||||
stream->recv_message = op->payload->recv_message.recv_message; |
||||
stream->call_failed_before_recv_message = |
||||
op->payload->recv_message.call_failed_before_recv_message; |
||||
if (op->payload->recv_message.flags != nullptr) { |
||||
*op->payload->recv_message.flags = 0; |
||||
} |
||||
GRPC_BINDER_STREAM_REF(stream, "recv_message"); |
||||
transport->transport_stream_receiver->RegisterRecvMessage( |
||||
tx_code, |
||||
[tx_code, stream, transport](absl::StatusOr<std::string> message) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
stream->recv_message_args.tx_code = tx_code; |
||||
stream->recv_message_args.message = std::move(message); |
||||
transport->combiner->Run( |
||||
GRPC_CLOSURE_INIT(&stream->recv_message_closure, |
||||
recv_message_locked, &stream->recv_message_args, |
||||
nullptr), |
||||
absl::OkStatus()); |
||||
}); |
||||
} |
||||
if (op->recv_trailing_metadata) { |
||||
LOG(INFO) << "recv_trailing_metadata"; |
||||
stream->recv_trailing_metadata_finished = |
||||
op->payload->recv_trailing_metadata.recv_trailing_metadata_ready; |
||||
stream->recv_trailing_metadata = |
||||
op->payload->recv_trailing_metadata.recv_trailing_metadata; |
||||
GRPC_BINDER_STREAM_REF(stream, "recv_trailing_metadata"); |
||||
transport->transport_stream_receiver->RegisterRecvTrailingMetadata( |
||||
tx_code, [tx_code, stream, transport]( |
||||
absl::StatusOr<grpc_binder::Metadata> trailing_metadata, |
||||
int status) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
stream->recv_trailing_metadata_args.tx_code = tx_code; |
||||
stream->recv_trailing_metadata_args.trailing_metadata = |
||||
std::move(trailing_metadata); |
||||
stream->recv_trailing_metadata_args.status = status; |
||||
transport->combiner->Run( |
||||
GRPC_CLOSURE_INIT(&stream->recv_trailing_metadata_closure, |
||||
recv_trailing_metadata_locked, |
||||
&stream->recv_trailing_metadata_args, nullptr), |
||||
absl::OkStatus()); |
||||
}); |
||||
} |
||||
// Only send transaction when there's a send op presented.
|
||||
absl::Status status; |
||||
if (op->send_initial_metadata || op->send_message || |
||||
op->send_trailing_metadata) { |
||||
status = transport->wire_writer->RpcCall(std::move(tx)); |
||||
if (!stream->is_client && op->send_trailing_metadata) { |
||||
stream->trailing_metadata_sent = true; |
||||
// According to transport explaineer - "Server extra: This op shouldn't
|
||||
// actually be considered complete until the server has also sent trailing
|
||||
// metadata to provide the other side with final status"
|
||||
//
|
||||
// Because we've done sending trailing metadata here, we can safely
|
||||
// complete the recv_trailing_metadata callback here.
|
||||
if (stream->need_to_call_trailing_metadata_callback) { |
||||
grpc_closure* cb = stream->recv_trailing_metadata_finished; |
||||
stream->recv_trailing_metadata_finished = nullptr; |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, cb, absl::OkStatus()); |
||||
stream->need_to_call_trailing_metadata_callback = false; |
||||
} |
||||
} |
||||
} |
||||
// Note that this should only be scheduled when all non-recv ops are
|
||||
// completed
|
||||
if (op->on_complete != nullptr) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, op->on_complete, |
||||
absl_status_to_grpc_error(status)); |
||||
LOG(INFO) << "on_complete closure scheduled"; |
||||
} |
||||
GRPC_BINDER_STREAM_UNREF(stream, "perform_stream_op"); |
||||
} |
||||
|
||||
void grpc_binder_transport::PerformStreamOp( |
||||
grpc_stream* gs, grpc_transport_stream_op_batch* op) { |
||||
grpc_binder_stream* stream = reinterpret_cast<grpc_binder_stream*>(gs); |
||||
LOG(INFO) << __func__ << " = " << this << " " << gs << " " << op |
||||
<< " is_client = " << stream->is_client; |
||||
GRPC_BINDER_STREAM_REF(stream, "perform_stream_op"); |
||||
op->handler_private.extra_arg = stream; |
||||
combiner->Run(GRPC_CLOSURE_INIT(&op->handler_private.closure, |
||||
perform_stream_op_locked, op, nullptr), |
||||
absl::OkStatus()); |
||||
} |
||||
|
||||
static void close_transport_locked(grpc_binder_transport* transport) { |
||||
transport->state_tracker.SetState( |
||||
GRPC_CHANNEL_SHUTDOWN, absl::OkStatus(), |
||||
"transport closed due to disconnection/goaway"); |
||||
while (!transport->registered_stream.empty()) { |
||||
cancel_stream_locked( |
||||
transport, transport->registered_stream.begin()->second, |
||||
grpc_error_set_int(GRPC_ERROR_CREATE("transport closed"), |
||||
grpc_core::StatusIntProperty::kRpcStatus, |
||||
GRPC_STATUS_UNAVAILABLE)); |
||||
} |
||||
} |
||||
|
||||
static void perform_transport_op_locked(void* transport_op, |
||||
grpc_error_handle /*error*/) { |
||||
grpc_transport_op* op = static_cast<grpc_transport_op*>(transport_op); |
||||
grpc_binder_transport* transport = |
||||
static_cast<grpc_binder_transport*>(op->handler_private.extra_arg); |
||||
// TODO(waynetu): Should we lock here to avoid data race?
|
||||
if (op->start_connectivity_watch != nullptr) { |
||||
transport->state_tracker.AddWatcher( |
||||
op->start_connectivity_watch_state, |
||||
std::move(op->start_connectivity_watch)); |
||||
} |
||||
if (op->stop_connectivity_watch != nullptr) { |
||||
transport->state_tracker.RemoveWatcher(op->stop_connectivity_watch); |
||||
} |
||||
if (op->set_accept_stream) { |
||||
transport->accept_stream_user_data = op->set_accept_stream_user_data; |
||||
transport->accept_stream_fn = op->set_accept_stream_fn; |
||||
transport->registered_method_matcher_cb = |
||||
op->set_registered_method_matcher_fn; |
||||
VLOG(2) << "accept_stream_fn_called_count_ = " |
||||
<< transport->accept_stream_fn_called_count_; |
||||
while (transport->accept_stream_fn_called_count_ > 0) { |
||||
--transport->accept_stream_fn_called_count_; |
||||
transport->combiner->Run( |
||||
GRPC_CLOSURE_CREATE(accept_stream_locked, transport, nullptr), |
||||
absl::OkStatus()); |
||||
} |
||||
} |
||||
if (op->on_consumed) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, op->on_consumed, absl::OkStatus()); |
||||
} |
||||
bool do_close = false; |
||||
if (!op->disconnect_with_error.ok()) { |
||||
do_close = true; |
||||
} |
||||
if (!op->goaway_error.ok()) { |
||||
do_close = true; |
||||
} |
||||
if (do_close) { |
||||
close_transport_locked(transport); |
||||
} |
||||
GRPC_BINDER_UNREF_TRANSPORT(transport, "perform_transport_op"); |
||||
} |
||||
|
||||
void grpc_binder_transport::PerformOp(grpc_transport_op* op) { |
||||
LOG(INFO) << __func__; |
||||
op->handler_private.extra_arg = this; |
||||
GRPC_BINDER_REF_TRANSPORT(this, "perform_transport_op"); |
||||
combiner->Run(GRPC_CLOSURE_INIT(&op->handler_private.closure, |
||||
perform_transport_op_locked, op, nullptr), |
||||
absl::OkStatus()); |
||||
} |
||||
|
||||
static void destroy_stream_locked(void* sp, grpc_error_handle /*error*/) { |
||||
grpc_binder_stream* stream = static_cast<grpc_binder_stream*>(sp); |
||||
grpc_binder_transport* transport = stream->t; |
||||
cancel_stream_locked( |
||||
transport, stream, |
||||
grpc_error_set_int(GRPC_ERROR_CREATE("destroy stream"), |
||||
grpc_core::StatusIntProperty::kRpcStatus, |
||||
GRPC_STATUS_UNAVAILABLE)); |
||||
stream->~grpc_binder_stream(); |
||||
} |
||||
|
||||
void grpc_binder_transport::DestroyStream(grpc_stream* gs, |
||||
grpc_closure* then_schedule_closure) { |
||||
LOG(INFO) << __func__; |
||||
grpc_binder_stream* stream = reinterpret_cast<grpc_binder_stream*>(gs); |
||||
stream->destroy_stream_then_closure = then_schedule_closure; |
||||
stream->t->combiner->Run( |
||||
GRPC_CLOSURE_INIT(&stream->destroy_stream, destroy_stream_locked, stream, |
||||
nullptr), |
||||
absl::OkStatus()); |
||||
} |
||||
|
||||
static void destroy_transport_locked(void* gt, grpc_error_handle /*error*/) { |
||||
grpc_binder_transport* transport = static_cast<grpc_binder_transport*>(gt); |
||||
close_transport_locked(transport); |
||||
// Release the references held by the transport.
|
||||
transport->wire_reader = nullptr; |
||||
transport->transport_stream_receiver = nullptr; |
||||
transport->wire_writer = nullptr; |
||||
GRPC_BINDER_UNREF_TRANSPORT(transport, "transport destroyed"); |
||||
} |
||||
|
||||
void grpc_binder_transport::Orphan() { |
||||
LOG(INFO) << __func__; |
||||
combiner->Run(GRPC_CLOSURE_CREATE(destroy_transport_locked, this, nullptr), |
||||
absl::OkStatus()); |
||||
} |
||||
|
||||
size_t grpc_binder_transport::SizeOfStream() const { |
||||
return sizeof(grpc_binder_stream); |
||||
} |
||||
|
||||
grpc_binder_transport::grpc_binder_transport( |
||||
std::unique_ptr<grpc_binder::Binder> binder, bool is_client, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy) |
||||
: is_client(is_client), |
||||
combiner(grpc_combiner_create( |
||||
grpc_event_engine::experimental::GetDefaultEventEngine())), |
||||
state_tracker( |
||||
is_client ? "binder_transport_client" : "binder_transport_server", |
||||
GRPC_CHANNEL_READY), |
||||
refs(1, nullptr) { |
||||
LOG(INFO) << __func__; |
||||
transport_stream_receiver = |
||||
std::make_shared<grpc_binder::TransportStreamReceiverImpl>( |
||||
is_client, /*accept_stream_callback=*/[this] { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
combiner->Run( |
||||
GRPC_CLOSURE_CREATE(accept_stream_locked, this, nullptr), |
||||
absl::OkStatus()); |
||||
}); |
||||
// WireReader holds a ref to grpc_binder_transport.
|
||||
GRPC_BINDER_REF_TRANSPORT(this, "wire reader"); |
||||
wire_reader = grpc_core::MakeOrphanable<grpc_binder::WireReaderImpl>( |
||||
transport_stream_receiver, is_client, security_policy, |
||||
// on_destruct_callback=
|
||||
[this] { |
||||
// Unref transport when destructed.
|
||||
GRPC_BINDER_UNREF_TRANSPORT(this, "wire reader"); |
||||
}); |
||||
wire_writer = wire_reader->SetupTransport(std::move(binder)); |
||||
} |
||||
|
||||
grpc_binder_transport::~grpc_binder_transport() { |
||||
GRPC_COMBINER_UNREF(combiner, "binder_transport"); |
||||
} |
||||
|
||||
grpc_core::Transport* grpc_create_binder_transport_client( |
||||
std::unique_ptr<grpc_binder::Binder> endpoint_binder, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) { |
||||
LOG(INFO) << __func__; |
||||
|
||||
CHECK(endpoint_binder != nullptr); |
||||
CHECK_NE(security_policy, nullptr); |
||||
|
||||
grpc_binder_transport* t = new grpc_binder_transport( |
||||
std::move(endpoint_binder), /*is_client=*/true, security_policy); |
||||
|
||||
return t; |
||||
} |
||||
|
||||
grpc_core::Transport* grpc_create_binder_transport_server( |
||||
std::unique_ptr<grpc_binder::Binder> client_binder, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) { |
||||
LOG(INFO) << __func__; |
||||
|
||||
CHECK(client_binder != nullptr); |
||||
CHECK_NE(security_policy, nullptr); |
||||
|
||||
grpc_binder_transport* t = new grpc_binder_transport( |
||||
std::move(client_binder), /*is_client=*/false, security_policy); |
||||
|
||||
return t; |
||||
} |
||||
#endif |
@ -1,119 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_TRANSPORT_BINDER_TRANSPORT_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_TRANSPORT_BINDER_TRANSPORT_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
|
||||
#include <atomic> |
||||
#include <memory> |
||||
#include <string> |
||||
#include <utility> |
||||
#include <vector> |
||||
|
||||
#include "absl/container/flat_hash_map.h" |
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "src/core/ext/transport/binder/utils/transport_stream_receiver.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_reader.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_writer.h" |
||||
#include "src/core/lib/iomgr/combiner.h" |
||||
#include "src/core/lib/transport/transport.h" |
||||
#include "src/core/util/crash.h" |
||||
|
||||
struct grpc_binder_stream; |
||||
|
||||
// TODO(mingcl): Consider putting the struct in a namespace (Eventually this
|
||||
// depends on what style we want to follow)
|
||||
// TODO(mingcl): Decide casing for this class name. Should we use C-style class
|
||||
// name here or just go with C++ style?
|
||||
struct grpc_binder_transport final : public grpc_core::FilterStackTransport { |
||||
explicit grpc_binder_transport( |
||||
std::unique_ptr<grpc_binder::Binder> binder, bool is_client, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
~grpc_binder_transport() override; |
||||
|
||||
grpc_core::FilterStackTransport* filter_stack_transport() override { |
||||
return this; |
||||
} |
||||
grpc_core::ClientTransport* client_transport() override { return nullptr; } |
||||
grpc_core::ServerTransport* server_transport() override { return nullptr; } |
||||
absl::string_view GetTransportName() const override { return "binder"; } |
||||
void InitStream(grpc_stream* gs, grpc_stream_refcount* refcount, |
||||
const void* server_data, grpc_core::Arena* arena) override; |
||||
void SetPollset(grpc_stream*, grpc_pollset*) override {} |
||||
void SetPollsetSet(grpc_stream*, grpc_pollset_set*) override {} |
||||
void PerformOp(grpc_transport_op* op) override; |
||||
size_t SizeOfStream() const override; |
||||
bool HackyDisableStreamOpBatchCoalescingInConnectedChannel() const override { |
||||
return false; |
||||
} |
||||
void PerformStreamOp(grpc_stream* gs, |
||||
grpc_transport_stream_op_batch* op) override; |
||||
void DestroyStream(grpc_stream* gs, |
||||
grpc_closure* then_schedule_closure) override; |
||||
void Orphan() override; |
||||
|
||||
int NewStreamTxCode() { |
||||
// TODO(mingcl): Wrap around when all tx codes are used. "If we do detect a
|
||||
// collision however, we will fail the new call with UNAVAILABLE, and shut
|
||||
// down the transport gracefully."
|
||||
CHECK(next_free_tx_code <= LAST_CALL_TRANSACTION); |
||||
return next_free_tx_code++; |
||||
} |
||||
|
||||
std::shared_ptr<grpc_binder::TransportStreamReceiver> |
||||
transport_stream_receiver; |
||||
grpc_core::OrphanablePtr<grpc_binder::WireReader> wire_reader; |
||||
std::shared_ptr<grpc_binder::WireWriter> wire_writer; |
||||
|
||||
bool is_client; |
||||
// A set of currently registered streams (the key is the stream ID).
|
||||
absl::flat_hash_map<int, grpc_binder_stream*> registered_stream; |
||||
grpc_core::Combiner* combiner; |
||||
|
||||
// The callback and the data for the callback when the stream is connected
|
||||
// between client and server. registered_method_matcher_cb is called before
|
||||
// invoking the recv initial metadata callback.
|
||||
void (*accept_stream_fn)(void* user_data, grpc_core::Transport* transport, |
||||
const void* server_data) = nullptr; |
||||
void (*registered_method_matcher_cb)( |
||||
void* user_data, grpc_core::ServerMetadata* metadata) = nullptr; |
||||
void* accept_stream_user_data = nullptr; |
||||
// `accept_stream_locked()` could be called before `accept_stream_fn` has been
|
||||
// set, we need to remember those requests that comes too early and call them
|
||||
// later when we can.
|
||||
int accept_stream_fn_called_count_{0}; |
||||
|
||||
grpc_core::ConnectivityStateTracker state_tracker; |
||||
grpc_core::RefCount refs; |
||||
|
||||
private: |
||||
std::atomic<int> next_free_tx_code{grpc_binder::kFirstCallId}; |
||||
}; |
||||
|
||||
grpc_core::Transport* grpc_create_binder_transport_client( |
||||
std::unique_ptr<grpc_binder::Binder> endpoint_binder, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
grpc_core::Transport* grpc_create_binder_transport_server( |
||||
std::unique_ptr<grpc_binder::Binder> client_binder, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_TRANSPORT_BINDER_TRANSPORT_H
|
@ -1,76 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_BINDER_AUTO_UTILS_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_BINDER_AUTO_UTILS_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
|
||||
#include "src/core/ext/transport/binder/utils/ndk_binder.h" |
||||
|
||||
namespace grpc_binder { |
||||
namespace ndk_util { |
||||
|
||||
///
|
||||
/// Represents one strong pointer to an AIBinder object.
|
||||
/// Copied from binder/ndk/include_cpp/android/binder_auto_utils.h
|
||||
///
|
||||
class SpAIBinder { |
||||
public: |
||||
SpAIBinder() : mBinder(nullptr) {} |
||||
explicit SpAIBinder(AIBinder* binder) : mBinder(binder) {} |
||||
SpAIBinder(std::nullptr_t) |
||||
: SpAIBinder() {} // NOLINT(google-explicit-constructor)
|
||||
SpAIBinder(const SpAIBinder& other) { *this = other; } |
||||
|
||||
~SpAIBinder() { set(nullptr); } |
||||
SpAIBinder& operator=(const SpAIBinder& other) { |
||||
if (this == &other) { |
||||
return *this; |
||||
} |
||||
AIBinder_incStrong(other.mBinder); |
||||
set(other.mBinder); |
||||
return *this; |
||||
} |
||||
|
||||
void set(AIBinder* binder) { |
||||
AIBinder* old = *const_cast<AIBinder* volatile*>(&mBinder); |
||||
if (old != nullptr) AIBinder_decStrong(old); |
||||
if (old != *const_cast<AIBinder* volatile*>(&mBinder)) { |
||||
__assert(__FILE__, __LINE__, "Race detected."); |
||||
} |
||||
mBinder = binder; |
||||
} |
||||
|
||||
AIBinder* get() const { return mBinder; } |
||||
AIBinder** getR() { return &mBinder; } |
||||
|
||||
bool operator!=(const SpAIBinder& rhs) const { return get() != rhs.get(); } |
||||
bool operator<(const SpAIBinder& rhs) const { return get() < rhs.get(); } |
||||
bool operator<=(const SpAIBinder& rhs) const { return get() <= rhs.get(); } |
||||
bool operator==(const SpAIBinder& rhs) const { return get() == rhs.get(); } |
||||
bool operator>(const SpAIBinder& rhs) const { return get() > rhs.get(); } |
||||
bool operator>=(const SpAIBinder& rhs) const { return get() >= rhs.get(); } |
||||
|
||||
private: |
||||
AIBinder* mBinder = nullptr; |
||||
}; |
||||
} // namespace ndk_util
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif |
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_BINDER_AUTO_UTILS_H
|
@ -1,218 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "src/core/ext/transport/binder/utils/ndk_binder.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
|
||||
#include <dlfcn.h> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "src/core/util/crash.h" |
||||
#include "src/core/util/sync.h" |
||||
|
||||
namespace { |
||||
void* GetNdkBinderHandle() { |
||||
// TODO(mingcl): Consider using RTLD_NOLOAD to check if it is already loaded
|
||||
// first
|
||||
static void* handle = dlopen("libbinder_ndk.so", RTLD_LAZY); |
||||
if (handle == nullptr) { |
||||
LOG(ERROR) << "Cannot open libbinder_ndk.so. Does this device support API " |
||||
"level 29?"; |
||||
CHECK(0); |
||||
} |
||||
return handle; |
||||
} |
||||
|
||||
JavaVM* g_jvm = nullptr; |
||||
grpc_core::Mutex g_jvm_mu; |
||||
|
||||
// Whether the thread has already attached to JVM (this is to prevent
|
||||
// repeated attachment in `AttachJvm()`)
|
||||
thread_local bool g_is_jvm_attached = false; |
||||
|
||||
void SetJvm(JNIEnv* env) { |
||||
// OK to lock here since this function will only be called once for each
|
||||
// connection.
|
||||
grpc_core::MutexLock lock(&g_jvm_mu); |
||||
if (g_jvm != nullptr) { |
||||
return; |
||||
} |
||||
JavaVM* jvm = nullptr; |
||||
jint error = env->GetJavaVM(&jvm); |
||||
if (error != JNI_OK) { |
||||
LOG(ERROR) << "Failed to get JVM"; |
||||
} |
||||
g_jvm = jvm; |
||||
LOG(INFO) << "JVM cached"; |
||||
} |
||||
|
||||
// `SetJvm` need to be called in the process before `AttachJvm`. This is always
|
||||
// the case because one of `AIBinder_fromJavaBinder`/`AIBinder_toJavaBinder`
|
||||
// will be called before we actually uses the binder. Return `false` if not able
|
||||
// to attach to JVM. Return `true` if JVM is attached (or already attached).
|
||||
bool AttachJvm() { |
||||
if (g_is_jvm_attached) { |
||||
return true; |
||||
} |
||||
// Note: The following code would be run at most once per thread.
|
||||
grpc_core::MutexLock lock(&g_jvm_mu); |
||||
if (g_jvm == nullptr) { |
||||
LOG(ERROR) << "JVM not cached yet"; |
||||
return false; |
||||
} |
||||
JNIEnv* env_unused; |
||||
// Note that attach a thread that is already attached is a no-op, so it is
|
||||
// fine to call this again if the thread has already been attached by other.
|
||||
g_jvm->AttachCurrentThread(&env_unused, /* thr_args= */ nullptr); |
||||
LOG(INFO) << "JVM attached successfully"; |
||||
g_is_jvm_attached = true; |
||||
return true; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
namespace grpc_binder { |
||||
namespace ndk_util { |
||||
|
||||
// Helper macro to obtain the function pointer corresponding to the name
|
||||
#define FORWARD(name) \ |
||||
typedef decltype(&name) func_type; \
|
||||
static func_type ptr = \
|
||||
reinterpret_cast<func_type>(dlsym(GetNdkBinderHandle(), #name)); \
|
||||
if (ptr == nullptr) { \
|
||||
LOG(ERROR) << "dlsym failed. Cannot find " << #name \
|
||||
<< " in libbinder_ndk.so. " \
|
||||
<< "BinderTransport requires API level >= 33"; \
|
||||
CHECK(0); \
|
||||
} \
|
||||
return ptr |
||||
|
||||
void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz) { |
||||
FORWARD(AIBinder_Class_disableInterfaceTokenHeader)(clazz); |
||||
} |
||||
|
||||
void* AIBinder_getUserData(AIBinder* binder) { |
||||
FORWARD(AIBinder_getUserData)(binder); |
||||
} |
||||
|
||||
uid_t AIBinder_getCallingUid() { FORWARD(AIBinder_getCallingUid)(); } |
||||
|
||||
AIBinder* AIBinder_fromJavaBinder(JNIEnv* env, jobject binder) { |
||||
SetJvm(env); |
||||
FORWARD(AIBinder_fromJavaBinder)(env, binder); |
||||
} |
||||
|
||||
AIBinder_Class* AIBinder_Class_define(const char* interfaceDescriptor, |
||||
AIBinder_Class_onCreate onCreate, |
||||
AIBinder_Class_onDestroy onDestroy, |
||||
AIBinder_Class_onTransact onTransact) { |
||||
FORWARD(AIBinder_Class_define) |
||||
(interfaceDescriptor, onCreate, onDestroy, onTransact); |
||||
} |
||||
|
||||
AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args) { |
||||
FORWARD(AIBinder_new)(clazz, args); |
||||
} |
||||
|
||||
bool AIBinder_associateClass(AIBinder* binder, const AIBinder_Class* clazz) { |
||||
FORWARD(AIBinder_associateClass)(binder, clazz); |
||||
} |
||||
|
||||
void AIBinder_incStrong(AIBinder* binder) { |
||||
FORWARD(AIBinder_incStrong)(binder); |
||||
} |
||||
|
||||
void AIBinder_decStrong(AIBinder* binder) { |
||||
FORWARD(AIBinder_decStrong)(binder); |
||||
} |
||||
|
||||
binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, |
||||
AParcel** in, AParcel** out, |
||||
binder_flags_t flags) { |
||||
if (!AttachJvm()) { |
||||
LOG(ERROR) << "failed to attach JVM. AIBinder_transact might fail."; |
||||
} |
||||
FORWARD(AIBinder_transact)(binder, code, in, out, flags); |
||||
} |
||||
|
||||
binder_status_t AParcel_readByteArray(const AParcel* parcel, void* arrayData, |
||||
AParcel_byteArrayAllocator allocator) { |
||||
FORWARD(AParcel_readByteArray)(parcel, arrayData, allocator); |
||||
} |
||||
|
||||
void AParcel_delete(AParcel* parcel) { FORWARD(AParcel_delete)(parcel); } |
||||
int32_t AParcel_getDataSize(const AParcel* parcel) { |
||||
FORWARD(AParcel_getDataSize)(parcel); |
||||
} |
||||
|
||||
binder_status_t AParcel_writeInt32(AParcel* parcel, int32_t value) { |
||||
FORWARD(AParcel_writeInt32)(parcel, value); |
||||
} |
||||
|
||||
binder_status_t AParcel_writeInt64(AParcel* parcel, int64_t value) { |
||||
FORWARD(AParcel_writeInt64)(parcel, value); |
||||
} |
||||
|
||||
binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) { |
||||
FORWARD(AParcel_writeStrongBinder)(parcel, binder); |
||||
} |
||||
|
||||
binder_status_t AParcel_writeString(AParcel* parcel, const char* string, |
||||
int32_t length) { |
||||
FORWARD(AParcel_writeString)(parcel, string, length); |
||||
} |
||||
|
||||
binder_status_t AParcel_readInt32(const AParcel* parcel, int32_t* value) { |
||||
FORWARD(AParcel_readInt32)(parcel, value); |
||||
} |
||||
|
||||
binder_status_t AParcel_readInt64(const AParcel* parcel, int64_t* value) { |
||||
FORWARD(AParcel_readInt64)(parcel, value); |
||||
} |
||||
|
||||
binder_status_t AParcel_readString(const AParcel* parcel, void* stringData, |
||||
AParcel_stringAllocator allocator) { |
||||
FORWARD(AParcel_readString)(parcel, stringData, allocator); |
||||
} |
||||
|
||||
binder_status_t AParcel_readStrongBinder(const AParcel* parcel, |
||||
AIBinder** binder) { |
||||
FORWARD(AParcel_readStrongBinder)(parcel, binder); |
||||
} |
||||
|
||||
binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* arrayData, |
||||
int32_t length) { |
||||
FORWARD(AParcel_writeByteArray)(parcel, arrayData, length); |
||||
} |
||||
|
||||
binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) { |
||||
FORWARD(AIBinder_prepareTransaction)(binder, in); |
||||
} |
||||
|
||||
jobject AIBinder_toJavaBinder(JNIEnv* env, AIBinder* binder) { |
||||
SetJvm(env); |
||||
FORWARD(AIBinder_toJavaBinder)(env, binder); |
||||
} |
||||
|
||||
} // namespace ndk_util
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif |
||||
#endif |
@ -1,107 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_NDK_BINDER_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_NDK_BINDER_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
|
||||
#include <assert.h> |
||||
#include <jni.h> |
||||
|
||||
#include <memory> |
||||
|
||||
// This file defines NdkBinder functions, variables, and types in
|
||||
// grpc_binder::ndk_util namespace. This allows us to dynamically load
|
||||
// libbinder_ndk at runtime, and make it possible to compile the code without
|
||||
// the library present at compile time.
|
||||
|
||||
// TODO(mingcl): Consider if we want to check API level and include NDK headers
|
||||
// normally if the level is high enough
|
||||
|
||||
namespace grpc_binder { |
||||
namespace ndk_util { |
||||
|
||||
struct AIBinder; |
||||
struct AParcel; |
||||
struct AIBinder_Class; |
||||
|
||||
// Only enum values used by the project is defined here
|
||||
enum { |
||||
FLAG_ONEWAY = 0x01, |
||||
}; |
||||
enum { |
||||
STATUS_OK = 0, |
||||
STATUS_UNKNOWN_ERROR = (-2147483647 - 1), |
||||
}; |
||||
|
||||
typedef int32_t binder_status_t; |
||||
typedef uint32_t binder_flags_t; |
||||
typedef uint32_t transaction_code_t; |
||||
|
||||
typedef bool (*AParcel_byteArrayAllocator)(void* arrayData, int32_t length, |
||||
int8_t** outBuffer); |
||||
typedef bool (*AParcel_stringAllocator)(void* stringData, int32_t length, |
||||
char** buffer); |
||||
typedef void* (*AIBinder_Class_onCreate)(void* args); |
||||
typedef void (*AIBinder_Class_onDestroy)(void* userData); |
||||
typedef binder_status_t (*AIBinder_Class_onTransact)(AIBinder* binder, |
||||
transaction_code_t code, |
||||
const AParcel* in, |
||||
AParcel* out); |
||||
|
||||
void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz); |
||||
void* AIBinder_getUserData(AIBinder* binder); |
||||
uid_t AIBinder_getCallingUid(); |
||||
AIBinder* AIBinder_fromJavaBinder(JNIEnv* env, jobject binder); |
||||
AIBinder_Class* AIBinder_Class_define(const char* interfaceDescriptor, |
||||
AIBinder_Class_onCreate onCreate, |
||||
AIBinder_Class_onDestroy onDestroy, |
||||
AIBinder_Class_onTransact onTransact); |
||||
AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args); |
||||
bool AIBinder_associateClass(AIBinder* binder, const AIBinder_Class* clazz); |
||||
void AIBinder_incStrong(AIBinder* binder); |
||||
void AIBinder_decStrong(AIBinder* binder); |
||||
binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, |
||||
AParcel** in, AParcel** out, |
||||
binder_flags_t flags); |
||||
binder_status_t AParcel_readByteArray(const AParcel* parcel, void* arrayData, |
||||
AParcel_byteArrayAllocator allocator); |
||||
void AParcel_delete(AParcel* parcel); |
||||
int32_t AParcel_getDataSize(const AParcel* parcel); |
||||
binder_status_t AParcel_writeInt32(AParcel* parcel, int32_t value); |
||||
binder_status_t AParcel_writeInt64(AParcel* parcel, int64_t value); |
||||
binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder); |
||||
binder_status_t AParcel_writeString(AParcel* parcel, const char* string, |
||||
int32_t length); |
||||
binder_status_t AParcel_readInt32(const AParcel* parcel, int32_t* value); |
||||
binder_status_t AParcel_readInt64(const AParcel* parcel, int64_t* value); |
||||
binder_status_t AParcel_readString(const AParcel* parcel, void* stringData, |
||||
AParcel_stringAllocator allocator); |
||||
binder_status_t AParcel_readStrongBinder(const AParcel* parcel, |
||||
AIBinder** binder); |
||||
binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* arrayData, |
||||
int32_t length); |
||||
binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in); |
||||
jobject AIBinder_toJavaBinder(JNIEnv* env, AIBinder* binder); |
||||
|
||||
} // namespace ndk_util
|
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GPR_SUPPORT_BINDER_TRANSPORT
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_NDK_BINDER_H
|
@ -1,70 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_TRANSPORT_STREAM_RECEIVER_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_TRANSPORT_STREAM_RECEIVER_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <functional> |
||||
#include <string> |
||||
#include <vector> |
||||
|
||||
#include "absl/status/statusor.h" |
||||
#include "src/core/ext/transport/binder/wire_format/transaction.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
typedef int StreamIdentifier; |
||||
|
||||
class TransportStreamReceiver { |
||||
public: |
||||
virtual ~TransportStreamReceiver() = default; |
||||
|
||||
using InitialMetadataCallbackType = |
||||
std::function<void(absl::StatusOr<Metadata>)>; |
||||
using MessageDataCallbackType = |
||||
std::function<void(absl::StatusOr<std::string>)>; |
||||
using TrailingMetadataCallbackType = |
||||
std::function<void(absl::StatusOr<Metadata>, int)>; |
||||
|
||||
// Only handles single time invocation. Callback object will be deleted.
|
||||
// The callback should be valid until invocation or unregister.
|
||||
virtual void RegisterRecvInitialMetadata(StreamIdentifier id, |
||||
InitialMetadataCallbackType cb) = 0; |
||||
virtual void RegisterRecvMessage(StreamIdentifier id, |
||||
MessageDataCallbackType cb) = 0; |
||||
virtual void RegisterRecvTrailingMetadata( |
||||
StreamIdentifier id, TrailingMetadataCallbackType cb) = 0; |
||||
|
||||
// For the following functions, the second arguments are the transaction
|
||||
// result received from the lower level. If it is None, that means there's
|
||||
// something wrong when receiving the corresponding transaction. In such case,
|
||||
// we should cancel the gRPC callback as well.
|
||||
virtual void NotifyRecvInitialMetadata( |
||||
StreamIdentifier id, absl::StatusOr<Metadata> initial_metadata) = 0; |
||||
virtual void NotifyRecvMessage(StreamIdentifier id, |
||||
absl::StatusOr<std::string> message) = 0; |
||||
virtual void NotifyRecvTrailingMetadata( |
||||
StreamIdentifier id, absl::StatusOr<Metadata> trailing_metadata, |
||||
int status) = 0; |
||||
// Remove all entries associated with stream number `id`.
|
||||
virtual void CancelStream(StreamIdentifier id) = 0; |
||||
|
||||
static const absl::string_view kGrpcBinderTransportCancelledGracefully; |
||||
}; |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_TRANSPORT_STREAM_RECEIVER_H
|
@ -1,257 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <functional> |
||||
#include <string> |
||||
#include <utility> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "src/core/util/crash.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
const absl::string_view |
||||
TransportStreamReceiver::kGrpcBinderTransportCancelledGracefully = |
||||
"grpc-binder-transport: cancelled gracefully"; |
||||
|
||||
void TransportStreamReceiverImpl::RegisterRecvInitialMetadata( |
||||
StreamIdentifier id, InitialMetadataCallbackType cb) { |
||||
LOG(INFO) << __func__ << " id = " << id << " is_client = " << is_client_; |
||||
absl::StatusOr<Metadata> initial_metadata{}; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
CHECK_EQ(initial_metadata_cbs_.count(id), 0u); |
||||
auto iter = pending_initial_metadata_.find(id); |
||||
if (iter == pending_initial_metadata_.end()) { |
||||
if (trailing_metadata_recvd_.count(id)) { |
||||
cb(absl::CancelledError("")); |
||||
} else { |
||||
initial_metadata_cbs_[id] = std::move(cb); |
||||
} |
||||
cb = nullptr; |
||||
} else { |
||||
initial_metadata = std::move(iter->second.front()); |
||||
iter->second.pop(); |
||||
if (iter->second.empty()) { |
||||
pending_initial_metadata_.erase(iter); |
||||
} |
||||
} |
||||
} |
||||
if (cb != nullptr) { |
||||
cb(std::move(initial_metadata)); |
||||
} |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::RegisterRecvMessage( |
||||
StreamIdentifier id, MessageDataCallbackType cb) { |
||||
LOG(INFO) << __func__ << " id = " << id << " is_client = " << is_client_; |
||||
absl::StatusOr<std::string> message{}; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
CHECK_EQ(message_cbs_.count(id), 0u); |
||||
auto iter = pending_message_.find(id); |
||||
if (iter == pending_message_.end()) { |
||||
// If we'd already received trailing-metadata and there's no pending
|
||||
// messages, cancel the callback.
|
||||
if (trailing_metadata_recvd_.count(id)) { |
||||
cb(absl::CancelledError( |
||||
TransportStreamReceiver::kGrpcBinderTransportCancelledGracefully)); |
||||
} else { |
||||
message_cbs_[id] = std::move(cb); |
||||
} |
||||
cb = nullptr; |
||||
} else { |
||||
// We'll still keep all pending messages received before the trailing
|
||||
// metadata since they're issued before the end of stream, as promised by
|
||||
// WireReader which keeps transactions commit in-order.
|
||||
message = std::move(iter->second.front()); |
||||
iter->second.pop(); |
||||
if (iter->second.empty()) { |
||||
pending_message_.erase(iter); |
||||
} |
||||
} |
||||
} |
||||
if (cb != nullptr) { |
||||
cb(std::move(message)); |
||||
} |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::RegisterRecvTrailingMetadata( |
||||
StreamIdentifier id, TrailingMetadataCallbackType cb) { |
||||
LOG(INFO) << __func__ << " id = " << id << " is_client = " << is_client_; |
||||
std::pair<absl::StatusOr<Metadata>, int> trailing_metadata{}; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
CHECK_EQ(trailing_metadata_cbs_.count(id), 0u); |
||||
auto iter = pending_trailing_metadata_.find(id); |
||||
if (iter == pending_trailing_metadata_.end()) { |
||||
trailing_metadata_cbs_[id] = std::move(cb); |
||||
cb = nullptr; |
||||
} else { |
||||
trailing_metadata = std::move(iter->second.front()); |
||||
iter->second.pop(); |
||||
if (iter->second.empty()) { |
||||
pending_trailing_metadata_.erase(iter); |
||||
} |
||||
} |
||||
} |
||||
if (cb != nullptr) { |
||||
cb(std::move(trailing_metadata.first), trailing_metadata.second); |
||||
} |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::NotifyRecvInitialMetadata( |
||||
StreamIdentifier id, absl::StatusOr<Metadata> initial_metadata) { |
||||
LOG(INFO) << __func__ << " id = " << id << " is_client = " << is_client_; |
||||
if (!is_client_ && accept_stream_callback_ && initial_metadata.ok()) { |
||||
accept_stream_callback_(); |
||||
} |
||||
InitialMetadataCallbackType cb; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
auto iter = initial_metadata_cbs_.find(id); |
||||
if (iter != initial_metadata_cbs_.end()) { |
||||
cb = iter->second; |
||||
initial_metadata_cbs_.erase(iter); |
||||
} else { |
||||
pending_initial_metadata_[id].push(std::move(initial_metadata)); |
||||
return; |
||||
} |
||||
} |
||||
cb(std::move(initial_metadata)); |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::NotifyRecvMessage( |
||||
StreamIdentifier id, absl::StatusOr<std::string> message) { |
||||
LOG(INFO) << __func__ << " id = " << id << " is_client = " << is_client_; |
||||
MessageDataCallbackType cb; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
auto iter = message_cbs_.find(id); |
||||
if (iter != message_cbs_.end()) { |
||||
cb = iter->second; |
||||
message_cbs_.erase(iter); |
||||
} else { |
||||
pending_message_[id].push(std::move(message)); |
||||
return; |
||||
} |
||||
} |
||||
cb(std::move(message)); |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::NotifyRecvTrailingMetadata( |
||||
StreamIdentifier id, absl::StatusOr<Metadata> trailing_metadata, |
||||
int status) { |
||||
// Trailing metadata mark the end of the stream. Since TransportStreamReceiver
|
||||
// assumes in-order commitments of transactions and that trailing metadata is
|
||||
// parsed after message data, we can safely cancel all upcoming callbacks of
|
||||
// recv_message.
|
||||
LOG(INFO) << __func__ << " id = " << id << " is_client = " << is_client_; |
||||
OnRecvTrailingMetadata(id); |
||||
TrailingMetadataCallbackType cb; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
auto iter = trailing_metadata_cbs_.find(id); |
||||
if (iter != trailing_metadata_cbs_.end()) { |
||||
cb = iter->second; |
||||
trailing_metadata_cbs_.erase(iter); |
||||
} else { |
||||
pending_trailing_metadata_[id].emplace(std::move(trailing_metadata), |
||||
status); |
||||
return; |
||||
} |
||||
} |
||||
cb(std::move(trailing_metadata), status); |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::CancelInitialMetadataCallback( |
||||
StreamIdentifier id, absl::Status error) { |
||||
InitialMetadataCallbackType callback = nullptr; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
auto iter = initial_metadata_cbs_.find(id); |
||||
if (iter != initial_metadata_cbs_.end()) { |
||||
callback = std::move(iter->second); |
||||
initial_metadata_cbs_.erase(iter); |
||||
} |
||||
} |
||||
if (callback != nullptr) { |
||||
std::move(callback)(error); |
||||
} |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::CancelMessageCallback(StreamIdentifier id, |
||||
absl::Status error) { |
||||
MessageDataCallbackType callback = nullptr; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
auto iter = message_cbs_.find(id); |
||||
if (iter != message_cbs_.end()) { |
||||
callback = std::move(iter->second); |
||||
message_cbs_.erase(iter); |
||||
} |
||||
} |
||||
if (callback != nullptr) { |
||||
std::move(callback)(error); |
||||
} |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::CancelTrailingMetadataCallback( |
||||
StreamIdentifier id, absl::Status error) { |
||||
TrailingMetadataCallbackType callback = nullptr; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
auto iter = trailing_metadata_cbs_.find(id); |
||||
if (iter != trailing_metadata_cbs_.end()) { |
||||
callback = std::move(iter->second); |
||||
trailing_metadata_cbs_.erase(iter); |
||||
} |
||||
} |
||||
if (callback != nullptr) { |
||||
std::move(callback)(error, 0); |
||||
} |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::OnRecvTrailingMetadata(StreamIdentifier id) { |
||||
LOG(INFO) << __func__ << " id = " << id << " is_client = " << is_client_; |
||||
m_.Lock(); |
||||
trailing_metadata_recvd_.insert(id); |
||||
m_.Unlock(); |
||||
CancelInitialMetadataCallback(id, absl::CancelledError("")); |
||||
CancelMessageCallback( |
||||
id, |
||||
absl::CancelledError( |
||||
TransportStreamReceiver::kGrpcBinderTransportCancelledGracefully)); |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::CancelStream(StreamIdentifier id) { |
||||
LOG(INFO) << __func__ << " id = " << id << " is_client = " << is_client_; |
||||
CancelInitialMetadataCallback(id, absl::CancelledError("Stream cancelled")); |
||||
CancelMessageCallback(id, absl::CancelledError("Stream cancelled")); |
||||
CancelTrailingMetadataCallback(id, absl::CancelledError("Stream cancelled")); |
||||
grpc_core::MutexLock l(&m_); |
||||
trailing_metadata_recvd_.erase(id); |
||||
pending_initial_metadata_.erase(id); |
||||
pending_message_.erase(id); |
||||
pending_trailing_metadata_.erase(id); |
||||
} |
||||
} // namespace grpc_binder
|
||||
#endif |
@ -1,112 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_TRANSPORT_STREAM_RECEIVER_IMPL_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_TRANSPORT_STREAM_RECEIVER_IMPL_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <functional> |
||||
#include <map> |
||||
#include <queue> |
||||
#include <set> |
||||
#include <string> |
||||
#include <vector> |
||||
|
||||
#include "src/core/ext/transport/binder/utils/transport_stream_receiver.h" |
||||
#include "src/core/util/sync.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
// Routes the data received from transport to corresponding streams
|
||||
class TransportStreamReceiverImpl : public TransportStreamReceiver { |
||||
public: |
||||
explicit TransportStreamReceiverImpl( |
||||
bool is_client, std::function<void()> accept_stream_callback = nullptr) |
||||
: is_client_(is_client), |
||||
accept_stream_callback_(accept_stream_callback) {} |
||||
void RegisterRecvInitialMetadata(StreamIdentifier id, |
||||
InitialMetadataCallbackType cb) override; |
||||
void RegisterRecvMessage(StreamIdentifier id, |
||||
MessageDataCallbackType cb) override; |
||||
void RegisterRecvTrailingMetadata(StreamIdentifier id, |
||||
TrailingMetadataCallbackType cb) override; |
||||
void NotifyRecvInitialMetadata( |
||||
StreamIdentifier id, absl::StatusOr<Metadata> initial_metadata) override; |
||||
void NotifyRecvMessage(StreamIdentifier id, |
||||
absl::StatusOr<std::string> message) override; |
||||
void NotifyRecvTrailingMetadata(StreamIdentifier id, |
||||
absl::StatusOr<Metadata> trailing_metadata, |
||||
int status) override; |
||||
|
||||
void CancelStream(StreamIdentifier id) override; |
||||
|
||||
private: |
||||
// Trailing metadata marks the end of one-side of the stream. Thus, after
|
||||
// receiving trailing metadata from the other-end, we know that there will
|
||||
// never be in-coming message data anymore, and all recv_message callbacks
|
||||
// (as well as recv_initial_metadata callback, if there's any) registered will
|
||||
// never be satisfied. This function cancels all such callbacks gracefully
|
||||
// (with absl::OkStatus()) to avoid being blocked waiting for them.
|
||||
void OnRecvTrailingMetadata(StreamIdentifier id); |
||||
|
||||
void CancelInitialMetadataCallback(StreamIdentifier id, absl::Status error); |
||||
void CancelMessageCallback(StreamIdentifier id, absl::Status error); |
||||
void CancelTrailingMetadataCallback(StreamIdentifier id, absl::Status error); |
||||
|
||||
std::map<StreamIdentifier, InitialMetadataCallbackType> initial_metadata_cbs_; |
||||
std::map<StreamIdentifier, MessageDataCallbackType> message_cbs_; |
||||
std::map<StreamIdentifier, TrailingMetadataCallbackType> |
||||
trailing_metadata_cbs_; |
||||
// TODO(waynetu): Better thread safety design. For example, use separate
|
||||
// mutexes for different type of messages.
|
||||
grpc_core::Mutex m_; |
||||
// TODO(waynetu): gRPC surface layer will not wait for the current message to
|
||||
// be delivered before sending the next message. The following implementation
|
||||
// is still buggy with the current implementation of wire writer if
|
||||
// transaction issued first completes after the one issued later does. This is
|
||||
// because we just take the first element out of the queue and assume it's the
|
||||
// one issued first without further checking, which results in callbacks being
|
||||
// invoked with incorrect data.
|
||||
//
|
||||
// This should be fixed in the wire writer level and make sure out-of-order
|
||||
// messages will be re-ordered by it. In such case, the queueing approach will
|
||||
// work fine. Refer to the TODO in WireWriterImpl::ProcessTransaction() at
|
||||
// wire_reader_impl.cc for detecting and resolving out-of-order transactions.
|
||||
//
|
||||
// TODO(waynetu): Use absl::flat_hash_map.
|
||||
std::map<StreamIdentifier, std::queue<absl::StatusOr<Metadata>>> |
||||
pending_initial_metadata_ ABSL_GUARDED_BY(m_); |
||||
std::map<StreamIdentifier, std::queue<absl::StatusOr<std::string>>> |
||||
pending_message_ ABSL_GUARDED_BY(m_); |
||||
std::map<StreamIdentifier, |
||||
std::queue<std::pair<absl::StatusOr<Metadata>, int>>> |
||||
pending_trailing_metadata_ ABSL_GUARDED_BY(m_); |
||||
// Record whether or not the recv_message callbacks of a given stream is
|
||||
// cancelled. Although we explicitly cancel the registered recv_message() in
|
||||
// CancelRecvMessageCallbacksDueToTrailingMetadata(), there are chances that
|
||||
// the registration comes "after" we receive trailing metadata. Therefore,
|
||||
// when RegisterRecvMessage() gets called, we should check whether
|
||||
// recv_message_cancelled_ contains the corresponding stream ID, and if so,
|
||||
// directly cancel the callback gracefully without pending it.
|
||||
std::set<StreamIdentifier> trailing_metadata_recvd_ ABSL_GUARDED_BY(m_); |
||||
|
||||
bool is_client_; |
||||
// Called when receiving initial metadata to inform the server about a new
|
||||
// stream.
|
||||
std::function<void()> accept_stream_callback_; |
||||
}; |
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_TRANSPORT_STREAM_RECEIVER_IMPL_H
|
@ -1,104 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <cstdint> |
||||
#include <functional> |
||||
#include <memory> |
||||
#include <string> |
||||
|
||||
#include "absl/status/status.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder_constants.h" |
||||
#include "src/core/util/orphanable.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
class HasRawBinder { |
||||
public: |
||||
virtual ~HasRawBinder() = default; |
||||
virtual void* GetRawBinder() = 0; |
||||
}; |
||||
|
||||
class Binder; |
||||
|
||||
// TODO(waynetu): We might need other methods as well.
|
||||
// TODO(waynetu): Find a better way to express the returned status than
|
||||
// binder_status_t.
|
||||
class WritableParcel { |
||||
public: |
||||
virtual ~WritableParcel() = default; |
||||
virtual int32_t GetDataSize() const = 0; |
||||
virtual absl::Status WriteInt32(int32_t data) = 0; |
||||
virtual absl::Status WriteInt64(int64_t data) = 0; |
||||
virtual absl::Status WriteBinder(HasRawBinder* binder) = 0; |
||||
virtual absl::Status WriteString(absl::string_view s) = 0; |
||||
virtual absl::Status WriteByteArray(const int8_t* buffer, int32_t length) = 0; |
||||
|
||||
absl::Status WriteByteArrayWithLength(absl::string_view buffer) { |
||||
absl::Status status = WriteInt32(buffer.length()); |
||||
if (!status.ok()) return status; |
||||
if (buffer.empty()) return absl::OkStatus(); |
||||
return WriteByteArray(reinterpret_cast<const int8_t*>(buffer.data()), |
||||
buffer.length()); |
||||
} |
||||
}; |
||||
|
||||
// TODO(waynetu): We might need other methods as well.
|
||||
// TODO(waynetu): Find a better way to express the returned status than
|
||||
// binder_status_t.
|
||||
class ReadableParcel { |
||||
public: |
||||
virtual ~ReadableParcel() = default; |
||||
virtual int32_t GetDataSize() const = 0; |
||||
virtual absl::Status ReadInt32(int32_t* data) = 0; |
||||
virtual absl::Status ReadInt64(int64_t* data) = 0; |
||||
virtual absl::Status ReadBinder(std::unique_ptr<Binder>* data) = 0; |
||||
virtual absl::Status ReadByteArray(std::string* data) = 0; |
||||
virtual absl::Status ReadString(std::string* str) = 0; |
||||
}; |
||||
|
||||
class TransactionReceiver : public HasRawBinder { |
||||
public: |
||||
using OnTransactCb = |
||||
std::function<absl::Status(transaction_code_t, ReadableParcel*, int uid)>; |
||||
|
||||
~TransactionReceiver() override = default; |
||||
}; |
||||
|
||||
class WireReader; |
||||
|
||||
class Binder : public HasRawBinder { |
||||
public: |
||||
~Binder() override = default; |
||||
|
||||
virtual void Initialize() = 0; |
||||
virtual absl::Status PrepareTransaction() = 0; |
||||
virtual absl::Status Transact(BinderTransportTxCode tx_code) = 0; |
||||
|
||||
virtual WritableParcel* GetWritableParcel() const = 0; |
||||
|
||||
// TODO(waynetu): Can we decouple the receiver from the binder?
|
||||
virtual std::unique_ptr<TransactionReceiver> ConstructTxReceiver( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb transact_cb) const = 0; |
||||
}; |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_H
|
@ -1,308 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
|
||||
#include <map> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/memory/memory.h" |
||||
#include "absl/strings/str_cat.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder_android.h" |
||||
#include "src/core/util/crash.h" |
||||
#include "src/core/util/sync.h" |
||||
|
||||
namespace grpc_binder { |
||||
namespace { |
||||
|
||||
struct BinderUserData { |
||||
explicit BinderUserData(grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb* callback) |
||||
: wire_reader_ref(wire_reader_ref), callback(callback) {} |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref; |
||||
TransactionReceiver::OnTransactCb* callback; |
||||
}; |
||||
|
||||
struct OnCreateArgs { |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref; |
||||
TransactionReceiver::OnTransactCb* callback; |
||||
}; |
||||
|
||||
void* f_onCreate_userdata(void* data) { |
||||
auto* args = static_cast<OnCreateArgs*>(data); |
||||
return new BinderUserData(args->wire_reader_ref, args->callback); |
||||
} |
||||
|
||||
void f_onDestroy_delete(void* data) { |
||||
auto* user_data = static_cast<BinderUserData*>(data); |
||||
delete user_data; |
||||
} |
||||
|
||||
void* f_onCreate_noop(void* /*args*/) { return nullptr; } |
||||
void f_onDestroy_noop(void* /*userData*/) {} |
||||
|
||||
// TODO(mingcl): Consider if thread safety is a requirement here
|
||||
ndk_util::binder_status_t f_onTransact(ndk_util::AIBinder* binder, |
||||
transaction_code_t code, |
||||
const ndk_util::AParcel* in, |
||||
ndk_util::AParcel* /*out*/) { |
||||
LOG(INFO) << __func__; |
||||
LOG(INFO) << "tx code = " << code; |
||||
|
||||
auto* user_data = |
||||
static_cast<BinderUserData*>(ndk_util::AIBinder_getUserData(binder)); |
||||
TransactionReceiver::OnTransactCb* callback = user_data->callback; |
||||
// Wrap the parcel in a ReadableParcel.
|
||||
std::unique_ptr<ReadableParcel> output = |
||||
std::make_unique<ReadableParcelAndroid>(in); |
||||
// The lock should be released "after" the callback finishes.
|
||||
absl::Status status = |
||||
(*callback)(code, output.get(), ndk_util::AIBinder_getCallingUid()); |
||||
if (status.ok()) { |
||||
return ndk_util::STATUS_OK; |
||||
} else { |
||||
LOG(ERROR) << "Callback failed: " << status.ToString(); |
||||
return ndk_util::STATUS_UNKNOWN_ERROR; |
||||
} |
||||
} |
||||
|
||||
// StdStringAllocator, ReadString, StdVectorAllocator, and ReadVector's
|
||||
// implementations are copied from android/binder_parcel_utils.h
|
||||
// We cannot include the header because it does not compile in C++11
|
||||
|
||||
bool StdStringAllocator(void* stringData, int32_t length, char** buffer) { |
||||
if (length <= 0) return false; |
||||
|
||||
std::string* str = static_cast<std::string*>(stringData); |
||||
str->resize(static_cast<size_t>(length) - 1); |
||||
*buffer = &(*str)[0]; |
||||
return true; |
||||
} |
||||
|
||||
ndk_util::binder_status_t AParcelReadString(const ndk_util::AParcel* parcel, |
||||
std::string* str) { |
||||
void* stringData = static_cast<void*>(str); |
||||
return ndk_util::AParcel_readString(parcel, stringData, StdStringAllocator); |
||||
} |
||||
|
||||
template <typename T> |
||||
bool StdVectorAllocator(void* vectorData, int32_t length, T** outBuffer) { |
||||
if (length < 0) return false; |
||||
|
||||
std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData); |
||||
if (static_cast<size_t>(length) > vec->max_size()) return false; |
||||
|
||||
vec->resize(static_cast<size_t>(length)); |
||||
*outBuffer = vec->data(); |
||||
return true; |
||||
} |
||||
|
||||
ndk_util::binder_status_t AParcelReadVector(const ndk_util::AParcel* parcel, |
||||
std::vector<uint8_t>* vec) { |
||||
void* vectorData = static_cast<void*>(vec); |
||||
return ndk_util::AParcel_readByteArray(parcel, vectorData, |
||||
StdVectorAllocator<int8_t>); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
ndk_util::SpAIBinder FromJavaBinder(JNIEnv* jni_env, jobject binder) { |
||||
return ndk_util::SpAIBinder( |
||||
ndk_util::AIBinder_fromJavaBinder(jni_env, binder)); |
||||
} |
||||
|
||||
TransactionReceiverAndroid::TransactionReceiverAndroid( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
OnTransactCb transact_cb) |
||||
: transact_cb_(transact_cb) { |
||||
// TODO(mingcl): For now interface descriptor is always empty, figure out if
|
||||
// we want it to be something more meaningful (we can probably manually change
|
||||
// interface descriptor by modifying Java code's reply to
|
||||
// os.IBinder.INTERFACE_TRANSACTION)
|
||||
ndk_util::AIBinder_Class* aibinder_class = ndk_util::AIBinder_Class_define( |
||||
/*interfaceDescriptor=*/"", f_onCreate_userdata, f_onDestroy_delete, |
||||
f_onTransact); |
||||
|
||||
ndk_util::AIBinder_Class_disableInterfaceTokenHeader(aibinder_class); |
||||
|
||||
// Pass the on-transact callback to the on-create function of the binder. The
|
||||
// on-create function equips the callback with a mutex and gives it to the
|
||||
// user data stored in the binder which can be retrieved later.
|
||||
// Also Ref() (called implicitly by the copy constructor of RefCountedPtr) the
|
||||
// wire reader so that it would not be destructed during the callback
|
||||
// invocation.
|
||||
OnCreateArgs args; |
||||
args.wire_reader_ref = wire_reader_ref; |
||||
args.callback = &transact_cb_; |
||||
binder_ = ndk_util::AIBinder_new(aibinder_class, &args); |
||||
CHECK(binder_); |
||||
LOG(INFO) << "ndk_util::AIBinder_associateClass = " |
||||
<< ndk_util::AIBinder_associateClass(binder_, aibinder_class); |
||||
} |
||||
|
||||
TransactionReceiverAndroid::~TransactionReceiverAndroid() { |
||||
// Release the binder.
|
||||
ndk_util::AIBinder_decStrong(binder_); |
||||
} |
||||
|
||||
namespace { |
||||
|
||||
ndk_util::binder_status_t f_onTransact_noop(ndk_util::AIBinder* /*binder*/, |
||||
transaction_code_t /*code*/, |
||||
const ndk_util::AParcel* /*in*/, |
||||
ndk_util::AParcel* /*out*/) { |
||||
return {}; |
||||
} |
||||
|
||||
void AssociateWithNoopClass(ndk_util::AIBinder* binder) { |
||||
// Need to associate class before using it
|
||||
ndk_util::AIBinder_Class* aibinder_class = ndk_util::AIBinder_Class_define( |
||||
"", f_onCreate_noop, f_onDestroy_noop, f_onTransact_noop); |
||||
|
||||
ndk_util::AIBinder_Class_disableInterfaceTokenHeader(aibinder_class); |
||||
|
||||
LOG(INFO) << "ndk_util::AIBinder_associateClass = " |
||||
<< ndk_util::AIBinder_associateClass(binder, aibinder_class); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
void BinderAndroid::Initialize() { |
||||
ndk_util::AIBinder* binder = binder_.get(); |
||||
AssociateWithNoopClass(binder); |
||||
} |
||||
|
||||
absl::Status BinderAndroid::PrepareTransaction() { |
||||
ndk_util::AIBinder* binder = binder_.get(); |
||||
return ndk_util::AIBinder_prepareTransaction( |
||||
binder, &input_parcel_->parcel_) == ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError( |
||||
"ndk_util::AIBinder_prepareTransaction failed"); |
||||
} |
||||
|
||||
absl::Status BinderAndroid::Transact(BinderTransportTxCode tx_code) { |
||||
ndk_util::AIBinder* binder = binder_.get(); |
||||
// We only do one-way transaction and thus the output parcel is never used.
|
||||
ndk_util::AParcel* unused_output_parcel; |
||||
absl::Status result = |
||||
(ndk_util::AIBinder_transact( |
||||
binder, static_cast<transaction_code_t>(tx_code), |
||||
&input_parcel_->parcel_, &unused_output_parcel, |
||||
ndk_util::FLAG_ONEWAY) == ndk_util::STATUS_OK) |
||||
? absl::OkStatus() |
||||
: absl::InternalError("ndk_util::AIBinder_transact failed"); |
||||
ndk_util::AParcel_delete(unused_output_parcel); |
||||
return result; |
||||
} |
||||
|
||||
std::unique_ptr<TransactionReceiver> BinderAndroid::ConstructTxReceiver( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb transact_cb) const { |
||||
return std::make_unique<TransactionReceiverAndroid>(wire_reader_ref, |
||||
transact_cb); |
||||
} |
||||
|
||||
int32_t WritableParcelAndroid::GetDataSize() const { |
||||
return ndk_util::AParcel_getDataSize(parcel_); |
||||
} |
||||
|
||||
absl::Status WritableParcelAndroid::WriteInt32(int32_t data) { |
||||
return ndk_util::AParcel_writeInt32(parcel_, data) == ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError("AParcel_writeInt32 failed"); |
||||
} |
||||
|
||||
absl::Status WritableParcelAndroid::WriteInt64(int64_t data) { |
||||
return ndk_util::AParcel_writeInt64(parcel_, data) == ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError("AParcel_writeInt64 failed"); |
||||
} |
||||
|
||||
absl::Status WritableParcelAndroid::WriteBinder(HasRawBinder* binder) { |
||||
return ndk_util::AParcel_writeStrongBinder( |
||||
parcel_, reinterpret_cast<ndk_util::AIBinder*>( |
||||
binder->GetRawBinder())) == ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError("AParcel_writeStrongBinder failed"); |
||||
} |
||||
|
||||
absl::Status WritableParcelAndroid::WriteString(absl::string_view s) { |
||||
return ndk_util::AParcel_writeString(parcel_, s.data(), s.length()) == |
||||
ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError("AParcel_writeString failed"); |
||||
} |
||||
|
||||
absl::Status WritableParcelAndroid::WriteByteArray(const int8_t* buffer, |
||||
int32_t length) { |
||||
return ndk_util::AParcel_writeByteArray(parcel_, buffer, length) == |
||||
ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError("AParcel_writeByteArray failed"); |
||||
} |
||||
|
||||
int32_t ReadableParcelAndroid::GetDataSize() const { |
||||
return ndk_util::AParcel_getDataSize(parcel_); |
||||
} |
||||
|
||||
absl::Status ReadableParcelAndroid::ReadInt32(int32_t* data) { |
||||
return ndk_util::AParcel_readInt32(parcel_, data) == ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError("AParcel_readInt32 failed"); |
||||
} |
||||
|
||||
absl::Status ReadableParcelAndroid::ReadInt64(int64_t* data) { |
||||
return ndk_util::AParcel_readInt64(parcel_, data) == ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError("AParcel_readInt64 failed"); |
||||
} |
||||
|
||||
absl::Status ReadableParcelAndroid::ReadBinder(std::unique_ptr<Binder>* data) { |
||||
ndk_util::AIBinder* binder; |
||||
if (AParcel_readStrongBinder(parcel_, &binder) != ndk_util::STATUS_OK) { |
||||
*data = nullptr; |
||||
return absl::InternalError("AParcel_readStrongBinder failed"); |
||||
} |
||||
*data = std::make_unique<BinderAndroid>(ndk_util::SpAIBinder(binder)); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status ReadableParcelAndroid::ReadByteArray(std::string* data) { |
||||
std::vector<uint8_t> vec; |
||||
if (AParcelReadVector(parcel_, &vec) == ndk_util::STATUS_OK) { |
||||
data->resize(vec.size()); |
||||
if (!vec.empty()) { |
||||
memcpy(&((*data)[0]), vec.data(), vec.size()); |
||||
} |
||||
return absl::OkStatus(); |
||||
} |
||||
return absl::InternalError("AParcel_readByteArray failed"); |
||||
} |
||||
|
||||
absl::Status ReadableParcelAndroid::ReadString(std::string* str) { |
||||
return AParcelReadString(parcel_, str) == ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError("AParcel_readString failed"); |
||||
} |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GPR_SUPPORT_BINDER_TRANSPORT
|
||||
#endif |
@ -1,121 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_ANDROID_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_ANDROID_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
|
||||
#include <jni.h> |
||||
|
||||
#include <memory> |
||||
|
||||
#include "absl/memory/memory.h" |
||||
#include "src/core/ext/transport/binder/utils/binder_auto_utils.h" |
||||
#include "src/core/ext/transport/binder/utils/ndk_binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_reader.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
ndk_util::SpAIBinder FromJavaBinder(JNIEnv* jni_env, jobject binder); |
||||
|
||||
class BinderAndroid; |
||||
|
||||
class WritableParcelAndroid final : public WritableParcel { |
||||
public: |
||||
WritableParcelAndroid() = default; |
||||
explicit WritableParcelAndroid(ndk_util::AParcel* parcel) : parcel_(parcel) {} |
||||
~WritableParcelAndroid() override = default; |
||||
|
||||
int32_t GetDataSize() const override; |
||||
absl::Status WriteInt32(int32_t data) override; |
||||
absl::Status WriteInt64(int64_t data) override; |
||||
absl::Status WriteBinder(HasRawBinder* binder) override; |
||||
absl::Status WriteString(absl::string_view s) override; |
||||
absl::Status WriteByteArray(const int8_t* buffer, int32_t length) override; |
||||
|
||||
private: |
||||
ndk_util::AParcel* parcel_ = nullptr; |
||||
|
||||
friend class BinderAndroid; |
||||
}; |
||||
|
||||
class ReadableParcelAndroid final : public ReadableParcel { |
||||
public: |
||||
ReadableParcelAndroid() = default; |
||||
// TODO(waynetu): Get rid of the const_cast.
|
||||
explicit ReadableParcelAndroid(const ndk_util::AParcel* parcel) |
||||
: parcel_(parcel) {} |
||||
~ReadableParcelAndroid() override = default; |
||||
|
||||
int32_t GetDataSize() const override; |
||||
absl::Status ReadInt32(int32_t* data) override; |
||||
absl::Status ReadInt64(int64_t* data) override; |
||||
absl::Status ReadBinder(std::unique_ptr<Binder>* data) override; |
||||
absl::Status ReadByteArray(std::string* data) override; |
||||
absl::Status ReadString(std::string* str) override; |
||||
|
||||
private: |
||||
const ndk_util::AParcel* parcel_ = nullptr; |
||||
|
||||
friend class BinderAndroid; |
||||
}; |
||||
|
||||
class BinderAndroid final : public Binder { |
||||
public: |
||||
explicit BinderAndroid(ndk_util::SpAIBinder binder) |
||||
: binder_(binder), |
||||
input_parcel_(std::make_unique<WritableParcelAndroid>()) {} |
||||
~BinderAndroid() override = default; |
||||
|
||||
void* GetRawBinder() override { return binder_.get(); } |
||||
|
||||
void Initialize() override; |
||||
absl::Status PrepareTransaction() override; |
||||
absl::Status Transact(BinderTransportTxCode tx_code) override; |
||||
|
||||
WritableParcel* GetWritableParcel() const override { |
||||
return input_parcel_.get(); |
||||
} |
||||
|
||||
std::unique_ptr<TransactionReceiver> ConstructTxReceiver( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb transact_cb) const override; |
||||
|
||||
private: |
||||
ndk_util::SpAIBinder binder_; |
||||
std::unique_ptr<WritableParcelAndroid> input_parcel_; |
||||
}; |
||||
|
||||
class TransactionReceiverAndroid final : public TransactionReceiver { |
||||
public: |
||||
TransactionReceiverAndroid( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
OnTransactCb transaction_cb); |
||||
~TransactionReceiverAndroid() override; |
||||
void* GetRawBinder() override { return binder_; } |
||||
|
||||
private: |
||||
ndk_util::AIBinder* binder_; |
||||
OnTransactCb transact_cb_; |
||||
}; |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GPR_SUPPORT_BINDER_TRANSPORT
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_ANDROID_H
|
@ -1,29 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include "src/core/ext/transport/binder/wire_format/binder_constants.h" |
||||
|
||||
ABSL_CONST_INIT const int FIRST_CALL_TRANSACTION = 0x00000001; |
||||
ABSL_CONST_INIT const int LAST_CALL_TRANSACTION = 0x00FFFFFF; |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
ABSL_CONST_INIT const int kFirstCallId = FIRST_CALL_TRANSACTION + 1000; |
||||
|
||||
} // namespace grpc_binder
|
||||
#endif |
@ -1,43 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_CONSTANTS_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_CONSTANTS_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <cstdint> |
||||
|
||||
#include "absl/base/attributes.h" |
||||
|
||||
using transaction_code_t = uint32_t; |
||||
|
||||
ABSL_CONST_INIT extern const int FIRST_CALL_TRANSACTION; |
||||
ABSL_CONST_INIT extern const int LAST_CALL_TRANSACTION; |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
enum class BinderTransportTxCode : int32_t { |
||||
SETUP_TRANSPORT = 1, |
||||
SHUTDOWN_TRANSPORT = 2, |
||||
ACKNOWLEDGE_BYTES = 3, |
||||
PING = 4, |
||||
PING_RESPONSE = 5, |
||||
}; |
||||
|
||||
ABSL_CONST_INIT extern const int kFirstCallId; |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_CONSTANTS_H
|
@ -1,33 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include "src/core/ext/transport/binder/wire_format/transaction.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
ABSL_CONST_INIT const int kFlagPrefix = 0x1; |
||||
ABSL_CONST_INIT const int kFlagMessageData = 0x2; |
||||
ABSL_CONST_INIT const int kFlagSuffix = 0x4; |
||||
ABSL_CONST_INIT const int kFlagOutOfBandClose = 0x8; |
||||
ABSL_CONST_INIT const int kFlagExpectSingleMessage = 0x10; |
||||
ABSL_CONST_INIT const int kFlagStatusDescription = 0x20; |
||||
ABSL_CONST_INIT const int kFlagMessageDataIsParcelable = 0x40; |
||||
ABSL_CONST_INIT const int kFlagMessageDataIsPartial = 0x80; |
||||
|
||||
} // namespace grpc_binder
|
||||
#endif |
@ -1,106 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_TRANSACTION_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_TRANSACTION_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <string> |
||||
#include <vector> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "src/core/util/crash.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
ABSL_CONST_INIT extern const int kFlagPrefix; |
||||
ABSL_CONST_INIT extern const int kFlagMessageData; |
||||
ABSL_CONST_INIT extern const int kFlagSuffix; |
||||
ABSL_CONST_INIT extern const int kFlagOutOfBandClose; |
||||
ABSL_CONST_INIT extern const int kFlagExpectSingleMessage; |
||||
ABSL_CONST_INIT extern const int kFlagStatusDescription; |
||||
ABSL_CONST_INIT extern const int kFlagMessageDataIsParcelable; |
||||
ABSL_CONST_INIT extern const int kFlagMessageDataIsPartial; |
||||
|
||||
using Metadata = std::vector<std::pair<std::string, std::string>>; |
||||
|
||||
class Transaction { |
||||
public: |
||||
Transaction(int tx_code, bool is_client) |
||||
: tx_code_(tx_code), is_client_(is_client) {} |
||||
// TODO(mingcl): Consider using string_view
|
||||
void SetPrefix(Metadata prefix_metadata) { |
||||
prefix_metadata_ = prefix_metadata; |
||||
CHECK_EQ((flags_ & kFlagPrefix), 0); |
||||
flags_ |= kFlagPrefix; |
||||
} |
||||
void SetMethodRef(std::string method_ref) { |
||||
CHECK(is_client_); |
||||
method_ref_ = method_ref; |
||||
} |
||||
void SetData(std::string message_data) { |
||||
message_data_ = message_data; |
||||
CHECK_EQ((flags_ & kFlagMessageData), 0); |
||||
flags_ |= kFlagMessageData; |
||||
} |
||||
void SetSuffix(Metadata suffix_metadata) { |
||||
if (is_client_) CHECK(suffix_metadata.empty()); |
||||
suffix_metadata_ = suffix_metadata; |
||||
CHECK_EQ((flags_ & kFlagSuffix), 0); |
||||
flags_ |= kFlagSuffix; |
||||
} |
||||
void SetStatusDescription(std::string status_desc) { |
||||
CHECK(!is_client_); |
||||
CHECK_EQ((flags_ & kFlagStatusDescription), 0); |
||||
status_desc_ = status_desc; |
||||
} |
||||
void SetStatus(int status) { |
||||
CHECK(!is_client_); |
||||
CHECK_EQ((flags_ >> 16), 0); |
||||
CHECK(status < (1 << 16)); |
||||
flags_ |= (status << 16); |
||||
} |
||||
|
||||
bool IsClient() const { return is_client_; } |
||||
bool IsServer() const { return !is_client_; } |
||||
int GetTxCode() const { return tx_code_; } |
||||
int GetFlags() const { return flags_; } |
||||
|
||||
absl::string_view GetMethodRef() const { return method_ref_; } |
||||
const Metadata& GetPrefixMetadata() const { return prefix_metadata_; } |
||||
const Metadata& GetSuffixMetadata() const { return suffix_metadata_; } |
||||
absl::string_view GetMessageData() const { return message_data_; } |
||||
absl::string_view GetStatusDesc() const { return status_desc_; } |
||||
|
||||
Transaction(const Transaction&) = delete; |
||||
void operator=(const Transaction&) = delete; |
||||
|
||||
private: |
||||
int tx_code_; |
||||
bool is_client_; |
||||
Metadata prefix_metadata_; |
||||
Metadata suffix_metadata_; |
||||
std::string method_ref_; |
||||
std::string message_data_; |
||||
std::string status_desc_; |
||||
|
||||
int flags_ = 0; |
||||
}; |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_TRANSACTION_H
|
@ -1,38 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_READER_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_READER_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <memory> |
||||
#include <utility> |
||||
|
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_writer.h" |
||||
#include "src/core/util/orphanable.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
class WireReader : public grpc_core::InternallyRefCounted<WireReader> { |
||||
public: |
||||
~WireReader() override = default; |
||||
virtual std::shared_ptr<WireWriter> SetupTransport( |
||||
std::unique_ptr<Binder> endpoint_binder) = 0; |
||||
}; |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_READER_H
|
@ -1,450 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "src/core/ext/transport/binder/wire_format/wire_reader_impl.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <functional> |
||||
#include <limits> |
||||
#include <string> |
||||
#include <utility> |
||||
#include <vector> |
||||
|
||||
#include "absl/functional/any_invocable.h" |
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/memory/memory.h" |
||||
#include "absl/status/statusor.h" |
||||
#include "src/core/ext/transport/binder/utils/transport_stream_receiver.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_writer.h" |
||||
#include "src/core/util/crash.h" |
||||
#include "src/core/util/status_helper.h" |
||||
|
||||
namespace grpc_binder { |
||||
namespace { |
||||
|
||||
const int32_t kWireFormatVersion = 1; |
||||
const char kAuthorityMetadataKey[] = ":authority"; |
||||
|
||||
absl::StatusOr<Metadata> parse_metadata(ReadableParcel* reader) { |
||||
int num_header; |
||||
GRPC_RETURN_IF_ERROR(reader->ReadInt32(&num_header)); |
||||
if (num_header < 0) { |
||||
return absl::InvalidArgumentError("num_header cannot be negative"); |
||||
} |
||||
std::vector<std::pair<std::string, std::string>> ret; |
||||
for (int i = 0; i < num_header; i++) { |
||||
int count; |
||||
GRPC_RETURN_IF_ERROR(reader->ReadInt32(&count)); |
||||
std::string key{}; |
||||
if (count > 0) GRPC_RETURN_IF_ERROR(reader->ReadByteArray(&key)); |
||||
GRPC_RETURN_IF_ERROR(reader->ReadInt32(&count)); |
||||
std::string value{}; |
||||
if (count > 0) GRPC_RETURN_IF_ERROR(reader->ReadByteArray(&value)); |
||||
ret.emplace_back(key, value); |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
WireReaderImpl::WireReaderImpl( |
||||
std::shared_ptr<TransportStreamReceiver> transport_stream_receiver, |
||||
bool is_client, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy, |
||||
std::function<void()> on_destruct_callback) |
||||
: transport_stream_receiver_(std::move(transport_stream_receiver)), |
||||
is_client_(is_client), |
||||
security_policy_(security_policy), |
||||
on_destruct_callback_(on_destruct_callback) {} |
||||
|
||||
WireReaderImpl::~WireReaderImpl() { |
||||
if (on_destruct_callback_) { |
||||
on_destruct_callback_(); |
||||
} |
||||
} |
||||
|
||||
std::shared_ptr<WireWriter> WireReaderImpl::SetupTransport( |
||||
std::unique_ptr<Binder> binder) { |
||||
if (!is_client_) { |
||||
connected_ = true; |
||||
SendSetupTransport(binder.get()); |
||||
{ |
||||
grpc_core::MutexLock lock(&mu_); |
||||
wire_writer_ = std::make_shared<WireWriterImpl>(std::move(binder)); |
||||
} |
||||
wire_writer_ready_notification_.Notify(); |
||||
return wire_writer_; |
||||
} else { |
||||
SendSetupTransport(binder.get()); |
||||
auto other_end_binder = RecvSetupTransport(); |
||||
{ |
||||
grpc_core::MutexLock lock(&mu_); |
||||
connected_ = true; |
||||
wire_writer_ = |
||||
std::make_shared<WireWriterImpl>(std::move(other_end_binder)); |
||||
} |
||||
wire_writer_ready_notification_.Notify(); |
||||
return wire_writer_; |
||||
} |
||||
} |
||||
|
||||
void WireReaderImpl::SendSetupTransport(Binder* binder) { |
||||
binder->Initialize(); |
||||
const absl::Status prep_transaction_status = binder->PrepareTransaction(); |
||||
VLOG(2) << "prepare transaction = " << prep_transaction_status; |
||||
WritableParcel* writable_parcel = binder->GetWritableParcel(); |
||||
const absl::Status write_status = |
||||
writable_parcel->WriteInt32(kWireFormatVersion); |
||||
VLOG(2) << "write int32 = " << write_status; |
||||
// The lifetime of the transaction receiver is the same as the wire writer's.
|
||||
// The transaction receiver is responsible for not calling the on-transact
|
||||
// callback when it's dead.
|
||||
// Give TransactionReceiver a Ref() since WireReader cannot be destructed
|
||||
// during callback execution. TransactionReceiver should make sure that the
|
||||
// callback owns a Ref() when it's being invoked.
|
||||
tx_receiver_ = binder->ConstructTxReceiver( |
||||
/*wire_reader_ref=*/Ref(), |
||||
[this](transaction_code_t code, ReadableParcel* readable_parcel, |
||||
int uid) { |
||||
return this->ProcessTransaction(code, readable_parcel, uid); |
||||
}); |
||||
|
||||
VLOG(2) << "tx_receiver = " << tx_receiver_->GetRawBinder(); |
||||
const absl::Status write_binder_status = |
||||
writable_parcel->WriteBinder(tx_receiver_.get()); |
||||
VLOG(2) << "AParcel_writeStrongBinder = " << write_binder_status; |
||||
const absl::Status transact_status = |
||||
binder->Transact(BinderTransportTxCode::SETUP_TRANSPORT); |
||||
VLOG(2) << "AIBinder_transact = " << transact_status; |
||||
} |
||||
|
||||
std::unique_ptr<Binder> WireReaderImpl::RecvSetupTransport() { |
||||
// TODO(b/191941760): avoid blocking, handle wire_writer_noti lifetime
|
||||
// better
|
||||
VLOG(2) << "start waiting for noti"; |
||||
connection_noti_.WaitForNotification(); |
||||
VLOG(2) << "end waiting for noti"; |
||||
return std::move(other_end_binder_); |
||||
} |
||||
|
||||
absl::Status WireReaderImpl::ProcessTransaction(transaction_code_t code, |
||||
ReadableParcel* parcel, |
||||
int uid) { |
||||
if (code >= static_cast<unsigned>(kFirstCallId)) { |
||||
return ProcessStreamingTransaction(code, parcel); |
||||
} |
||||
|
||||
if (!(code >= static_cast<transaction_code_t>( |
||||
BinderTransportTxCode::SETUP_TRANSPORT) && |
||||
code <= static_cast<transaction_code_t>( |
||||
BinderTransportTxCode::PING_RESPONSE))) { |
||||
LOG(INFO) |
||||
<< "Received unknown control message. Shutdown transport gracefully."; |
||||
// TODO(waynetu): Shutdown transport gracefully.
|
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
{ |
||||
grpc_core::MutexLock lock(&mu_); |
||||
if (static_cast<BinderTransportTxCode>(code) != |
||||
BinderTransportTxCode::SETUP_TRANSPORT && |
||||
!connected_) { |
||||
return absl::InvalidArgumentError("Transports not connected yet"); |
||||
} |
||||
} |
||||
|
||||
// TODO(mingcl): See if we want to check the security policy for every RPC
|
||||
// call or just during transport setup.
|
||||
|
||||
switch (static_cast<BinderTransportTxCode>(code)) { |
||||
case BinderTransportTxCode::SETUP_TRANSPORT: { |
||||
grpc_core::MutexLock lock(&mu_); |
||||
if (recvd_setup_transport_) { |
||||
return absl::InvalidArgumentError( |
||||
"Already received a SETUP_TRANSPORT request"); |
||||
} |
||||
recvd_setup_transport_ = true; |
||||
|
||||
VLOG(2) << "calling uid = " << uid; |
||||
if (!security_policy_->IsAuthorized(uid)) { |
||||
return absl::PermissionDeniedError( |
||||
"UID " + std::to_string(uid) + |
||||
" is not allowed to connect to this " |
||||
"transport according to security policy."); |
||||
} |
||||
|
||||
int version; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadInt32(&version)); |
||||
VLOG(2) << "The other end respond with version = " << version; |
||||
// We only support this single lowest possible version, so server must
|
||||
// respond that version too.
|
||||
if (version != kWireFormatVersion) { |
||||
LOG(ERROR) << "The other end respond with version = " << version |
||||
<< ", but we requested version " << kWireFormatVersion |
||||
<< ", trying to continue anyway"; |
||||
} |
||||
std::unique_ptr<Binder> binder{}; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadBinder(&binder)); |
||||
if (!binder) { |
||||
return absl::InternalError("Read NULL binder from the parcel"); |
||||
} |
||||
binder->Initialize(); |
||||
other_end_binder_ = std::move(binder); |
||||
connection_noti_.Notify(); |
||||
break; |
||||
} |
||||
case BinderTransportTxCode::SHUTDOWN_TRANSPORT: { |
||||
LOG(ERROR) |
||||
<< "Received SHUTDOWN_TRANSPORT request but not implemented yet."; |
||||
return absl::UnimplementedError("SHUTDOWN_TRANSPORT"); |
||||
} |
||||
case BinderTransportTxCode::ACKNOWLEDGE_BYTES: { |
||||
int64_t num_bytes = -1; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadInt64(&num_bytes)); |
||||
VLOG(2) << "received acknowledge bytes = " << num_bytes; |
||||
if (!wire_writer_ready_notification_.WaitForNotificationWithTimeout( |
||||
absl::Seconds(5))) { |
||||
return absl::DeadlineExceededError( |
||||
"wire_writer_ is not ready in time!"); |
||||
} |
||||
wire_writer_->OnAckReceived(num_bytes); |
||||
break; |
||||
} |
||||
case BinderTransportTxCode::PING: { |
||||
if (is_client_) { |
||||
return absl::FailedPreconditionError("Receive PING request in client"); |
||||
} |
||||
int ping_id = -1; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadInt32(&ping_id)); |
||||
VLOG(2) << "received ping id = " << ping_id; |
||||
// TODO(waynetu): Ping back.
|
||||
break; |
||||
} |
||||
case BinderTransportTxCode::PING_RESPONSE: { |
||||
int value = -1; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadInt32(&value)); |
||||
VLOG(2) << "received ping response = " << value; |
||||
break; |
||||
} |
||||
} |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status WireReaderImpl::ProcessStreamingTransaction( |
||||
transaction_code_t code, ReadableParcel* parcel) { |
||||
bool need_to_send_ack = false; |
||||
int64_t num_bytes = 0; |
||||
// Indicates which callbacks should be cancelled. It will be initialized as
|
||||
// the flags the in-coming transaction carries, and when a particular
|
||||
// callback is completed, the corresponding bit in cancellation_flag will be
|
||||
// set to 0 so that we won't cancel it afterward.
|
||||
int cancellation_flags = 0; |
||||
// The queue saves the actions needed to be done "WITHOUT" `mu_`.
|
||||
// It prevents deadlock against wire writer issues.
|
||||
std::queue<absl::AnyInvocable<void() &&>> deferred_func_queue; |
||||
absl::Status tx_process_result; |
||||
|
||||
{ |
||||
grpc_core::MutexLock lock(&mu_); |
||||
if (!connected_) { |
||||
return absl::InvalidArgumentError("Transports not connected yet"); |
||||
} |
||||
|
||||
tx_process_result = ProcessStreamingTransactionImpl( |
||||
code, parcel, &cancellation_flags, deferred_func_queue); |
||||
if ((num_incoming_bytes_ - num_acknowledged_bytes_) >= |
||||
kFlowControlAckBytes) { |
||||
need_to_send_ack = true; |
||||
num_bytes = num_incoming_bytes_; |
||||
num_acknowledged_bytes_ = num_incoming_bytes_; |
||||
} |
||||
} |
||||
// Executes all actions in the queue.
|
||||
while (!deferred_func_queue.empty()) { |
||||
std::move(deferred_func_queue.front())(); |
||||
deferred_func_queue.pop(); |
||||
} |
||||
|
||||
if (!tx_process_result.ok()) { |
||||
LOG(ERROR) << "Failed to process streaming transaction: " |
||||
<< tx_process_result.ToString(); |
||||
// Something went wrong when receiving transaction. Cancel failed requests.
|
||||
if (cancellation_flags & kFlagPrefix) { |
||||
LOG(INFO) << "cancelling initial metadata"; |
||||
transport_stream_receiver_->NotifyRecvInitialMetadata(code, |
||||
tx_process_result); |
||||
} |
||||
if (cancellation_flags & kFlagMessageData) { |
||||
LOG(INFO) << "cancelling message data"; |
||||
transport_stream_receiver_->NotifyRecvMessage(code, tx_process_result); |
||||
} |
||||
if (cancellation_flags & kFlagSuffix) { |
||||
LOG(INFO) << "cancelling trailing metadata"; |
||||
transport_stream_receiver_->NotifyRecvTrailingMetadata( |
||||
code, tx_process_result, 0); |
||||
} |
||||
} |
||||
|
||||
if (need_to_send_ack) { |
||||
if (!wire_writer_ready_notification_.WaitForNotificationWithTimeout( |
||||
absl::Seconds(5))) { |
||||
return absl::DeadlineExceededError("wire_writer_ is not ready in time!"); |
||||
} |
||||
CHECK(wire_writer_); |
||||
// wire_writer_ should not be accessed while holding mu_!
|
||||
// Otherwise, it is possible that
|
||||
// 1. wire_writer_::mu_ is acquired before mu_ (NDK call back during
|
||||
// transaction)
|
||||
// 2. mu_ is acquired before wire_writer_::mu_ (e.g. Java call back us, and
|
||||
// we call WireWriter::SendAck which will try to acquire wire_writer_::mu_)
|
||||
absl::Status ack_status = wire_writer_->SendAck(num_bytes); |
||||
if (tx_process_result.ok()) { |
||||
return ack_status; |
||||
} |
||||
} |
||||
return tx_process_result; |
||||
} |
||||
|
||||
absl::Status WireReaderImpl::ProcessStreamingTransactionImpl( |
||||
transaction_code_t code, ReadableParcel* parcel, int* cancellation_flags, |
||||
std::queue<absl::AnyInvocable<void() &&>>& deferred_func_queue) { |
||||
CHECK(cancellation_flags); |
||||
num_incoming_bytes_ += parcel->GetDataSize(); |
||||
LOG(INFO) << "Total incoming bytes: " << num_incoming_bytes_; |
||||
|
||||
int flags; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadInt32(&flags)); |
||||
*cancellation_flags = flags; |
||||
|
||||
// Ignore in-coming transaction with flag = 0 to match with Java
|
||||
// implementation.
|
||||
// TODO(waynetu): Check with grpc-java team to see whether this is the
|
||||
// intended behavior.
|
||||
// TODO(waynetu): What should be returned here?
|
||||
if (flags == 0) { |
||||
LOG(INFO) << "[WARNING] Receive empty transaction. Ignored."; |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
int status = flags >> 16; |
||||
VLOG(2) << "status = " << status; |
||||
VLOG(2) << "FLAG_PREFIX = " << (flags & kFlagPrefix); |
||||
VLOG(2) << "FLAG_MESSAGE_DATA = " << (flags & kFlagMessageData); |
||||
VLOG(2) << "FLAG_SUFFIX = " << (flags & kFlagSuffix); |
||||
int seq_num; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadInt32(&seq_num)); |
||||
// TODO(waynetu): For now we'll just assume that the transactions commit in
|
||||
// the same order they're issued. The following assertion detects
|
||||
// out-of-order or missing transactions. WireReaderImpl should be fixed if
|
||||
// we indeed found such behavior.
|
||||
int32_t& expectation = expected_seq_num_[code]; |
||||
if (seq_num < 0 || seq_num != expectation) { |
||||
// Unexpected sequence number.
|
||||
return absl::InternalError("Unexpected sequence number"); |
||||
} |
||||
// TODO(waynetu): According to the protocol, "The sequence number will wrap
|
||||
// around to 0 if more than 2^31 messages are sent." For now we'll just
|
||||
// assert that it never reach such circumstances.
|
||||
CHECK(expectation < std::numeric_limits<int32_t>::max()) |
||||
<< "Sequence number too large"; |
||||
expectation++; |
||||
VLOG(2) << "sequence number = " << seq_num; |
||||
if (flags & kFlagPrefix) { |
||||
std::string method_ref; |
||||
if (!is_client_) { |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadString(&method_ref)); |
||||
} |
||||
absl::StatusOr<Metadata> initial_metadata_or_error = parse_metadata(parcel); |
||||
if (!initial_metadata_or_error.ok()) { |
||||
return initial_metadata_or_error.status(); |
||||
} |
||||
if (!is_client_) { |
||||
// In BinderChannel wireformat specification, path is not encoded as part
|
||||
// of metadata. So we extract the path and turn it into metadata here
|
||||
// (this is what core API layer expects).
|
||||
initial_metadata_or_error->emplace_back(":path", |
||||
std::string("/") + method_ref); |
||||
// Since authority metadata is not part of BinderChannel wireformat
|
||||
// specification, and gRPC core API layer expects the presence of
|
||||
// authority for message sent from client to server, we add one if
|
||||
// missing (it will be missing if client grpc-java).
|
||||
bool has_authority = false; |
||||
for (const auto& p : *initial_metadata_or_error) { |
||||
if (p.first == kAuthorityMetadataKey) has_authority = true; |
||||
} |
||||
if (!has_authority) { |
||||
initial_metadata_or_error->emplace_back(kAuthorityMetadataKey, |
||||
"binder.authority"); |
||||
} |
||||
} |
||||
deferred_func_queue.emplace([this, code, |
||||
initial_metadata_or_error = std::move( |
||||
initial_metadata_or_error)]() mutable { |
||||
this->transport_stream_receiver_->NotifyRecvInitialMetadata( |
||||
code, std::move(initial_metadata_or_error)); |
||||
}); |
||||
*cancellation_flags &= ~kFlagPrefix; |
||||
} |
||||
if (flags & kFlagMessageData) { |
||||
int count; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadInt32(&count)); |
||||
VLOG(2) << "count = " << count; |
||||
std::string msg_data{}; |
||||
if (count > 0) { |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadByteArray(&msg_data)); |
||||
} |
||||
message_buffer_[code] += msg_data; |
||||
if ((flags & kFlagMessageDataIsPartial) == 0) { |
||||
std::string s = std::move(message_buffer_[code]); |
||||
message_buffer_.erase(code); |
||||
deferred_func_queue.emplace([this, code, s = std::move(s)]() mutable { |
||||
this->transport_stream_receiver_->NotifyRecvMessage(code, std::move(s)); |
||||
}); |
||||
} |
||||
*cancellation_flags &= ~kFlagMessageData; |
||||
} |
||||
if (flags & kFlagSuffix) { |
||||
if (flags & kFlagStatusDescription) { |
||||
// FLAG_STATUS_DESCRIPTION set
|
||||
std::string desc; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadString(&desc)); |
||||
VLOG(2) << "description = " << desc; |
||||
} |
||||
Metadata trailing_metadata; |
||||
if (is_client_) { |
||||
absl::StatusOr<Metadata> trailing_metadata_or_error = |
||||
parse_metadata(parcel); |
||||
if (!trailing_metadata_or_error.ok()) { |
||||
return trailing_metadata_or_error.status(); |
||||
} |
||||
trailing_metadata = *trailing_metadata_or_error; |
||||
} |
||||
deferred_func_queue.emplace( |
||||
[this, code, trailing_metadata = std::move(trailing_metadata), |
||||
status]() mutable { |
||||
this->transport_stream_receiver_->NotifyRecvTrailingMetadata( |
||||
code, std::move(trailing_metadata), status); |
||||
}); |
||||
*cancellation_flags &= ~kFlagSuffix; |
||||
} |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
} // namespace grpc_binder
|
||||
#endif |
@ -1,158 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_READER_IMPL_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_READER_IMPL_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
|
||||
#include <memory> |
||||
#include <queue> |
||||
#include <utility> |
||||
|
||||
#include "absl/container/flat_hash_map.h" |
||||
#include "absl/functional/any_invocable.h" |
||||
#include "src/core/ext/transport/binder/utils/transport_stream_receiver.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_reader.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_writer.h" |
||||
#include "src/core/util/notification.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
class WireReaderImpl : public WireReader { |
||||
public: |
||||
WireReaderImpl( |
||||
std::shared_ptr<TransportStreamReceiver> transport_stream_receiver, |
||||
bool is_client, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy, |
||||
std::function<void()> on_destruct_callback = nullptr); |
||||
~WireReaderImpl() override; |
||||
|
||||
void Orphan() override { Unref(); } |
||||
|
||||
/// Setup the transport between endpoint binders.
|
||||
///
|
||||
/// The client and the server both call SetupTransport() when constructing
|
||||
/// transport.
|
||||
///
|
||||
/// High-level overview of transaction setup:
|
||||
/// 0. Client obtains an |endpoint_binder| from the server (in the Android
|
||||
/// setting, this can be achieved by "binding" to the server APK).
|
||||
/// 1. Client creates a binder |client_binder| and hook its on-transaction
|
||||
/// callback to client's ProcessTransaction(). Client then sends
|
||||
/// |client_binder| through |endpoint_binder| to server.
|
||||
/// 2. Server receives |client_binder| via |endpoint_binder|.
|
||||
/// 3. Server creates a binder |server_binder| and hook its on-transaction
|
||||
/// callback to server's ProcessTransaction(). Server then sends
|
||||
/// |server_binder| through |client_binder| back to the client.
|
||||
/// 4. Client receives |server_binder| via |client_binder|'s on-transaction
|
||||
/// callback.
|
||||
///
|
||||
/// The parameter \p binder here means different things for client nad server.
|
||||
/// For client, \p binder refers to |endpoint_binder|, and for server, \p
|
||||
/// binder refers to |client_binder|. That is, for server-side transport
|
||||
/// setup, we assume that the first half of SETUP_TRANSPORT (up to step 2) is
|
||||
/// already done somewhere else (see test/end2end/binder_transport_test.cc for
|
||||
/// how it's handled in the testing environment).
|
||||
std::shared_ptr<WireWriter> SetupTransport( |
||||
std::unique_ptr<Binder> binder) override; |
||||
|
||||
absl::Status ProcessTransaction(transaction_code_t code, |
||||
ReadableParcel* parcel, int uid); |
||||
|
||||
/// Send SETUP_TRANSPORT request through \p binder.
|
||||
///
|
||||
/// This is the one half (for client it's the first half, and for server it's
|
||||
/// the second) of the SETUP_TRANSPORT negotiation process. First, a new
|
||||
/// binder is created. We take its "receiving" part and construct the
|
||||
/// transaction receiver with it, and sends the "sending" part along with the
|
||||
/// SETUP_TRANSPORT message through \p binder.
|
||||
void SendSetupTransport(Binder* binder); |
||||
|
||||
/// Recv SETUP_TRANSPORT request.
|
||||
///
|
||||
/// This is the other half of the SETUP_TRANSPORT process. We wait for
|
||||
/// in-coming SETUP_TRANSPORT request with the "sending" part of a binder from
|
||||
/// the other end. For client, the message is coming from the transaction
|
||||
/// receiver we just constructed in SendSetupTransport(). For server, we
|
||||
/// assume that this step is already completed.
|
||||
// TODO(waynetu): In the testing environment, we still use this method (on
|
||||
// another WireReader instance) for server-side transport setup, and thus it
|
||||
// is marked as public. Try moving this method back to private, and hopefully
|
||||
// we can also avoid moving |other_end_binder_| out in the implementation.
|
||||
std::unique_ptr<Binder> RecvSetupTransport(); |
||||
|
||||
private: |
||||
absl::Status ProcessStreamingTransaction(transaction_code_t code, |
||||
ReadableParcel* parcel); |
||||
absl::Status ProcessStreamingTransactionImpl( |
||||
transaction_code_t code, ReadableParcel* parcel, int* cancellation_flags, |
||||
// The queue saves the actions needed to be done "WITHOUT" `mu_`.
|
||||
// It prevents deadlock against wire writer issues.
|
||||
std::queue<absl::AnyInvocable<void() &&>>& deferred_func_queue) |
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); |
||||
|
||||
std::shared_ptr<TransportStreamReceiver> transport_stream_receiver_; |
||||
grpc_core::Notification connection_noti_; |
||||
grpc_core::Mutex mu_; |
||||
std::atomic_bool connected_{false}; |
||||
bool recvd_setup_transport_ ABSL_GUARDED_BY(mu_) = false; |
||||
// NOTE: other_end_binder_ will be moved out when RecvSetupTransport() is
|
||||
// called. Be cautious not to access it afterward.
|
||||
std::unique_ptr<Binder> other_end_binder_; |
||||
absl::flat_hash_map<transaction_code_t, int32_t> expected_seq_num_ |
||||
ABSL_GUARDED_BY(mu_); |
||||
absl::flat_hash_map<transaction_code_t, std::string> message_buffer_ |
||||
ABSL_GUARDED_BY(mu_); |
||||
std::unique_ptr<TransactionReceiver> tx_receiver_; |
||||
bool is_client_; |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy_; |
||||
// When WireReaderImpl gets destructed, call on_destruct_callback_. This is
|
||||
// mostly for decrementing the reference count of its transport.
|
||||
std::function<void()> on_destruct_callback_; |
||||
|
||||
// ACK every 16k bytes.
|
||||
static constexpr int64_t kFlowControlAckBytes = 16 * 1024; |
||||
int64_t num_incoming_bytes_ ABSL_GUARDED_BY(mu_) = 0; |
||||
int64_t num_acknowledged_bytes_ ABSL_GUARDED_BY(mu_) = 0; |
||||
|
||||
// Used to send ACK.
|
||||
std::shared_ptr<WireWriter> wire_writer_; |
||||
|
||||
// Workaround for race condition.
|
||||
//
|
||||
// In `SetupTransport()`, we set `connected_` to true, call
|
||||
// `SendSetupTransport()`, and construct `wire_writer_`. There is a potential
|
||||
// race condition between calling `SendSetupTransport()` and constructing
|
||||
// `wire_writer_`. So use this notification to wait. This should be very fast
|
||||
// and waiting is acceptable.
|
||||
//
|
||||
// The original problem was that we can't move `connected_ = true` and
|
||||
// `SendSetupTransport()` into the mutex, as it will deadlock if
|
||||
// `ProcessTransaction()` is called in the same call chain.
|
||||
//
|
||||
// Note: this is not the perfect solution, the system will still deadlock if,
|
||||
// e.g., the first request is 64K and we entered the sending ACK code path.
|
||||
//
|
||||
// TODO(littlecvr): Figure out a better solution to not causing any potential
|
||||
// deadlock and not having to wait.
|
||||
grpc_core::Notification wire_writer_ready_notification_; |
||||
}; |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_READER_IMPL_H
|
@ -1,393 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "src/core/ext/transport/binder/wire_format/wire_writer.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <utility> |
||||
|
||||
#include "absl/cleanup/cleanup.h" |
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/types/variant.h" |
||||
#include "src/core/lib/event_engine/default_event_engine.h" |
||||
#include "src/core/util/crash.h" |
||||
|
||||
#define RETURN_IF_ERROR(expr) \ |
||||
do { \
|
||||
const absl::Status status = (expr); \
|
||||
if (!status.ok()) return status; \
|
||||
} while (0) |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
bool CanBeSentInOneTransaction(const Transaction& tx) { |
||||
return (tx.GetFlags() & kFlagMessageData) == 0 || |
||||
static_cast<int64_t>(tx.GetMessageData().size()) <= |
||||
WireWriterImpl::kBlockSize; |
||||
} |
||||
|
||||
// Simply forward the call to `WireWriterImpl::RunScheduledTx`.
|
||||
void RunScheduledTx(void* arg, grpc_error_handle /*error*/) { |
||||
auto* run_scheduled_tx_args = |
||||
static_cast<WireWriterImpl::RunScheduledTxArgs*>(arg); |
||||
run_scheduled_tx_args->writer->RunScheduledTxInternal(run_scheduled_tx_args); |
||||
} |
||||
|
||||
absl::Status WriteInitialMetadata(const Transaction& tx, |
||||
WritableParcel* parcel) { |
||||
if (tx.IsClient()) { |
||||
// Only client sends method ref.
|
||||
RETURN_IF_ERROR(parcel->WriteString(tx.GetMethodRef())); |
||||
} |
||||
RETURN_IF_ERROR(parcel->WriteInt32(tx.GetPrefixMetadata().size())); |
||||
for (const auto& md : tx.GetPrefixMetadata()) { |
||||
RETURN_IF_ERROR(parcel->WriteByteArrayWithLength(md.first)); |
||||
RETURN_IF_ERROR(parcel->WriteByteArrayWithLength(md.second)); |
||||
} |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status WriteTrailingMetadata(const Transaction& tx, |
||||
WritableParcel* parcel) { |
||||
if (tx.IsServer()) { |
||||
if (tx.GetFlags() & kFlagStatusDescription) { |
||||
RETURN_IF_ERROR(parcel->WriteString(tx.GetStatusDesc())); |
||||
} |
||||
RETURN_IF_ERROR(parcel->WriteInt32(tx.GetSuffixMetadata().size())); |
||||
for (const auto& md : tx.GetSuffixMetadata()) { |
||||
RETURN_IF_ERROR(parcel->WriteByteArrayWithLength(md.first)); |
||||
RETURN_IF_ERROR(parcel->WriteByteArrayWithLength(md.second)); |
||||
} |
||||
} else { |
||||
// client suffix currently is always empty according to the wireformat
|
||||
if (!tx.GetSuffixMetadata().empty()) { |
||||
LOG(ERROR) << "Got non-empty suffix metadata from client."; |
||||
} |
||||
} |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
WireWriterImpl::WireWriterImpl(std::unique_ptr<Binder> binder) |
||||
: binder_(std::move(binder)), |
||||
combiner_(grpc_combiner_create( |
||||
grpc_event_engine::experimental::GetDefaultEventEngine())) {} |
||||
|
||||
WireWriterImpl::~WireWriterImpl() { |
||||
GRPC_COMBINER_UNREF(combiner_, "wire_writer_impl"); |
||||
while (!pending_outgoing_tx_.empty()) { |
||||
delete pending_outgoing_tx_.front(); |
||||
pending_outgoing_tx_.pop(); |
||||
} |
||||
} |
||||
|
||||
// Flow control constant are specified at
|
||||
// https://github.com/grpc/proposal/blob/master/L73-java-binderchannel/wireformat.md#flow-control
|
||||
const int64_t WireWriterImpl::kBlockSize = 16 * 1024; |
||||
const int64_t WireWriterImpl::kFlowControlWindowSize = 128 * 1024; |
||||
|
||||
absl::Status WireWriterImpl::MakeBinderTransaction( |
||||
BinderTransportTxCode tx_code, |
||||
std::function<absl::Status(WritableParcel*)> fill_parcel) { |
||||
grpc_core::MutexLock lock(&write_mu_); |
||||
RETURN_IF_ERROR(binder_->PrepareTransaction()); |
||||
WritableParcel* parcel = binder_->GetWritableParcel(); |
||||
RETURN_IF_ERROR(fill_parcel(parcel)); |
||||
// Only stream transaction is accounted in flow control spec.
|
||||
if (static_cast<int32_t>(tx_code) >= kFirstCallId) { |
||||
int64_t parcel_size = parcel->GetDataSize(); |
||||
if (parcel_size > 2 * kBlockSize) { |
||||
LOG(ERROR) << "Unexpected large transaction (possibly caused by a very " |
||||
"large metadata). This might overflow the binder " |
||||
"transaction buffer. Size: " |
||||
<< parcel_size << " bytes"; |
||||
} |
||||
num_outgoing_bytes_ += parcel_size; |
||||
LOG(INFO) << "Total outgoing bytes: " << num_outgoing_bytes_.load(); |
||||
} |
||||
CHECK(!is_transacting_); |
||||
is_transacting_ = true; |
||||
absl::Status result = binder_->Transact(tx_code); |
||||
is_transacting_ = false; |
||||
return result; |
||||
} |
||||
|
||||
absl::Status WireWriterImpl::RpcCallFastPath(std::unique_ptr<Transaction> tx) { |
||||
return MakeBinderTransaction( |
||||
static_cast<BinderTransportTxCode>(tx->GetTxCode()), |
||||
[this, tx = tx.get()]( |
||||
WritableParcel* parcel) ABSL_EXCLUSIVE_LOCKS_REQUIRED(write_mu_) { |
||||
RETURN_IF_ERROR(parcel->WriteInt32(tx->GetFlags())); |
||||
RETURN_IF_ERROR(parcel->WriteInt32(next_seq_num_[tx->GetTxCode()]++)); |
||||
if (tx->GetFlags() & kFlagPrefix) { |
||||
RETURN_IF_ERROR(WriteInitialMetadata(*tx, parcel)); |
||||
} |
||||
if (tx->GetFlags() & kFlagMessageData) { |
||||
RETURN_IF_ERROR( |
||||
parcel->WriteByteArrayWithLength(tx->GetMessageData())); |
||||
} |
||||
if (tx->GetFlags() & kFlagSuffix) { |
||||
RETURN_IF_ERROR(WriteTrailingMetadata(*tx, parcel)); |
||||
} |
||||
return absl::OkStatus(); |
||||
}); |
||||
} |
||||
|
||||
absl::Status WireWriterImpl::RunStreamTx( |
||||
RunScheduledTxArgs::StreamTx* stream_tx, WritableParcel* parcel, |
||||
bool* is_last_chunk) { |
||||
Transaction* tx = stream_tx->tx.get(); |
||||
// Transaction without data flag should go to fast path.
|
||||
CHECK(tx->GetFlags() & kFlagMessageData); |
||||
|
||||
absl::string_view data = tx->GetMessageData(); |
||||
CHECK(stream_tx->bytes_sent <= static_cast<int64_t>(data.size())); |
||||
|
||||
int flags = kFlagMessageData; |
||||
|
||||
if (stream_tx->bytes_sent == 0) { |
||||
// This is the first transaction. Include initial
|
||||
// metadata if there's any.
|
||||
if (tx->GetFlags() & kFlagPrefix) { |
||||
flags |= kFlagPrefix; |
||||
} |
||||
} |
||||
// There is also prefix/suffix in transaction beside the transaction data so
|
||||
// actual transaction size will be greater than `kBlockSize`. This is
|
||||
// unavoidable because we cannot split the prefix metadata and trailing
|
||||
// metadata into different binder transactions. In most cases this is fine
|
||||
// because single transaction size is not required to be strictly lower than
|
||||
// `kBlockSize`, as long as it won't overflow Android's binder buffer.
|
||||
int64_t size = std::min<int64_t>(WireWriterImpl::kBlockSize, |
||||
data.size() - stream_tx->bytes_sent); |
||||
if (stream_tx->bytes_sent + WireWriterImpl::kBlockSize >= |
||||
static_cast<int64_t>(data.size())) { |
||||
// This is the last transaction. Include trailing
|
||||
// metadata if there's any.
|
||||
if (tx->GetFlags() & kFlagSuffix) { |
||||
flags |= kFlagSuffix; |
||||
} |
||||
size = data.size() - stream_tx->bytes_sent; |
||||
*is_last_chunk = true; |
||||
} else { |
||||
// There are more messages to send.
|
||||
flags |= kFlagMessageDataIsPartial; |
||||
*is_last_chunk = false; |
||||
} |
||||
RETURN_IF_ERROR(parcel->WriteInt32(flags)); |
||||
RETURN_IF_ERROR(parcel->WriteInt32(next_seq_num_[tx->GetTxCode()]++)); |
||||
if (flags & kFlagPrefix) { |
||||
RETURN_IF_ERROR(WriteInitialMetadata(*tx, parcel)); |
||||
} |
||||
RETURN_IF_ERROR(parcel->WriteByteArrayWithLength( |
||||
data.substr(stream_tx->bytes_sent, size))); |
||||
if (flags & kFlagSuffix) { |
||||
RETURN_IF_ERROR(WriteTrailingMetadata(*tx, parcel)); |
||||
} |
||||
stream_tx->bytes_sent += size; |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
void WireWriterImpl::RunScheduledTxInternal(RunScheduledTxArgs* args) { |
||||
CHECK(args->writer == this); |
||||
if (absl::holds_alternative<RunScheduledTxArgs::AckTx>(args->tx)) { |
||||
int64_t num_bytes = |
||||
absl::get<RunScheduledTxArgs::AckTx>(args->tx).num_bytes; |
||||
absl::Status result = |
||||
MakeBinderTransaction(BinderTransportTxCode::ACKNOWLEDGE_BYTES, |
||||
[num_bytes](WritableParcel* parcel) { |
||||
RETURN_IF_ERROR(parcel->WriteInt64(num_bytes)); |
||||
return absl::OkStatus(); |
||||
}); |
||||
if (!result.ok()) { |
||||
LOG(ERROR) << "Failed to make binder transaction " << result; |
||||
} |
||||
delete args; |
||||
return; |
||||
} |
||||
CHECK(absl::holds_alternative<RunScheduledTxArgs::StreamTx>(args->tx)); |
||||
RunScheduledTxArgs::StreamTx* stream_tx = |
||||
&absl::get<RunScheduledTxArgs::StreamTx>(args->tx); |
||||
// Be reservative. Decrease CombinerTxCount after the data size of this
|
||||
// transaction has already been added to `num_outgoing_bytes_`, to make sure
|
||||
// we never underestimate `num_outgoing_bytes_`.
|
||||
auto decrease_combiner_tx_count = absl::MakeCleanup([this]() { |
||||
{ |
||||
grpc_core::MutexLock lock(&flow_control_mu_); |
||||
CHECK_GT(num_non_acked_tx_in_combiner_, 0); |
||||
num_non_acked_tx_in_combiner_--; |
||||
} |
||||
// New transaction might be ready to be scheduled.
|
||||
TryScheduleTransaction(); |
||||
}); |
||||
if (CanBeSentInOneTransaction(*stream_tx->tx.get())) { // NOLINT
|
||||
absl::Status result = RpcCallFastPath(std::move(stream_tx->tx)); |
||||
if (!result.ok()) { |
||||
LOG(ERROR) << "Failed to handle non-chunked RPC call " << result; |
||||
} |
||||
delete args; |
||||
return; |
||||
} |
||||
bool is_last_chunk = true; |
||||
absl::Status result = MakeBinderTransaction( |
||||
static_cast<BinderTransportTxCode>(stream_tx->tx->GetTxCode()), |
||||
[stream_tx, &is_last_chunk, this](WritableParcel* parcel) |
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(write_mu_) { |
||||
return RunStreamTx(stream_tx, parcel, &is_last_chunk); |
||||
}); |
||||
if (!result.ok()) { |
||||
LOG(ERROR) << "Failed to make binder transaction " << result; |
||||
} |
||||
if (!is_last_chunk) { |
||||
{ |
||||
grpc_core::MutexLock lock(&flow_control_mu_); |
||||
pending_outgoing_tx_.push(args); |
||||
} |
||||
TryScheduleTransaction(); |
||||
} else { |
||||
delete args; |
||||
} |
||||
} |
||||
|
||||
absl::Status WireWriterImpl::RpcCall(std::unique_ptr<Transaction> tx) { |
||||
// TODO(mingcl): check tx_code <= last call id
|
||||
CHECK(tx->GetTxCode() >= kFirstCallId); |
||||
auto args = new RunScheduledTxArgs(); |
||||
args->writer = this; |
||||
args->tx = RunScheduledTxArgs::StreamTx(); |
||||
absl::get<RunScheduledTxArgs::StreamTx>(args->tx).tx = std::move(tx); |
||||
absl::get<RunScheduledTxArgs::StreamTx>(args->tx).bytes_sent = 0; |
||||
{ |
||||
grpc_core::MutexLock lock(&flow_control_mu_); |
||||
pending_outgoing_tx_.push(args); |
||||
} |
||||
TryScheduleTransaction(); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status WireWriterImpl::SendAck(int64_t num_bytes) { |
||||
// Ensure combiner will be run if this is not called from top-level gRPC API
|
||||
// entrypoint.
|
||||
grpc_core::ExecCtx exec_ctx; |
||||
LOG(INFO) << "Ack " << num_bytes << " bytes received"; |
||||
if (is_transacting_) { |
||||
// This can happen because NDK might call our registered callback function
|
||||
// in the same thread while we are telling it to send a transaction
|
||||
// `is_transacting_` will be true. `Binder::Transact` is now being called on
|
||||
// the same thread or the other thread. We are currently in the call stack
|
||||
// of other transaction, Liveness of ACK is still guaranteed even if this is
|
||||
// a race with another thread.
|
||||
LOG(INFO) << "Scheduling ACK transaction instead of directly execute it to " |
||||
"avoid deadlock."; |
||||
auto args = new RunScheduledTxArgs(); |
||||
args->writer = this; |
||||
args->tx = RunScheduledTxArgs::AckTx(); |
||||
absl::get<RunScheduledTxArgs::AckTx>(args->tx).num_bytes = num_bytes; |
||||
auto cl = GRPC_CLOSURE_CREATE(RunScheduledTx, args, nullptr); |
||||
combiner_->Run(cl, absl::OkStatus()); |
||||
return absl::OkStatus(); |
||||
} |
||||
// Otherwise, we can directly send ack.
|
||||
absl::Status result = |
||||
MakeBinderTransaction((BinderTransportTxCode::ACKNOWLEDGE_BYTES), |
||||
[num_bytes](WritableParcel* parcel) { |
||||
RETURN_IF_ERROR(parcel->WriteInt64(num_bytes)); |
||||
return absl::OkStatus(); |
||||
}); |
||||
if (!result.ok()) { |
||||
LOG(ERROR) << "Failed to make binder transaction " << result; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
void WireWriterImpl::OnAckReceived(int64_t num_bytes) { |
||||
// Ensure combiner will be run if this is not called from top-level gRPC API
|
||||
// entrypoint.
|
||||
grpc_core::ExecCtx exec_ctx; |
||||
LOG(INFO) << "OnAckReceived " << num_bytes; |
||||
// Do not try to obtain `write_mu_` in this function. NDKBinder might invoke
|
||||
// the callback to notify us about new incoming binder transaction when we are
|
||||
// sending transaction. i.e. `write_mu_` might have already been acquired by
|
||||
// this thread.
|
||||
{ |
||||
grpc_core::MutexLock lock(&flow_control_mu_); |
||||
num_acknowledged_bytes_ = std::max(num_acknowledged_bytes_, num_bytes); |
||||
int64_t num_outgoing_bytes = num_outgoing_bytes_; |
||||
if (num_acknowledged_bytes_ > num_outgoing_bytes) { |
||||
LOG(ERROR) << "The other end of transport acked more bytes than we ever " |
||||
"sent, " |
||||
<< num_acknowledged_bytes_ << " > " << num_outgoing_bytes; |
||||
} |
||||
} |
||||
TryScheduleTransaction(); |
||||
} |
||||
|
||||
void WireWriterImpl::TryScheduleTransaction() { |
||||
while (true) { |
||||
grpc_core::MutexLock lock(&flow_control_mu_); |
||||
if (pending_outgoing_tx_.empty()) { |
||||
// Nothing to be schduled.
|
||||
break; |
||||
} |
||||
// Number of bytes we have scheduled in combiner but have not yet be
|
||||
// executed by combiner. Here we make an assumption that every binder
|
||||
// transaction will take `kBlockSize`. This should be close to truth when a
|
||||
// large message is being cut to `kBlockSize` chunks.
|
||||
int64_t num_bytes_scheduled_in_combiner = |
||||
num_non_acked_tx_in_combiner_ * kBlockSize; |
||||
// An estimation of number of bytes of traffic we will eventually send to
|
||||
// the other end, assuming all tasks in combiner will be executed and we
|
||||
// receive no new ACK from the other end of transport.
|
||||
int64_t num_total_bytes_will_be_sent = |
||||
num_outgoing_bytes_ + num_bytes_scheduled_in_combiner; |
||||
// An estimation of number of bytes of traffic that will not be
|
||||
// acknowledged, assuming all tasks in combiner will be executed and we
|
||||
// receive no new ack message fomr the other end of transport.
|
||||
int64_t num_non_acked_bytes_estimation = |
||||
num_total_bytes_will_be_sent - num_acknowledged_bytes_; |
||||
if (num_non_acked_bytes_estimation < 0) { |
||||
LOG(ERROR) << "Something went wrong. `num_non_acked_bytes_estimation` " |
||||
"should be non-negative but it is " |
||||
<< num_non_acked_bytes_estimation; |
||||
} |
||||
// If we can schedule another transaction (which has size estimation of
|
||||
// `kBlockSize`) without exceeding `kFlowControlWindowSize`, schedule it.
|
||||
if ((num_non_acked_bytes_estimation + kBlockSize < |
||||
kFlowControlWindowSize)) { |
||||
num_non_acked_tx_in_combiner_++; |
||||
combiner_->Run(GRPC_CLOSURE_CREATE(RunScheduledTx, |
||||
pending_outgoing_tx_.front(), nullptr), |
||||
absl::OkStatus()); |
||||
pending_outgoing_tx_.pop(); |
||||
} else { |
||||
// It is common to fill `kFlowControlWindowSize` completely because
|
||||
// transactions are send at faster rate than the other end of transport
|
||||
// can handle it, so here we use VLOG(2).
|
||||
VLOG(2) << "Some work cannot be scheduled yet due to slow ack from the " |
||||
"other end of transport. This transport might be blocked if " |
||||
"this number don't go down. pending_outgoing_tx_.size() = " |
||||
<< pending_outgoing_tx_.size() |
||||
<< " pending_outgoing_tx_.front() = " |
||||
<< pending_outgoing_tx_.front(); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif |
@ -1,126 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_WRITER_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_WRITER_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <queue> |
||||
#include <string> |
||||
#include <vector> |
||||
|
||||
#include "absl/container/flat_hash_map.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/transaction.h" |
||||
#include "src/core/lib/iomgr/combiner.h" |
||||
#include "src/core/util/sync.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
// Member functions are thread safe.
|
||||
class WireWriter { |
||||
public: |
||||
virtual ~WireWriter() = default; |
||||
virtual absl::Status RpcCall(std::unique_ptr<Transaction> tx) = 0; |
||||
virtual absl::Status SendAck(int64_t num_bytes) = 0; |
||||
virtual void OnAckReceived(int64_t num_bytes) = 0; |
||||
}; |
||||
|
||||
class WireWriterImpl : public WireWriter { |
||||
public: |
||||
explicit WireWriterImpl(std::unique_ptr<Binder> binder); |
||||
~WireWriterImpl() override; |
||||
absl::Status RpcCall(std::unique_ptr<Transaction> tx) override; |
||||
absl::Status SendAck(int64_t num_bytes) override; |
||||
void OnAckReceived(int64_t num_bytes) override; |
||||
|
||||
// Required to be public because we would like to call this in combiner (which
|
||||
// cannot invoke class member function). `RunScheduledTxArgs` and
|
||||
// `RunScheduledTxInternal` should not be used by user directly.
|
||||
struct RunScheduledTxArgs { |
||||
WireWriterImpl* writer; |
||||
struct StreamTx { |
||||
std::unique_ptr<Transaction> tx; |
||||
// How many data in transaction's `data` field has been sent.
|
||||
int64_t bytes_sent = 0; |
||||
}; |
||||
struct AckTx { |
||||
int64_t num_bytes; |
||||
}; |
||||
absl::variant<AckTx, StreamTx> tx; |
||||
}; |
||||
|
||||
void RunScheduledTxInternal(RunScheduledTxArgs* arg); |
||||
|
||||
// Split long message into chunks of size 16k. This doesn't necessarily have
|
||||
// to be the same as the flow control acknowledgement size, but it should not
|
||||
// exceed 128k.
|
||||
static const int64_t kBlockSize; |
||||
|
||||
// Flow control allows sending at most 128k between acknowledgements.
|
||||
static const int64_t kFlowControlWindowSize; |
||||
|
||||
private: |
||||
// Fast path: send data in one transaction.
|
||||
absl::Status RpcCallFastPath(std::unique_ptr<Transaction> tx); |
||||
|
||||
// This function will acquire `write_mu_` to make sure the binder is not used
|
||||
// concurrently, so this can be called by different threads safely.
|
||||
absl::Status MakeBinderTransaction( |
||||
BinderTransportTxCode tx_code, |
||||
std::function<absl::Status(WritableParcel*)> fill_parcel); |
||||
|
||||
// Send a stream to `binder_`. Set `is_last_chunk` to `true` if the stream
|
||||
// transaction has been sent completely. Otherwise set to `false`.
|
||||
absl::Status RunStreamTx(RunScheduledTxArgs::StreamTx* stream_tx, |
||||
WritableParcel* parcel, bool* is_last_chunk) |
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(write_mu_); |
||||
|
||||
// Schdule `RunScheduledTxArgs*` in `pending_outgoing_tx_` to `combiner_`, as
|
||||
// many as possible (under the constraint of `kFlowControlWindowSize`).
|
||||
void TryScheduleTransaction(); |
||||
|
||||
// Guards variables related to transport state.
|
||||
grpc_core::Mutex write_mu_; |
||||
std::unique_ptr<Binder> binder_ ABSL_GUARDED_BY(write_mu_); |
||||
|
||||
// Maps the transaction code (which identifies streams) to their next
|
||||
// available sequence number. See
|
||||
// https://github.com/grpc/proposal/blob/master/L73-java-binderchannel/wireformat.md#sequence-number
|
||||
absl::flat_hash_map<int, int> next_seq_num_ ABSL_GUARDED_BY(write_mu_); |
||||
|
||||
// Number of bytes we have already sent in stream transactions.
|
||||
std::atomic<int64_t> num_outgoing_bytes_{0}; |
||||
|
||||
// Guards variables related to flow control logic.
|
||||
grpc_core::Mutex flow_control_mu_; |
||||
int64_t num_acknowledged_bytes_ ABSL_GUARDED_BY(flow_control_mu_) = 0; |
||||
|
||||
// The queue takes ownership of the pointer.
|
||||
std::queue<RunScheduledTxArgs*> pending_outgoing_tx_ |
||||
ABSL_GUARDED_BY(flow_control_mu_); |
||||
int num_non_acked_tx_in_combiner_ ABSL_GUARDED_BY(flow_control_mu_) = 0; |
||||
|
||||
// Helper variable for determining if we are currently calling into
|
||||
// `Binder::Transact`. Useful for avoiding the attempt of acquiring
|
||||
// `write_mu_` multiple times on the same thread.
|
||||
std::atomic_bool is_transacting_{false}; |
||||
|
||||
grpc_core::Combiner* combiner_; |
||||
}; |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_WRITER_H
|
@ -1,9 +0,0 @@ |
||||
Support for resolving the scheme used by binder transport implementation. |
||||
|
||||
The URI's authority is required to be empty. |
||||
|
||||
The path is used as the identifiers of endpoint binder objects and the length |
||||
limit of the identifier is the same as unix socket length limit. |
||||
|
||||
The length limit of the path should at least be 100 characters long. This is |
||||
guaranteed by `static_assert` in the implementation. |
@ -1,151 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 <grpc/support/port_platform.h> |
||||
|
||||
#include <algorithm> |
||||
|
||||
#include "absl/status/status.h" |
||||
#include "src/core/lib/iomgr/port.h" // IWYU pragma: keep |
||||
#include "src/core/util/status_helper.h" |
||||
|
||||
#ifdef GRPC_HAVE_UNIX_SOCKET |
||||
|
||||
#include <string.h> |
||||
#ifdef GPR_WINDOWS |
||||
// clang-format off
|
||||
#include <ws2def.h> |
||||
#include <afunix.h> |
||||
// clang-format on
|
||||
#else |
||||
#include <sys/socket.h> |
||||
#include <sys/un.h> |
||||
#endif // GPR_WINDOWS
|
||||
|
||||
#include <memory> |
||||
#include <utility> |
||||
|
||||
#include "absl/log/log.h" |
||||
#include "absl/status/statusor.h" |
||||
#include "absl/strings/str_cat.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/strings/strip.h" |
||||
#include "src/core/lib/channel/channel_args.h" |
||||
#include "src/core/lib/config/core_configuration.h" |
||||
#include "src/core/lib/iomgr/error.h" |
||||
#include "src/core/lib/iomgr/resolved_address.h" |
||||
#include "src/core/resolver/endpoint_addresses.h" |
||||
#include "src/core/resolver/resolver.h" |
||||
#include "src/core/resolver/resolver_factory.h" |
||||
#include "src/core/util/orphanable.h" |
||||
#include "src/core/util/uri.h" |
||||
|
||||
namespace grpc_core { |
||||
namespace { |
||||
|
||||
class BinderResolver final : public Resolver { |
||||
public: |
||||
BinderResolver(EndpointAddressesList addresses, ResolverArgs args) |
||||
: result_handler_(std::move(args.result_handler)), |
||||
addresses_(std::move(addresses)), |
||||
channel_args_(std::move(args.args)) {} |
||||
|
||||
void StartLocked() override { |
||||
Result result; |
||||
result.addresses = std::move(addresses_); |
||||
result.args = channel_args_; |
||||
channel_args_ = ChannelArgs(); |
||||
result_handler_->ReportResult(std::move(result)); |
||||
} |
||||
|
||||
void ShutdownLocked() override {} |
||||
|
||||
private: |
||||
std::unique_ptr<ResultHandler> result_handler_; |
||||
EndpointAddressesList addresses_; |
||||
ChannelArgs channel_args_; |
||||
}; |
||||
|
||||
class BinderResolverFactory final : public ResolverFactory { |
||||
public: |
||||
absl::string_view scheme() const override { return "binder"; } |
||||
|
||||
bool IsValidUri(const URI& uri) const override { |
||||
return ParseUri(uri, nullptr); |
||||
} |
||||
|
||||
OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override { |
||||
EndpointAddressesList addresses; |
||||
if (!ParseUri(args.uri, &addresses)) return nullptr; |
||||
return MakeOrphanable<BinderResolver>(std::move(addresses), |
||||
std::move(args)); |
||||
} |
||||
|
||||
private: |
||||
static grpc_error_handle BinderAddrPopulate( |
||||
absl::string_view path, grpc_resolved_address* resolved_addr) { |
||||
path = absl::StripPrefix(path, "/"); |
||||
if (path.empty()) { |
||||
return GRPC_ERROR_CREATE("path is empty"); |
||||
} |
||||
// Store parsed path in a unix socket so it can be reinterpreted as
|
||||
// sockaddr. An invalid address family (AF_MAX) is set to make sure it won't
|
||||
// be accidentally used.
|
||||
memset(resolved_addr, 0, sizeof(*resolved_addr)); |
||||
struct sockaddr_un* un = |
||||
reinterpret_cast<struct sockaddr_un*>(resolved_addr->addr); |
||||
un->sun_family = AF_MAX; |
||||
static_assert(sizeof(un->sun_path) >= 101, |
||||
"unix socket path size is unexpectedly short"); |
||||
if (path.size() + 1 > sizeof(un->sun_path)) { |
||||
return GRPC_ERROR_CREATE( |
||||
absl::StrCat(path, " is too long to be handled")); |
||||
} |
||||
// `un` has already be set to zero, no need to append null after the string
|
||||
memcpy(un->sun_path, path.data(), path.size()); |
||||
resolved_addr->len = |
||||
static_cast<socklen_t>(sizeof(un->sun_family) + path.size() + 1); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
static bool ParseUri(const URI& uri, EndpointAddressesList* addresses) { |
||||
grpc_resolved_address addr; |
||||
{ |
||||
if (!uri.authority().empty()) { |
||||
LOG(ERROR) << "authority is not supported in binder scheme"; |
||||
return false; |
||||
} |
||||
grpc_error_handle error = BinderAddrPopulate(uri.path(), &addr); |
||||
if (!error.ok()) { |
||||
LOG(ERROR) << StatusToString(error); |
||||
return false; |
||||
} |
||||
} |
||||
if (addresses != nullptr) { |
||||
addresses->emplace_back(addr, ChannelArgs()); |
||||
} |
||||
return true; |
||||
} |
||||
}; |
||||
|
||||
} // namespace
|
||||
|
||||
void RegisterBinderResolver(CoreConfiguration::Builder* builder) { |
||||
builder->resolver_registry()->RegisterResolverFactory( |
||||
std::make_unique<BinderResolverFactory>()); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif |
@ -1,216 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 <memory> |
||||
#include <string> |
||||
#include <utility> |
||||
#include <vector> |
||||
|
||||
#include "absl/status/status.h" |
||||
#include "absl/status/statusor.h" |
||||
#include "gtest/gtest.h" |
||||
#include "src/core/lib/config/core_configuration.h" |
||||
#include "src/core/lib/iomgr/port.h" |
||||
#include "src/core/lib/iomgr/resolved_address.h" |
||||
#include "src/core/resolver/endpoint_addresses.h" |
||||
#include "src/core/resolver/resolver.h" |
||||
#include "src/core/resolver/resolver_factory.h" |
||||
#include "src/core/util/orphanable.h" |
||||
#include "src/core/util/uri.h" |
||||
#include "test/core/test_util/test_config.h" |
||||
|
||||
#ifdef GRPC_HAVE_UNIX_SOCKET |
||||
|
||||
#ifdef GPR_WINDOWS |
||||
// clang-format off
|
||||
#include <ws2def.h> |
||||
#include <afunix.h> |
||||
// clang-format on
|
||||
#else |
||||
#include <sys/socket.h> |
||||
#include <sys/un.h> |
||||
#endif // GPR_WINDOWS
|
||||
|
||||
#include <grpc/grpc.h> |
||||
|
||||
#include "absl/log/log.h" |
||||
#include "src/core/lib/iomgr/exec_ctx.h" |
||||
#include "src/core/resolver/resolver_registry.h" |
||||
|
||||
// Registers the factory with `grpc_core::ResolverRegistry`. Defined in
|
||||
// binder_resolver.cc
|
||||
namespace grpc_core { |
||||
void RegisterBinderResolver(CoreConfiguration::Builder* builder); |
||||
} |
||||
|
||||
namespace { |
||||
|
||||
class BinderResolverTest : public ::testing::Test { |
||||
public: |
||||
BinderResolverTest() { |
||||
factory_ = grpc_core::CoreConfiguration::Get() |
||||
.resolver_registry() |
||||
.LookupResolverFactory("binder"); |
||||
} |
||||
~BinderResolverTest() override {} |
||||
static void SetUpTestSuite() { |
||||
builder_ = |
||||
std::make_unique<grpc_core::CoreConfiguration::WithSubstituteBuilder>( |
||||
[](grpc_core::CoreConfiguration::Builder* builder) { |
||||
BuildCoreConfiguration(builder); |
||||
if (!builder->resolver_registry()->HasResolverFactory("binder")) { |
||||
// Binder resolver will only be registered on platforms that
|
||||
// support binder transport. If it is not registered on current
|
||||
// platform, we manually register it here for testing purpose.
|
||||
RegisterBinderResolver(builder); |
||||
ASSERT_TRUE( |
||||
builder->resolver_registry()->HasResolverFactory("binder")); |
||||
} |
||||
}); |
||||
grpc_init(); |
||||
if (grpc_core::CoreConfiguration::Get() |
||||
.resolver_registry() |
||||
.LookupResolverFactory("binder") == nullptr) { |
||||
} |
||||
} |
||||
static void TearDownTestSuite() { |
||||
grpc_shutdown(); |
||||
builder_.reset(); |
||||
} |
||||
|
||||
void SetUp() override { ASSERT_TRUE(factory_); } |
||||
|
||||
class ResultHandler : public grpc_core::Resolver::ResultHandler { |
||||
public: |
||||
ResultHandler() = default; |
||||
|
||||
explicit ResultHandler(const std::string& expected_binder_id) |
||||
: expect_result_(true), expected_binder_id_(expected_binder_id) {} |
||||
|
||||
void ReportResult(grpc_core::Resolver::Result result) override { |
||||
EXPECT_TRUE(expect_result_); |
||||
ASSERT_TRUE(result.addresses.ok()); |
||||
ASSERT_EQ(result.addresses->size(), 1); |
||||
grpc_core::EndpointAddresses addr = (*result.addresses)[0]; |
||||
const struct sockaddr_un* un = |
||||
reinterpret_cast<const struct sockaddr_un*>(addr.address().addr); |
||||
EXPECT_EQ(addr.address().len, |
||||
sizeof(un->sun_family) + expected_binder_id_.length() + 1); |
||||
EXPECT_EQ(un->sun_family, AF_MAX); |
||||
EXPECT_EQ(un->sun_path, expected_binder_id_); |
||||
} |
||||
|
||||
private: |
||||
// Whether we expect ReportResult function to be invoked
|
||||
bool expect_result_ = false; |
||||
|
||||
std::string expected_binder_id_; |
||||
}; |
||||
|
||||
void TestSucceeds(const char* string, const std::string& expected_path) { |
||||
VLOG(2) << "test: '" << string << "' should be valid for '" |
||||
<< factory_->scheme(); |
||||
grpc_core::ExecCtx exec_ctx; |
||||
absl::StatusOr<grpc_core::URI> uri = grpc_core::URI::Parse(string); |
||||
ASSERT_TRUE(uri.ok()) << uri.status().ToString(); |
||||
grpc_core::ResolverArgs args; |
||||
args.uri = std::move(*uri); |
||||
args.result_handler = |
||||
std::make_unique<BinderResolverTest::ResultHandler>(expected_path); |
||||
grpc_core::OrphanablePtr<grpc_core::Resolver> resolver = |
||||
factory_->CreateResolver(std::move(args)); |
||||
ASSERT_TRUE(resolver != nullptr); |
||||
resolver->StartLocked(); |
||||
} |
||||
|
||||
void TestFails(const char* string) { |
||||
VLOG(2) << "test: '" << string << "' should be invalid for '" |
||||
<< factory_->scheme(); |
||||
grpc_core::ExecCtx exec_ctx; |
||||
absl::StatusOr<grpc_core::URI> uri = grpc_core::URI::Parse(string); |
||||
ASSERT_TRUE(uri.ok()) << uri.status().ToString(); |
||||
grpc_core::ResolverArgs args; |
||||
args.uri = std::move(*uri); |
||||
args.result_handler = std::make_unique<BinderResolverTest::ResultHandler>(); |
||||
grpc_core::OrphanablePtr<grpc_core::Resolver> resolver = |
||||
factory_->CreateResolver(std::move(args)); |
||||
EXPECT_TRUE(resolver == nullptr); |
||||
} |
||||
|
||||
private: |
||||
grpc_core::ResolverFactory* factory_; |
||||
static std::unique_ptr<grpc_core::CoreConfiguration::WithSubstituteBuilder> |
||||
builder_; |
||||
}; |
||||
|
||||
std::unique_ptr<grpc_core::CoreConfiguration::WithSubstituteBuilder> |
||||
BinderResolverTest::builder_; |
||||
|
||||
} // namespace
|
||||
|
||||
// Authority is not allowed
|
||||
TEST_F(BinderResolverTest, AuthorityPresents) { |
||||
TestFails("binder://example"); |
||||
TestFails("binder://google.com"); |
||||
TestFails("binder://google.com/test"); |
||||
} |
||||
|
||||
// Path cannot be empty
|
||||
TEST_F(BinderResolverTest, EmptyPath) { |
||||
TestFails("binder:"); |
||||
TestFails("binder:/"); |
||||
TestFails("binder://"); |
||||
} |
||||
|
||||
TEST_F(BinderResolverTest, PathLength) { |
||||
// Note that we have a static assert in binder_resolver.cc that checks
|
||||
// sizeof(sockaddr_un::sun_path) is greater than 100
|
||||
|
||||
// 100 character path should be fine
|
||||
TestSucceeds(("binder:l" + std::string(98, 'o') + "g").c_str(), |
||||
"l" + std::string(98, 'o') + "g"); |
||||
|
||||
// 200 character path most likely will fail
|
||||
TestFails(("binder:l" + std::string(198, 'o') + "g").c_str()); |
||||
} |
||||
|
||||
TEST_F(BinderResolverTest, SlashPrefixes) { |
||||
TestSucceeds("binder:///test", "test"); |
||||
TestSucceeds("binder:////test", "/test"); |
||||
} |
||||
|
||||
TEST_F(BinderResolverTest, ValidCases) { |
||||
TestSucceeds("binder:[[", "[["); |
||||
TestSucceeds("binder:google!com", "google!com"); |
||||
TestSucceeds("binder:test/", "test/"); |
||||
TestSucceeds("binder:test:", "test:"); |
||||
|
||||
TestSucceeds("binder:e", "e"); |
||||
TestSucceeds("binder:example", "example"); |
||||
TestSucceeds("binder:google.com", "google.com"); |
||||
TestSucceeds("binder:~", "~"); |
||||
TestSucceeds("binder:12345", "12345"); |
||||
TestSucceeds( |
||||
"binder:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._" |
||||
"~", |
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~"); |
||||
} |
||||
|
||||
#endif // GRPC_HAVE_UNIX_SOCKET
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
grpc::testing::TestEnvironment env(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
@ -1,133 +0,0 @@ |
||||
# Copyright 2021 gRPC authors. |
||||
# |
||||
# 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. |
||||
|
||||
load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_cc_test", "grpc_package") |
||||
|
||||
licenses(["notice"]) |
||||
|
||||
grpc_package( |
||||
name = "test/core/transport/binder", |
||||
visibility = "tests", |
||||
) |
||||
|
||||
grpc_cc_library( |
||||
name = "mock_objects", |
||||
testonly = 1, |
||||
srcs = ["mock_objects.cc"], |
||||
hdrs = ["mock_objects.h"], |
||||
external_deps = [ |
||||
"absl/memory", |
||||
"gtest", |
||||
], |
||||
language = "C++", |
||||
deps = [ |
||||
"//:grpc++_binder", |
||||
], |
||||
) |
||||
|
||||
grpc_cc_test( |
||||
name = "wire_writer_test", |
||||
srcs = ["wire_writer_test.cc"], |
||||
external_deps = [ |
||||
"absl/memory", |
||||
"gtest", |
||||
], |
||||
language = "C++", |
||||
tags = ["no_test_ios"], |
||||
uses_event_engine = False, |
||||
uses_polling = False, |
||||
deps = [ |
||||
":mock_objects", |
||||
"//:grpc++_binder", |
||||
"//test/core/test_util:grpc_test_util", |
||||
], |
||||
) |
||||
|
||||
grpc_cc_test( |
||||
name = "wire_reader_test", |
||||
srcs = ["wire_reader_test.cc"], |
||||
external_deps = [ |
||||
"absl/memory", |
||||
"gtest", |
||||
], |
||||
language = "C++", |
||||
tags = ["no_test_ios"], |
||||
uses_event_engine = False, |
||||
uses_polling = False, |
||||
deps = [ |
||||
":mock_objects", |
||||
"//:grpc++_binder", |
||||
"//test/core/test_util:grpc_test_util", |
||||
], |
||||
) |
||||
|
||||
grpc_cc_test( |
||||
name = "transport_stream_receiver_test", |
||||
srcs = ["transport_stream_receiver_test.cc"], |
||||
external_deps = [ |
||||
"absl/memory", |
||||
"gtest", |
||||
], |
||||
language = "C++", |
||||
tags = ["no_test_ios"], |
||||
uses_event_engine = False, |
||||
uses_polling = False, |
||||
deps = [ |
||||
"//:grpc++_binder", |
||||
"//test/core/test_util:grpc_test_util", |
||||
], |
||||
) |
||||
|
||||
grpc_cc_test( |
||||
name = "binder_transport_test", |
||||
srcs = ["binder_transport_test.cc"], |
||||
external_deps = [ |
||||
"absl/memory", |
||||
"absl/strings", |
||||
"gtest", |
||||
], |
||||
language = "C++", |
||||
tags = [ |
||||
# To avoid `symbolizer buffer too small` warning of UBSAN |
||||
"noubsan", |
||||
"no_test_ios", |
||||
], |
||||
uses_event_engine = False, |
||||
uses_polling = False, |
||||
deps = [ |
||||
":mock_objects", |
||||
"//:grpc", |
||||
"//:grpc++_binder", |
||||
"//test/core/test_util:grpc_test_util", |
||||
], |
||||
) |
||||
|
||||
grpc_cc_test( |
||||
name = "endpoint_binder_pool_test", |
||||
srcs = ["endpoint_binder_pool_test.cc"], |
||||
external_deps = [ |
||||
"absl/memory", |
||||
"gtest", |
||||
], |
||||
language = "C++", |
||||
tags = ["no_test_ios"], |
||||
uses_event_engine = False, |
||||
uses_polling = False, |
||||
deps = [ |
||||
":mock_objects", |
||||
"//:grpc", |
||||
"//:grpc++_binder", |
||||
"//test/core/test_util:grpc_test_util", |
||||
], |
||||
) |
@ -1,729 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Unit-tests for grpc_binder_transport
|
||||
//
|
||||
// Verify that a calls to the perform_stream_op of grpc_binder_transport
|
||||
// transform into the correct sequence of binder transactions.
|
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
|
||||
#include <grpc/grpc.h> |
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <memory> |
||||
#include <string> |
||||
#include <vector> |
||||
|
||||
#include "absl/memory/memory.h" |
||||
#include "absl/strings/match.h" |
||||
#include "absl/strings/str_join.h" |
||||
#include "src/core/ext/transport/binder/transport/binder_stream.h" |
||||
#include "src/core/lib/resource_quota/resource_quota.h" |
||||
#include "src/core/util/notification.h" |
||||
#include "test/core/test_util/test_config.h" |
||||
#include "test/core/transport/binder/mock_objects.h" |
||||
|
||||
namespace grpc_binder { |
||||
namespace { |
||||
|
||||
using ::testing::Expectation; |
||||
using ::testing::NiceMock; |
||||
using ::testing::Return; |
||||
|
||||
class BinderTransportTest : public ::testing::Test { |
||||
public: |
||||
BinderTransportTest() |
||||
: transport_(grpc_create_binder_transport_client( |
||||
std::make_unique<NiceMock<MockBinder>>(), |
||||
std::make_shared< |
||||
grpc::experimental::binder::UntrustedSecurityPolicy>())) { |
||||
auto* gbt = reinterpret_cast<grpc_binder_transport*>(transport_); |
||||
gbt->wire_writer = std::make_unique<MockWireWriter>(); |
||||
GRPC_STREAM_REF_INIT(&ref_, 1, nullptr, nullptr, "phony ref"); |
||||
} |
||||
|
||||
~BinderTransportTest() override { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
transport_->Orphan(); |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
for (grpc_binder_stream* gbs : stream_buffer_) { |
||||
gbs->~grpc_binder_stream(); |
||||
gpr_free(gbs); |
||||
} |
||||
} |
||||
|
||||
void PerformStreamOp(grpc_binder_stream* gbs, |
||||
grpc_transport_stream_op_batch* op) { |
||||
transport_->filter_stack_transport()->PerformStreamOp( |
||||
reinterpret_cast<grpc_stream*>(gbs), op); |
||||
} |
||||
|
||||
grpc_binder_transport* GetBinderTransport() { |
||||
return reinterpret_cast<grpc_binder_transport*>(transport_); |
||||
} |
||||
|
||||
grpc_binder_stream* InitNewBinderStream() { |
||||
grpc_binder_stream* gbs = static_cast<grpc_binder_stream*>( |
||||
gpr_malloc(transport_->filter_stack_transport()->SizeOfStream())); |
||||
transport_->filter_stack_transport()->InitStream( |
||||
reinterpret_cast<grpc_stream*>(gbs), &ref_, nullptr, arena_.get()); |
||||
stream_buffer_.push_back(gbs); |
||||
return gbs; |
||||
} |
||||
|
||||
MockWireWriter& GetWireWriter() { |
||||
return *reinterpret_cast<MockWireWriter*>( |
||||
GetBinderTransport()->wire_writer.get()); |
||||
} |
||||
|
||||
static void SetUpTestSuite() { grpc_init(); } |
||||
static void TearDownTestSuite() { grpc_shutdown(); } |
||||
|
||||
protected: |
||||
grpc_core::RefCountedPtr<grpc_core::Arena> arena_ = |
||||
grpc_core::SimpleArenaAllocator()->MakeArena(); |
||||
grpc_core::Transport* transport_; |
||||
grpc_stream_refcount ref_; |
||||
std::vector<grpc_binder_stream*> stream_buffer_; |
||||
}; |
||||
|
||||
void MockCallback(void* arg, grpc_error_handle error); |
||||
|
||||
class MockGrpcClosure { |
||||
public: |
||||
explicit MockGrpcClosure(grpc_core::Notification* notification = nullptr) |
||||
: notification_(notification) { |
||||
GRPC_CLOSURE_INIT(&closure_, MockCallback, this, nullptr); |
||||
} |
||||
|
||||
grpc_closure* GetGrpcClosure() { return &closure_; } |
||||
MOCK_METHOD(void, Callback, (grpc_error_handle), ()); |
||||
|
||||
grpc_core::Notification* notification_; |
||||
|
||||
private: |
||||
grpc_closure closure_; |
||||
}; |
||||
|
||||
void MockCallback(void* arg, grpc_error_handle error) { |
||||
MockGrpcClosure* mock_closure = static_cast<MockGrpcClosure*>(arg); |
||||
mock_closure->Callback(error); |
||||
if (mock_closure->notification_) { |
||||
mock_closure->notification_->Notify(); |
||||
} |
||||
} |
||||
|
||||
std::string MetadataString(const Metadata& a) { |
||||
return absl::StrCat( |
||||
"{", |
||||
absl::StrJoin( |
||||
a, ", ", |
||||
[](std::string* out, const std::pair<std::string, std::string>& kv) { |
||||
out->append( |
||||
absl::StrCat("\"", kv.first, "\": \"", kv.second, "\"")); |
||||
}), |
||||
"}"); |
||||
} |
||||
|
||||
bool MetadataEquivalent(Metadata a, Metadata b) { |
||||
std::sort(a.begin(), a.end()); |
||||
std::sort(b.begin(), b.end()); |
||||
return a == b; |
||||
} |
||||
|
||||
// Matches with transactions having the desired flag, method_ref,
|
||||
// initial_metadata, and message_data.
|
||||
MATCHER_P4(TransactionMatches, flag, method_ref, initial_metadata, message_data, |
||||
"") { |
||||
if (arg->GetFlags() != flag) return false; |
||||
if (flag & kFlagPrefix) { |
||||
if (arg->GetMethodRef() != method_ref) { |
||||
printf("METHOD REF NOT EQ: %s %s\n", |
||||
std::string(arg->GetMethodRef()).c_str(), |
||||
std::string(method_ref).c_str()); |
||||
return false; |
||||
} |
||||
if (!MetadataEquivalent(arg->GetPrefixMetadata(), initial_metadata)) { |
||||
printf("METADATA NOT EQUIVALENT: %s %s\n", |
||||
MetadataString(arg->GetPrefixMetadata()).c_str(), |
||||
MetadataString(initial_metadata).c_str()); |
||||
return false; |
||||
} |
||||
} |
||||
if (flag & kFlagMessageData) { |
||||
if (arg->GetMessageData() != message_data) return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
// Matches with grpc_error having error message containing |msg|.
|
||||
MATCHER_P(GrpcErrorMessageContains, msg, "") { |
||||
return absl::StrContains(grpc_core::StatusToString(arg), msg); |
||||
} |
||||
|
||||
namespace { |
||||
class MetadataEncoder { |
||||
public: |
||||
void Encode(const grpc_core::Slice& key, const grpc_core::Slice& value) { |
||||
metadata_.emplace_back(std::string(key.as_string_view()), |
||||
std::string(value.as_string_view())); |
||||
} |
||||
|
||||
template <typename Which> |
||||
void Encode(Which, const typename Which::ValueType& value) { |
||||
metadata_.emplace_back( |
||||
std::string(Which::key()), |
||||
// NOLINTNEXTLINE(google-readability-casting)
|
||||
std::string(grpc_core::Slice(Which::Encode(value)).as_string_view())); |
||||
} |
||||
|
||||
const Metadata& metadata() const { return metadata_; } |
||||
|
||||
private: |
||||
Metadata metadata_; |
||||
}; |
||||
} // namespace
|
||||
|
||||
// Verify that the lower-level metadata has the same content as the gRPC
|
||||
// metadata.
|
||||
void VerifyMetadataEqual(const Metadata& md, |
||||
const grpc_metadata_batch& grpc_md) { |
||||
MetadataEncoder encoder; |
||||
grpc_md.Encode(&encoder); |
||||
EXPECT_TRUE(MetadataEquivalent(encoder.metadata(), md)); |
||||
} |
||||
|
||||
// RAII helper classes for constructing gRPC metadata and receiving callbacks.
|
||||
struct MakeSendInitialMetadata { |
||||
MakeSendInitialMetadata(const Metadata& initial_metadata, |
||||
const std::string& method_ref, |
||||
grpc_transport_stream_op_batch* op) { |
||||
for (const auto& md : initial_metadata) { |
||||
const std::string& key = md.first; |
||||
const std::string& value = md.second; |
||||
grpc_initial_metadata.Append( |
||||
key, grpc_core::Slice::FromCopiedString(value), |
||||
[](absl::string_view, const grpc_core::Slice&) { abort(); }); |
||||
} |
||||
if (!method_ref.empty()) { |
||||
grpc_initial_metadata.Set(grpc_core::HttpPathMetadata(), |
||||
grpc_core::Slice::FromCopiedString(method_ref)); |
||||
} |
||||
op->send_initial_metadata = true; |
||||
op->payload->send_initial_metadata.send_initial_metadata = |
||||
&grpc_initial_metadata; |
||||
} |
||||
~MakeSendInitialMetadata() {} |
||||
|
||||
grpc_metadata_batch grpc_initial_metadata; |
||||
}; |
||||
|
||||
struct MakeSendMessage { |
||||
MakeSendMessage(const std::string& message, |
||||
grpc_transport_stream_op_batch* op) { |
||||
send_stream.Append(grpc_core::Slice::FromCopiedString(message)); |
||||
|
||||
op->send_message = true; |
||||
op->payload->send_message.send_message = &send_stream; |
||||
} |
||||
|
||||
grpc_core::SliceBuffer send_stream; |
||||
}; |
||||
|
||||
struct MakeSendTrailingMetadata { |
||||
explicit MakeSendTrailingMetadata(const Metadata& trailing_metadata, |
||||
grpc_transport_stream_op_batch* op) { |
||||
EXPECT_TRUE(trailing_metadata.empty()); |
||||
|
||||
op->send_trailing_metadata = true; |
||||
op->payload->send_trailing_metadata.send_trailing_metadata = |
||||
&grpc_trailing_metadata; |
||||
} |
||||
|
||||
grpc_core::MemoryAllocator memory_allocator = |
||||
grpc_core::MemoryAllocator(grpc_core::ResourceQuota::Default() |
||||
->memory_quota() |
||||
->CreateMemoryAllocator("test")); |
||||
grpc_metadata_batch grpc_trailing_metadata; |
||||
}; |
||||
|
||||
struct MakeRecvInitialMetadata { |
||||
explicit MakeRecvInitialMetadata(grpc_transport_stream_op_batch* op, |
||||
Expectation* call_before = nullptr) |
||||
: ready(¬ification) { |
||||
op->recv_initial_metadata = true; |
||||
op->payload->recv_initial_metadata.recv_initial_metadata = |
||||
&grpc_initial_metadata; |
||||
op->payload->recv_initial_metadata.recv_initial_metadata_ready = |
||||
ready.GetGrpcClosure(); |
||||
if (call_before) { |
||||
EXPECT_CALL(ready, Callback).After(*call_before); |
||||
} else { |
||||
EXPECT_CALL(ready, Callback); |
||||
} |
||||
} |
||||
|
||||
~MakeRecvInitialMetadata() {} |
||||
|
||||
MockGrpcClosure ready; |
||||
grpc_core::MemoryAllocator memory_allocator = |
||||
grpc_core::MemoryAllocator(grpc_core::ResourceQuota::Default() |
||||
->memory_quota() |
||||
->CreateMemoryAllocator("test")); |
||||
grpc_metadata_batch grpc_initial_metadata; |
||||
grpc_core::Notification notification; |
||||
}; |
||||
|
||||
struct MakeRecvMessage { |
||||
explicit MakeRecvMessage(grpc_transport_stream_op_batch* op, |
||||
Expectation* call_before = nullptr) |
||||
: ready(¬ification) { |
||||
op->recv_message = true; |
||||
op->payload->recv_message.recv_message = &grpc_message; |
||||
op->payload->recv_message.recv_message_ready = ready.GetGrpcClosure(); |
||||
if (call_before) { |
||||
EXPECT_CALL(ready, Callback).After(*call_before); |
||||
} else { |
||||
EXPECT_CALL(ready, Callback); |
||||
} |
||||
} |
||||
|
||||
MockGrpcClosure ready; |
||||
grpc_core::Notification notification; |
||||
absl::optional<grpc_core::SliceBuffer> grpc_message; |
||||
}; |
||||
|
||||
struct MakeRecvTrailingMetadata { |
||||
explicit MakeRecvTrailingMetadata(grpc_transport_stream_op_batch* op, |
||||
Expectation* call_before = nullptr) |
||||
: ready(¬ification) { |
||||
op->recv_trailing_metadata = true; |
||||
op->payload->recv_trailing_metadata.recv_trailing_metadata = |
||||
&grpc_trailing_metadata; |
||||
op->payload->recv_trailing_metadata.recv_trailing_metadata_ready = |
||||
ready.GetGrpcClosure(); |
||||
if (call_before) { |
||||
EXPECT_CALL(ready, Callback).After(*call_before); |
||||
} else { |
||||
EXPECT_CALL(ready, Callback); |
||||
} |
||||
} |
||||
|
||||
~MakeRecvTrailingMetadata() {} |
||||
|
||||
MockGrpcClosure ready; |
||||
grpc_core::MemoryAllocator memory_allocator = |
||||
grpc_core::MemoryAllocator(grpc_core::ResourceQuota::Default() |
||||
->memory_quota() |
||||
->CreateMemoryAllocator("test")); |
||||
grpc_metadata_batch grpc_trailing_metadata; |
||||
grpc_core::Notification notification; |
||||
}; |
||||
|
||||
const Metadata kDefaultMetadata = { |
||||
{"", ""}, |
||||
{"", "value"}, |
||||
{"key", ""}, |
||||
{"key", "value"}, |
||||
}; |
||||
|
||||
constexpr char kDefaultMethodRef[] = "/some/path"; |
||||
constexpr char kDefaultMessage[] = "binder transport message"; |
||||
constexpr int kDefaultStatus = 0x1234; |
||||
|
||||
Metadata AppendMethodRef(const Metadata& md, const std::string& method_ref) { |
||||
Metadata result = md; |
||||
result.emplace_back(":path", method_ref); |
||||
return result; |
||||
} |
||||
|
||||
Metadata AppendStatus(const Metadata& md, int status) { |
||||
Metadata result = md; |
||||
result.emplace_back("grpc-status", std::to_string(status)); |
||||
return result; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(BinderTransportTest, CreateBinderTransport) { |
||||
EXPECT_NE(transport_, nullptr); |
||||
} |
||||
|
||||
TEST_F(BinderTransportTest, TransactionIdIncrement) { |
||||
grpc_binder_stream* gbs0 = InitNewBinderStream(); |
||||
EXPECT_EQ(gbs0->t, GetBinderTransport()); |
||||
EXPECT_EQ(gbs0->tx_code, kFirstCallId); |
||||
grpc_binder_stream* gbs1 = InitNewBinderStream(); |
||||
EXPECT_EQ(gbs1->t, GetBinderTransport()); |
||||
EXPECT_EQ(gbs1->tx_code, kFirstCallId + 1); |
||||
grpc_binder_stream* gbs2 = InitNewBinderStream(); |
||||
EXPECT_EQ(gbs2->t, GetBinderTransport()); |
||||
EXPECT_EQ(gbs2->tx_code, kFirstCallId + 2); |
||||
} |
||||
|
||||
TEST_F(BinderTransportTest, PerformSendInitialMetadata) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
grpc_binder_stream* gbs = InitNewBinderStream(); |
||||
grpc_transport_stream_op_batch op{}; |
||||
grpc_transport_stream_op_batch_payload payload; |
||||
op.payload = &payload; |
||||
const Metadata kInitialMetadata = kDefaultMetadata; |
||||
MakeSendInitialMetadata send_initial_metadata(kInitialMetadata, "", &op); |
||||
MockGrpcClosure mock_on_complete; |
||||
op.on_complete = mock_on_complete.GetGrpcClosure(); |
||||
|
||||
::testing::InSequence sequence; |
||||
EXPECT_CALL(GetWireWriter(), RpcCall(TransactionMatches( |
||||
kFlagPrefix, "", kInitialMetadata, ""))); |
||||
EXPECT_CALL(mock_on_complete, Callback); |
||||
|
||||
PerformStreamOp(gbs, &op); |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
} |
||||
|
||||
TEST_F(BinderTransportTest, PerformSendInitialMetadataMethodRef) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
grpc_binder_stream* gbs = InitNewBinderStream(); |
||||
grpc_transport_stream_op_batch op{}; |
||||
grpc_transport_stream_op_batch_payload payload; |
||||
op.payload = &payload; |
||||
const Metadata kInitialMetadata = kDefaultMetadata; |
||||
const std::string kMethodRef = kDefaultMethodRef; |
||||
MakeSendInitialMetadata send_initial_metadata(kInitialMetadata, kMethodRef, |
||||
&op); |
||||
MockGrpcClosure mock_on_complete; |
||||
op.on_complete = mock_on_complete.GetGrpcClosure(); |
||||
|
||||
::testing::InSequence sequence; |
||||
EXPECT_CALL(GetWireWriter(), |
||||
RpcCall(TransactionMatches(kFlagPrefix, kMethodRef.substr(1), |
||||
kInitialMetadata, ""))); |
||||
EXPECT_CALL(mock_on_complete, Callback); |
||||
|
||||
PerformStreamOp(gbs, &op); |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
} |
||||
|
||||
TEST_F(BinderTransportTest, PerformSendMessage) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
grpc_binder_stream* gbs = InitNewBinderStream(); |
||||
grpc_transport_stream_op_batch op{}; |
||||
grpc_transport_stream_op_batch_payload payload; |
||||
op.payload = &payload; |
||||
|
||||
const std::string kMessage = kDefaultMessage; |
||||
MakeSendMessage send_message(kMessage, &op); |
||||
MockGrpcClosure mock_on_complete; |
||||
op.on_complete = mock_on_complete.GetGrpcClosure(); |
||||
|
||||
::testing::InSequence sequence; |
||||
EXPECT_CALL( |
||||
GetWireWriter(), |
||||
RpcCall(TransactionMatches(kFlagMessageData, "", Metadata{}, kMessage))); |
||||
EXPECT_CALL(mock_on_complete, Callback); |
||||
|
||||
PerformStreamOp(gbs, &op); |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
} |
||||
|
||||
TEST_F(BinderTransportTest, PerformSendTrailingMetadata) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
grpc_binder_stream* gbs = InitNewBinderStream(); |
||||
grpc_transport_stream_op_batch op{}; |
||||
grpc_transport_stream_op_batch_payload payload; |
||||
op.payload = &payload; |
||||
// The wireformat guarantees that suffix metadata will always be empty.
|
||||
// TODO(waynetu): Check whether gRPC can internally add extra trailing
|
||||
// metadata.
|
||||
const Metadata kTrailingMetadata = {}; |
||||
MakeSendTrailingMetadata send_trailing_metadata(kTrailingMetadata, &op); |
||||
MockGrpcClosure mock_on_complete; |
||||
op.on_complete = mock_on_complete.GetGrpcClosure(); |
||||
|
||||
::testing::InSequence sequence; |
||||
EXPECT_CALL(GetWireWriter(), RpcCall(TransactionMatches( |
||||
kFlagSuffix, "", kTrailingMetadata, ""))); |
||||
EXPECT_CALL(mock_on_complete, Callback); |
||||
|
||||
PerformStreamOp(gbs, &op); |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
} |
||||
|
||||
TEST_F(BinderTransportTest, PerformSendAll) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
grpc_binder_stream* gbs = InitNewBinderStream(); |
||||
grpc_transport_stream_op_batch op{}; |
||||
grpc_transport_stream_op_batch_payload payload; |
||||
op.payload = &payload; |
||||
|
||||
const Metadata kInitialMetadata = kDefaultMetadata; |
||||
const std::string kMethodRef = kDefaultMethodRef; |
||||
MakeSendInitialMetadata send_initial_metadata(kInitialMetadata, kMethodRef, |
||||
&op); |
||||
|
||||
const std::string kMessage = kDefaultMessage; |
||||
MakeSendMessage send_message(kMessage, &op); |
||||
|
||||
// The wireformat guarantees that suffix metadata will always be empty.
|
||||
// TODO(waynetu): Check whether gRPC can internally add extra trailing
|
||||
// metadata.
|
||||
const Metadata kTrailingMetadata = {}; |
||||
MakeSendTrailingMetadata send_trailing_metadata(kTrailingMetadata, &op); |
||||
|
||||
MockGrpcClosure mock_on_complete; |
||||
op.on_complete = mock_on_complete.GetGrpcClosure(); |
||||
|
||||
::testing::InSequence sequence; |
||||
EXPECT_CALL(GetWireWriter(), |
||||
RpcCall(TransactionMatches( |
||||
kFlagPrefix | kFlagMessageData | kFlagSuffix, |
||||
kMethodRef.substr(1), kInitialMetadata, kMessage))); |
||||
EXPECT_CALL(mock_on_complete, Callback); |
||||
|
||||
PerformStreamOp(gbs, &op); |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
} |
||||
|
||||
TEST_F(BinderTransportTest, PerformRecvInitialMetadata) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
grpc_binder_stream* gbs = InitNewBinderStream(); |
||||
grpc_transport_stream_op_batch op{}; |
||||
grpc_transport_stream_op_batch_payload payload; |
||||
op.payload = &payload; |
||||
|
||||
MakeRecvInitialMetadata recv_initial_metadata(&op); |
||||
|
||||
const Metadata kInitialMetadata = kDefaultMetadata; |
||||
auto* gbt = reinterpret_cast<grpc_binder_transport*>(transport_); |
||||
gbt->transport_stream_receiver->NotifyRecvInitialMetadata(gbs->tx_code, |
||||
kInitialMetadata); |
||||
PerformStreamOp(gbs, &op); |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
recv_initial_metadata.notification.WaitForNotification(); |
||||
|
||||
VerifyMetadataEqual(kInitialMetadata, |
||||
recv_initial_metadata.grpc_initial_metadata); |
||||
} |
||||
|
||||
TEST_F(BinderTransportTest, PerformRecvInitialMetadataWithMethodRef) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
grpc_binder_stream* gbs = InitNewBinderStream(); |
||||
grpc_transport_stream_op_batch op{}; |
||||
grpc_transport_stream_op_batch_payload payload; |
||||
op.payload = &payload; |
||||
|
||||
MakeRecvInitialMetadata recv_initial_metadata(&op); |
||||
|
||||
auto* gbt = reinterpret_cast<grpc_binder_transport*>(transport_); |
||||
const Metadata kInitialMetadataWithMethodRef = |
||||
AppendMethodRef(kDefaultMetadata, kDefaultMethodRef); |
||||
gbt->transport_stream_receiver->NotifyRecvInitialMetadata( |
||||
gbs->tx_code, kInitialMetadataWithMethodRef); |
||||
PerformStreamOp(gbs, &op); |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
recv_initial_metadata.notification.WaitForNotification(); |
||||
|
||||
VerifyMetadataEqual(kInitialMetadataWithMethodRef, |
||||
recv_initial_metadata.grpc_initial_metadata); |
||||
} |
||||
|
||||
TEST_F(BinderTransportTest, PerformRecvMessage) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
grpc_binder_stream* gbs = InitNewBinderStream(); |
||||
grpc_transport_stream_op_batch op{}; |
||||
grpc_transport_stream_op_batch_payload payload; |
||||
op.payload = &payload; |
||||
|
||||
MakeRecvMessage recv_message(&op); |
||||
|
||||
auto* gbt = reinterpret_cast<grpc_binder_transport*>(transport_); |
||||
const std::string kMessage = kDefaultMessage; |
||||
gbt->transport_stream_receiver->NotifyRecvMessage(gbs->tx_code, kMessage); |
||||
|
||||
PerformStreamOp(gbs, &op); |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
recv_message.notification.WaitForNotification(); |
||||
|
||||
EXPECT_EQ(kMessage, recv_message.grpc_message->JoinIntoString()); |
||||
} |
||||
|
||||
TEST_F(BinderTransportTest, PerformRecvTrailingMetadata) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
grpc_binder_stream* gbs = InitNewBinderStream(); |
||||
grpc_transport_stream_op_batch op{}; |
||||
grpc_transport_stream_op_batch_payload payload; |
||||
op.payload = &payload; |
||||
|
||||
MakeRecvTrailingMetadata recv_trailing_metadata(&op); |
||||
|
||||
const Metadata kTrailingMetadata = kDefaultMetadata; |
||||
auto* gbt = reinterpret_cast<grpc_binder_transport*>(transport_); |
||||
constexpr int kStatus = kDefaultStatus; |
||||
gbt->transport_stream_receiver->NotifyRecvTrailingMetadata( |
||||
gbs->tx_code, kTrailingMetadata, kStatus); |
||||
PerformStreamOp(gbs, &op); |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
recv_trailing_metadata.notification.WaitForNotification(); |
||||
|
||||
VerifyMetadataEqual(AppendStatus(kTrailingMetadata, kStatus), |
||||
recv_trailing_metadata.grpc_trailing_metadata); |
||||
} |
||||
|
||||
TEST_F(BinderTransportTest, PerformRecvAll) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
grpc_binder_stream* gbs = InitNewBinderStream(); |
||||
grpc_transport_stream_op_batch op{}; |
||||
grpc_transport_stream_op_batch_payload payload; |
||||
op.payload = &payload; |
||||
|
||||
MakeRecvInitialMetadata recv_initial_metadata(&op); |
||||
MakeRecvMessage recv_message(&op); |
||||
MakeRecvTrailingMetadata recv_trailing_metadata(&op); |
||||
|
||||
auto* gbt = reinterpret_cast<grpc_binder_transport*>(transport_); |
||||
const Metadata kInitialMetadataWithMethodRef = |
||||
AppendMethodRef(kDefaultMetadata, kDefaultMethodRef); |
||||
gbt->transport_stream_receiver->NotifyRecvInitialMetadata( |
||||
gbs->tx_code, kInitialMetadataWithMethodRef); |
||||
|
||||
const std::string kMessage = kDefaultMessage; |
||||
gbt->transport_stream_receiver->NotifyRecvMessage(gbs->tx_code, kMessage); |
||||
|
||||
Metadata trailing_metadata = kDefaultMetadata; |
||||
constexpr int kStatus = kDefaultStatus; |
||||
gbt->transport_stream_receiver->NotifyRecvTrailingMetadata( |
||||
gbs->tx_code, trailing_metadata, kStatus); |
||||
PerformStreamOp(gbs, &op); |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
recv_trailing_metadata.notification.WaitForNotification(); |
||||
|
||||
VerifyMetadataEqual(kInitialMetadataWithMethodRef, |
||||
recv_initial_metadata.grpc_initial_metadata); |
||||
trailing_metadata.emplace_back("grpc-status", std::to_string(kStatus)); |
||||
VerifyMetadataEqual(trailing_metadata, |
||||
recv_trailing_metadata.grpc_trailing_metadata); |
||||
EXPECT_EQ(kMessage, recv_message.grpc_message->JoinIntoString()); |
||||
} |
||||
|
||||
TEST_F(BinderTransportTest, PerformAllOps) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
grpc_binder_stream* gbs = InitNewBinderStream(); |
||||
grpc_transport_stream_op_batch op{}; |
||||
grpc_transport_stream_op_batch_payload payload; |
||||
op.payload = &payload; |
||||
|
||||
const Metadata kSendInitialMetadata = kDefaultMetadata; |
||||
const std::string kMethodRef = kDefaultMethodRef; |
||||
MakeSendInitialMetadata send_initial_metadata(kSendInitialMetadata, |
||||
kMethodRef, &op); |
||||
|
||||
const std::string kSendMessage = kDefaultMessage; |
||||
MakeSendMessage send_message(kSendMessage, &op); |
||||
|
||||
// The wireformat guarantees that suffix metadata will always be empty.
|
||||
// TODO(waynetu): Check whether gRPC can internally add extra trailing
|
||||
// metadata.
|
||||
const Metadata kSendTrailingMetadata = {}; |
||||
MakeSendTrailingMetadata send_trailing_metadata(kSendTrailingMetadata, &op); |
||||
|
||||
MockGrpcClosure mock_on_complete; |
||||
op.on_complete = mock_on_complete.GetGrpcClosure(); |
||||
|
||||
// TODO(waynetu): Currently, we simply drop the prefix '/' from the :path
|
||||
// argument to obtain the method name. Update the test if this turns out to be
|
||||
// incorrect.
|
||||
EXPECT_CALL(GetWireWriter(), |
||||
RpcCall(TransactionMatches( |
||||
kFlagPrefix | kFlagMessageData | kFlagSuffix, |
||||
kMethodRef.substr(1), kSendInitialMetadata, kSendMessage))); |
||||
Expectation on_complete = EXPECT_CALL(mock_on_complete, Callback); |
||||
|
||||
// Recv callbacks can happen after the on_complete callback.
|
||||
MakeRecvInitialMetadata recv_initial_metadata( |
||||
&op, /* call_before = */ &on_complete); |
||||
MakeRecvMessage recv_message(&op, /* call_before = */ &on_complete); |
||||
MakeRecvTrailingMetadata recv_trailing_metadata( |
||||
&op, /* call_before = */ &on_complete); |
||||
|
||||
PerformStreamOp(gbs, &op); |
||||
|
||||
// Flush the execution context to force on_complete to run before recv
|
||||
// callbacks get scheduled.
|
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
|
||||
auto* gbt = reinterpret_cast<grpc_binder_transport*>(transport_); |
||||
const Metadata kRecvInitialMetadata = |
||||
AppendMethodRef(kDefaultMetadata, kDefaultMethodRef); |
||||
gbt->transport_stream_receiver->NotifyRecvInitialMetadata( |
||||
gbs->tx_code, kRecvInitialMetadata); |
||||
const std::string kRecvMessage = kDefaultMessage; |
||||
gbt->transport_stream_receiver->NotifyRecvMessage(gbs->tx_code, kRecvMessage); |
||||
const Metadata kRecvTrailingMetadata = kDefaultMetadata; |
||||
constexpr int kStatus = 0x1234; |
||||
gbt->transport_stream_receiver->NotifyRecvTrailingMetadata( |
||||
gbs->tx_code, kRecvTrailingMetadata, kStatus); |
||||
|
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
recv_initial_metadata.notification.WaitForNotification(); |
||||
recv_message.notification.WaitForNotification(); |
||||
recv_trailing_metadata.notification.WaitForNotification(); |
||||
|
||||
VerifyMetadataEqual(kRecvInitialMetadata, |
||||
recv_initial_metadata.grpc_initial_metadata); |
||||
VerifyMetadataEqual(AppendStatus(kRecvTrailingMetadata, kStatus), |
||||
recv_trailing_metadata.grpc_trailing_metadata); |
||||
|
||||
EXPECT_EQ(kRecvMessage, recv_message.grpc_message->JoinIntoString()); |
||||
} |
||||
|
||||
TEST_F(BinderTransportTest, WireWriterRpcCallErrorPropagates) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
grpc_binder_stream* gbs = InitNewBinderStream(); |
||||
|
||||
MockGrpcClosure mock_on_complete1; |
||||
MockGrpcClosure mock_on_complete2; |
||||
|
||||
EXPECT_CALL(GetWireWriter(), RpcCall) |
||||
.WillOnce(Return(absl::OkStatus())) |
||||
.WillOnce(Return(absl::InternalError("WireWriter::RpcCall failed"))); |
||||
EXPECT_CALL(mock_on_complete1, Callback(absl::OkStatus())); |
||||
EXPECT_CALL(mock_on_complete2, |
||||
Callback(GrpcErrorMessageContains("WireWriter::RpcCall failed"))); |
||||
|
||||
const Metadata kInitialMetadata = {}; |
||||
grpc_transport_stream_op_batch op1{}; |
||||
grpc_transport_stream_op_batch_payload payload1; |
||||
op1.payload = &payload1; |
||||
MakeSendInitialMetadata send_initial_metadata1(kInitialMetadata, "", &op1); |
||||
op1.on_complete = mock_on_complete1.GetGrpcClosure(); |
||||
|
||||
grpc_transport_stream_op_batch op2{}; |
||||
grpc_transport_stream_op_batch_payload payload2; |
||||
op2.payload = &payload2; |
||||
MakeSendInitialMetadata send_initial_metadata2(kInitialMetadata, "", &op2); |
||||
op2.on_complete = mock_on_complete2.GetGrpcClosure(); |
||||
|
||||
PerformStreamOp(gbs, &op1); |
||||
PerformStreamOp(gbs, &op2); |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
} |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
grpc::testing::TestEnvironment env(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
@ -1,119 +0,0 @@ |
||||
# Copyright 2021 gRPC authors. |
||||
# |
||||
# 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. |
||||
|
||||
load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_cc_test", "grpc_package") |
||||
|
||||
licenses(["notice"]) |
||||
|
||||
grpc_package( |
||||
name = "test/core/transport/binder/end2end", |
||||
visibility = "tests", |
||||
) |
||||
|
||||
grpc_cc_library( |
||||
name = "fake_binder", |
||||
testonly = 1, |
||||
srcs = ["fake_binder.cc"], |
||||
hdrs = ["fake_binder.h"], |
||||
external_deps = [ |
||||
"absl/log:log", |
||||
"absl/memory", |
||||
"absl/random", |
||||
"absl/strings", |
||||
"absl/strings:str_format", |
||||
"absl/time", |
||||
"absl/types:variant", |
||||
], |
||||
deps = [ |
||||
"//:gpr", |
||||
"//:grpc++_binder", |
||||
], |
||||
) |
||||
|
||||
grpc_cc_test( |
||||
name = "fake_binder_test", |
||||
srcs = ["fake_binder_test.cc"], |
||||
external_deps = [ |
||||
"absl/strings", |
||||
"absl/time", |
||||
"gtest", |
||||
], |
||||
language = "C++", |
||||
tags = ["no_test_ios"], |
||||
uses_event_engine = False, |
||||
uses_polling = False, |
||||
deps = [ |
||||
":fake_binder", |
||||
"//test/core/test_util:grpc_test_util", |
||||
], |
||||
) |
||||
|
||||
grpc_cc_library( |
||||
name = "end2end_binder_channel", |
||||
testonly = 1, |
||||
srcs = ["testing_channel_create.cc"], |
||||
hdrs = ["testing_channel_create.h"], |
||||
external_deps = ["absl/log:check"], |
||||
deps = [ |
||||
":fake_binder", |
||||
"//:grpc++_base", |
||||
"//:grpc++_binder", |
||||
"//:grpc_base", |
||||
"//src/core:channel_args", |
||||
], |
||||
) |
||||
|
||||
grpc_cc_test( |
||||
name = "end2end_binder_transport_test", |
||||
srcs = ["end2end_binder_transport_test.cc"], |
||||
external_deps = [ |
||||
"absl/memory", |
||||
"absl/time", |
||||
"gtest", |
||||
], |
||||
flaky = True, |
||||
language = "C++", |
||||
tags = [ |
||||
# Flaky on windows |
||||
"no_windows", |
||||
"no_mac", |
||||
"no_test_ios", |
||||
|
||||
# Known race between stream creation and cancellation |
||||
"notsan", |
||||
], |
||||
deps = [ |
||||
":end2end_binder_channel", |
||||
":fake_binder", |
||||
"//:grpc++_binder", |
||||
"//test/core/test_util:grpc_test_util", |
||||
"//test/cpp/end2end:test_service_impl", |
||||
], |
||||
) |
||||
|
||||
grpc_cc_test( |
||||
name = "binder_server_test", |
||||
srcs = ["binder_server_test.cc"], |
||||
external_deps = [ |
||||
"gtest", |
||||
], |
||||
tags = ["no_test_ios"], |
||||
deps = [ |
||||
"//:grpc++", |
||||
"//:grpc++_binder", |
||||
"//test/core/test_util:grpc_test_util", |
||||
"//test/core/transport/binder/end2end:fake_binder", |
||||
"//test/cpp/end2end:test_service_impl", |
||||
], |
||||
) |
@ -1,243 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "src/core/ext/transport/binder/server/binder_server.h" |
||||
|
||||
#include <grpcpp/grpcpp.h> |
||||
#include <grpcpp/security/binder_credentials.h> |
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <memory> |
||||
#include <thread> |
||||
#include <vector> |
||||
|
||||
#include "absl/memory/memory.h" |
||||
#include "src/core/ext/transport/binder/client/channel_create_impl.h" |
||||
#include "test/core/test_util/test_config.h" |
||||
#include "test/core/transport/binder/end2end/fake_binder.h" |
||||
#include "test/cpp/end2end/test_service_impl.h" |
||||
|
||||
namespace grpc { |
||||
namespace testing { |
||||
|
||||
namespace { |
||||
|
||||
class BinderServerCredentialsImpl final : public ServerCredentials { |
||||
public: |
||||
BinderServerCredentialsImpl() : ServerCredentials(nullptr) {} |
||||
|
||||
int AddPortToServer(const std::string& addr, grpc_server* server) override { |
||||
return grpc_core::AddBinderPort( |
||||
addr, server, |
||||
[](grpc_binder::TransactionReceiver::OnTransactCb transact_cb) { |
||||
return std::make_unique< |
||||
grpc_binder::end2end_testing::FakeTransactionReceiver>( |
||||
nullptr, std::move(transact_cb)); |
||||
}, |
||||
std::make_shared< |
||||
grpc::experimental::binder::UntrustedSecurityPolicy>()); |
||||
} |
||||
}; |
||||
|
||||
} // namespace
|
||||
|
||||
std::shared_ptr<ServerCredentials> BinderServerCredentials() { |
||||
return std::shared_ptr<ServerCredentials>(new BinderServerCredentialsImpl()); |
||||
} |
||||
|
||||
std::shared_ptr<grpc::Channel> CreateBinderChannel( |
||||
std::unique_ptr<grpc_binder::Binder> endpoint_binder) { |
||||
return grpc::CreateChannelInternal( |
||||
"", |
||||
grpc::internal::CreateDirectBinderChannelImplForTesting( |
||||
std::move(endpoint_binder), nullptr, |
||||
std::make_shared< |
||||
grpc::experimental::binder::UntrustedSecurityPolicy>()), |
||||
std::vector<std::unique_ptr< |
||||
grpc::experimental::ClientInterceptorFactoryInterface>>()); |
||||
} |
||||
|
||||
} // namespace testing
|
||||
} // namespace grpc
|
||||
|
||||
namespace { |
||||
|
||||
class BinderServerTest : public ::testing::Test { |
||||
public: |
||||
BinderServerTest() { |
||||
grpc_binder::end2end_testing::g_transaction_processor = |
||||
new grpc_binder::end2end_testing::TransactionProcessor(); |
||||
} |
||||
~BinderServerTest() override { |
||||
delete grpc_binder::end2end_testing::g_transaction_processor; |
||||
} |
||||
static void SetUpTestSuite() { grpc_init(); } |
||||
static void TearDownTestSuite() { grpc_shutdown(); } |
||||
}; |
||||
|
||||
#ifndef GPR_SUPPORT_BINDER_TRANSPORT |
||||
TEST(BinderServerCredentialsTest, |
||||
FailedInEnvironmentsNotSupportingBinderTransport) { |
||||
grpc::ServerBuilder server_builder; |
||||
grpc::testing::TestServiceImpl service; |
||||
server_builder.RegisterService(&service); |
||||
server_builder.AddListeningPort( |
||||
"binder:fail", |
||||
grpc::experimental::BinderServerCredentials( |
||||
std::make_shared< |
||||
grpc::experimental::binder::UntrustedSecurityPolicy>())); |
||||
EXPECT_EQ(server_builder.BuildAndStart(), nullptr); |
||||
} |
||||
#endif // !GPR_SUPPORT_BINDER_TRANSPORT
|
||||
|
||||
TEST_F(BinderServerTest, BuildAndStart) { |
||||
grpc::ServerBuilder server_builder; |
||||
grpc::testing::TestServiceImpl service; |
||||
server_builder.RegisterService(&service); |
||||
server_builder.AddListeningPort("binder:example.service", |
||||
grpc::testing::BinderServerCredentials()); |
||||
std::unique_ptr<grpc::Server> server = server_builder.BuildAndStart(); |
||||
EXPECT_NE(grpc::experimental::binder::GetEndpointBinder("example.service"), |
||||
nullptr); |
||||
server->Shutdown(); |
||||
EXPECT_EQ(grpc::experimental::binder::GetEndpointBinder("example.service"), |
||||
nullptr); |
||||
} |
||||
|
||||
TEST_F(BinderServerTest, BuildAndStartFailed) { |
||||
grpc::ServerBuilder server_builder; |
||||
grpc::testing::TestServiceImpl service; |
||||
server_builder.RegisterService(&service); |
||||
// Error: binder address should begin with binder:
|
||||
server_builder.AddListeningPort("localhost:12345", |
||||
grpc::testing::BinderServerCredentials()); |
||||
std::unique_ptr<grpc::Server> server = server_builder.BuildAndStart(); |
||||
EXPECT_EQ(server, nullptr); |
||||
} |
||||
|
||||
TEST_F(BinderServerTest, CreateChannelWithEndpointBinder) { |
||||
grpc::ServerBuilder server_builder; |
||||
grpc::testing::TestServiceImpl service; |
||||
server_builder.RegisterService(&service); |
||||
server_builder.AddListeningPort("binder:example.service", |
||||
grpc::testing::BinderServerCredentials()); |
||||
std::unique_ptr<grpc::Server> server = server_builder.BuildAndStart(); |
||||
void* raw_endpoint_binder = |
||||
grpc::experimental::binder::GetEndpointBinder("example.service"); |
||||
std::unique_ptr<grpc_binder::Binder> endpoint_binder = |
||||
std::make_unique<grpc_binder::end2end_testing::FakeBinder>( |
||||
static_cast<grpc_binder::end2end_testing::FakeEndpoint*>( |
||||
raw_endpoint_binder)); |
||||
std::shared_ptr<grpc::Channel> channel = |
||||
grpc::testing::CreateBinderChannel(std::move(endpoint_binder)); |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = |
||||
grpc::testing::EchoTestService::NewStub(channel); |
||||
grpc::testing::EchoRequest request; |
||||
grpc::testing::EchoResponse response; |
||||
grpc::ClientContext context; |
||||
request.set_message("BinderServerBuilder"); |
||||
grpc::Status status = stub->Echo(&context, request, &response); |
||||
EXPECT_TRUE(status.ok()); |
||||
EXPECT_EQ(response.message(), "BinderServerBuilder"); |
||||
server->Shutdown(); |
||||
} |
||||
|
||||
TEST_F(BinderServerTest, CreateChannelWithEndpointBinderMultipleConnections) { |
||||
grpc::ServerBuilder server_builder; |
||||
grpc::testing::TestServiceImpl service; |
||||
server_builder.RegisterService(&service); |
||||
server_builder.AddListeningPort("binder:example.service.multiple.connections", |
||||
grpc::testing::BinderServerCredentials()); |
||||
std::unique_ptr<grpc::Server> server = server_builder.BuildAndStart(); |
||||
void* raw_endpoint_binder = grpc::experimental::binder::GetEndpointBinder( |
||||
"example.service.multiple.connections"); |
||||
constexpr size_t kNumThreads = 10; |
||||
|
||||
auto thread_fn = [&](size_t id) { |
||||
std::unique_ptr<grpc_binder::Binder> endpoint_binder = |
||||
std::make_unique<grpc_binder::end2end_testing::FakeBinder>( |
||||
static_cast<grpc_binder::end2end_testing::FakeEndpoint*>( |
||||
raw_endpoint_binder)); |
||||
std::shared_ptr<grpc::Channel> channel = |
||||
grpc::testing::CreateBinderChannel(std::move(endpoint_binder)); |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = |
||||
grpc::testing::EchoTestService::NewStub(channel); |
||||
grpc::testing::EchoRequest request; |
||||
grpc::testing::EchoResponse response; |
||||
grpc::ClientContext context; |
||||
request.set_message(absl::StrFormat("BinderServerBuilder-%d", id)); |
||||
grpc::Status status = stub->Echo(&context, request, &response); |
||||
EXPECT_TRUE(status.ok()); |
||||
EXPECT_EQ(response.message(), |
||||
absl::StrFormat("BinderServerBuilder-%d", id)); |
||||
}; |
||||
|
||||
std::vector<std::thread> threads(kNumThreads); |
||||
for (size_t i = 0; i < kNumThreads; ++i) { |
||||
threads[i] = std::thread(thread_fn, i); |
||||
} |
||||
for (auto& thr : threads) { |
||||
thr.join(); |
||||
} |
||||
server->Shutdown(); |
||||
} |
||||
|
||||
TEST_F(BinderServerTest, CreateChannelWithEndpointBinderParallelRequests) { |
||||
grpc::ServerBuilder server_builder; |
||||
grpc::testing::TestServiceImpl service; |
||||
server_builder.RegisterService(&service); |
||||
server_builder.AddListeningPort("binder:example.service", |
||||
grpc::testing::BinderServerCredentials()); |
||||
std::unique_ptr<grpc::Server> server = server_builder.BuildAndStart(); |
||||
void* raw_endpoint_binder = |
||||
grpc::experimental::binder::GetEndpointBinder("example.service"); |
||||
std::unique_ptr<grpc_binder::Binder> endpoint_binder = |
||||
std::make_unique<grpc_binder::end2end_testing::FakeBinder>( |
||||
static_cast<grpc_binder::end2end_testing::FakeEndpoint*>( |
||||
raw_endpoint_binder)); |
||||
std::shared_ptr<grpc::Channel> channel = |
||||
grpc::testing::CreateBinderChannel(std::move(endpoint_binder)); |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = |
||||
grpc::testing::EchoTestService::NewStub(channel); |
||||
|
||||
constexpr size_t kNumRequests = 10; |
||||
|
||||
auto thread_fn = [&](size_t id) { |
||||
grpc::testing::EchoRequest request; |
||||
std::string msg = absl::StrFormat("BinderServerBuilder-%d", id); |
||||
request.set_message(msg); |
||||
grpc::testing::EchoResponse response; |
||||
grpc::ClientContext context; |
||||
grpc::Status status = stub->Echo(&context, request, &response); |
||||
EXPECT_TRUE(status.ok()); |
||||
EXPECT_EQ(response.message(), msg); |
||||
}; |
||||
std::vector<std::thread> threads(kNumRequests); |
||||
for (size_t i = 0; i < kNumRequests; ++i) { |
||||
threads[i] = std::thread(thread_fn, i); |
||||
} |
||||
for (auto& thr : threads) { |
||||
thr.join(); |
||||
} |
||||
server->Shutdown(); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
grpc::testing::TestEnvironment env(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
@ -1,566 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 <gmock/gmock.h> |
||||
#include <grpcpp/grpcpp.h> |
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <string> |
||||
#include <thread> |
||||
#include <utility> |
||||
|
||||
#include "absl/memory/memory.h" |
||||
#include "absl/time/time.h" |
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_reader_impl.h" |
||||
#include "test/core/test_util/test_config.h" |
||||
#include "test/core/transport/binder/end2end/fake_binder.h" |
||||
#include "test/core/transport/binder/end2end/testing_channel_create.h" |
||||
#include "test/cpp/end2end/test_service_impl.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
namespace { |
||||
|
||||
class End2EndBinderTransportTest |
||||
: public ::testing::TestWithParam<absl::Duration> { |
||||
public: |
||||
End2EndBinderTransportTest() { |
||||
end2end_testing::g_transaction_processor = |
||||
new end2end_testing::TransactionProcessor(GetParam()); |
||||
service_ = std::make_unique<grpc::testing::TestServiceImpl>(); |
||||
grpc::ServerBuilder builder; |
||||
builder.RegisterService(service_.get()); |
||||
server_ = builder.BuildAndStart(); |
||||
} |
||||
|
||||
~End2EndBinderTransportTest() override { |
||||
server_->Shutdown(); |
||||
service_.reset(); |
||||
exec_ctx.Flush(); |
||||
delete end2end_testing::g_transaction_processor; |
||||
} |
||||
|
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> NewStub() { |
||||
grpc::ChannelArguments args; |
||||
std::shared_ptr<grpc::Channel> channel = BinderChannel(server_.get(), args); |
||||
return grpc::testing::EchoTestService::NewStub(channel); |
||||
} |
||||
|
||||
static void SetUpTestSuite() { grpc_init(); } |
||||
static void TearDownTestSuite() { grpc_shutdown(); } |
||||
|
||||
std::shared_ptr<grpc::Channel> BinderChannel( |
||||
grpc::Server* server, const grpc::ChannelArguments& args) { |
||||
return end2end_testing::BinderChannelForTesting(server, args); |
||||
} |
||||
|
||||
protected: |
||||
std::unique_ptr<grpc::testing::TestServiceImpl> service_; |
||||
std::unique_ptr<grpc::Server> server_; |
||||
|
||||
private: |
||||
grpc_core::ExecCtx exec_ctx; |
||||
}; |
||||
|
||||
} // namespace
|
||||
|
||||
TEST_P(End2EndBinderTransportTest, SetupTransport) { |
||||
grpc_core::Transport *client_transport, *server_transport; |
||||
std::tie(client_transport, server_transport) = |
||||
end2end_testing::CreateClientServerBindersPairForTesting(); |
||||
EXPECT_NE(client_transport, nullptr); |
||||
EXPECT_NE(server_transport, nullptr); |
||||
|
||||
client_transport->Orphan(); |
||||
server_transport->Orphan(); |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, UnaryCall) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
grpc::ClientContext context; |
||||
grpc::testing::EchoRequest request; |
||||
grpc::testing::EchoResponse response; |
||||
request.set_message("UnaryCall"); |
||||
grpc::Status status = stub->Echo(&context, request, &response); |
||||
EXPECT_TRUE(status.ok()); |
||||
EXPECT_EQ(response.message(), "UnaryCall"); |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, UnaryCallWithNonOkStatus) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
grpc::ClientContext context; |
||||
grpc::testing::EchoRequest request; |
||||
grpc::testing::EchoResponse response; |
||||
request.set_message("UnaryCallWithNonOkStatus"); |
||||
request.mutable_param()->mutable_expected_error()->set_code( |
||||
grpc::StatusCode::INTERNAL); |
||||
request.mutable_param()->mutable_expected_error()->set_error_message( |
||||
"expected to fail"); |
||||
// Server will not response the client with message data, however, since all
|
||||
// callbacks after the trailing metadata are cancelled, we shall not be
|
||||
// blocked here.
|
||||
grpc::Status status = stub->Echo(&context, request, &response); |
||||
EXPECT_FALSE(status.ok()); |
||||
EXPECT_EQ(status.error_code(), grpc::StatusCode::INTERNAL); |
||||
EXPECT_THAT(status.error_message(), ::testing::HasSubstr("expected to fail")); |
||||
} |
||||
|
||||
// Disabled because the test is ~0.01% flaky
|
||||
TEST_P(End2EndBinderTransportTest, DISABLED_UnaryCallServerTimeout) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
grpc::ClientContext context; |
||||
context.set_deadline(absl::ToChronoTime( |
||||
absl::Now() + (absl::Seconds(1) * grpc_test_slowdown_factor()))); |
||||
grpc::testing::EchoRequest request; |
||||
grpc::testing::EchoResponse response; |
||||
request.set_message("UnaryCallServerTimeout"); |
||||
// Server will sleep for 2 seconds before responding us.
|
||||
request.mutable_param()->set_server_sleep_us(2000000); |
||||
// Disable cancellation check because the request will time out.
|
||||
request.mutable_param()->set_skip_cancelled_check(true); |
||||
grpc::Status status = stub->Echo(&context, request, &response); |
||||
EXPECT_FALSE(status.ok()); |
||||
EXPECT_EQ(status.error_code(), grpc::StatusCode::DEADLINE_EXCEEDED); |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, UnaryCallClientTimeout) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
|
||||
// Set transaction delay to a large number. This happens after the channel
|
||||
// creation so that we don't need to wait that long for client and server to
|
||||
// be connected.
|
||||
end2end_testing::g_transaction_processor->SetDelay(absl::Seconds(5)); |
||||
|
||||
grpc::ClientContext context; |
||||
context.set_deadline(absl::ToChronoTime(absl::Now() + absl::Seconds(1))); |
||||
grpc::testing::EchoRequest request; |
||||
grpc::testing::EchoResponse response; |
||||
request.set_message("UnaryCallClientTimeout"); |
||||
grpc::Status status = stub->Echo(&context, request, &response); |
||||
EXPECT_FALSE(status.ok()); |
||||
EXPECT_EQ(status.error_code(), grpc::StatusCode::DEADLINE_EXCEEDED); |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, UnaryCallUnimplemented) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
|
||||
grpc::ClientContext context; |
||||
grpc::testing::EchoRequest request; |
||||
grpc::testing::EchoResponse response; |
||||
request.set_message("UnaryCallUnimplemented"); |
||||
grpc::Status status = stub->Unimplemented(&context, request, &response); |
||||
EXPECT_FALSE(status.ok()); |
||||
EXPECT_EQ(status.error_code(), grpc::StatusCode::UNIMPLEMENTED); |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, UnaryCallClientCancel) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
|
||||
grpc::ClientContext context; |
||||
grpc::testing::EchoRequest request; |
||||
grpc::testing::EchoResponse response; |
||||
request.set_message("UnaryCallClientCancel"); |
||||
context.TryCancel(); |
||||
grpc::Status status = stub->Unimplemented(&context, request, &response); |
||||
EXPECT_FALSE(status.ok()); |
||||
EXPECT_EQ(status.error_code(), grpc::StatusCode::CANCELLED); |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, UnaryCallEchoMetadataInitially) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
|
||||
grpc::ClientContext context; |
||||
grpc::testing::EchoRequest request; |
||||
grpc::testing::EchoResponse response; |
||||
request.set_message("UnaryCallEchoMetadataInitially"); |
||||
request.mutable_param()->set_echo_metadata_initially(true); |
||||
context.AddMetadata("key1", "value1"); |
||||
context.AddMetadata("key2", "value2"); |
||||
grpc::Status status = stub->Echo(&context, request, &response); |
||||
const auto& initial_metadata = context.GetServerInitialMetadata(); |
||||
EXPECT_EQ(initial_metadata.find("key1")->second, "value1"); |
||||
EXPECT_EQ(initial_metadata.find("key2")->second, "value2"); |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, UnaryCallEchoMetadata) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
|
||||
grpc::ClientContext context; |
||||
grpc::testing::EchoRequest request; |
||||
grpc::testing::EchoResponse response; |
||||
request.set_message("UnaryCallEchoMetadata"); |
||||
request.mutable_param()->set_echo_metadata(true); |
||||
context.AddMetadata("key1", "value1"); |
||||
context.AddMetadata("key2", "value2"); |
||||
grpc::Status status = stub->Echo(&context, request, &response); |
||||
const auto& initial_metadata = context.GetServerTrailingMetadata(); |
||||
EXPECT_EQ(initial_metadata.find("key1")->second, "value1"); |
||||
EXPECT_EQ(initial_metadata.find("key2")->second, "value2"); |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, UnaryCallResponseMessageLength) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
|
||||
for (size_t response_length : {1, 2, 5, 10, 100, 1000000}) { |
||||
grpc::ClientContext context; |
||||
grpc::testing::EchoRequest request; |
||||
grpc::testing::EchoResponse response; |
||||
request.set_message("UnaryCallResponseMessageLength"); |
||||
request.mutable_param()->set_response_message_length(response_length); |
||||
grpc::Status status = stub->Echo(&context, request, &response); |
||||
EXPECT_EQ(response.message().length(), response_length); |
||||
} |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, UnaryCallTryCancel) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
|
||||
grpc::ClientContext context; |
||||
context.AddMetadata(grpc::testing::kServerTryCancelRequest, |
||||
std::to_string(grpc::testing::CANCEL_BEFORE_PROCESSING)); |
||||
grpc::testing::EchoRequest request; |
||||
grpc::testing::EchoResponse response; |
||||
request.set_message("UnaryCallTryCancel"); |
||||
grpc::Status status = stub->Echo(&context, request, &response); |
||||
EXPECT_FALSE(status.ok()); |
||||
EXPECT_EQ(status.error_code(), grpc::StatusCode::CANCELLED); |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, ServerStreamingCall) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
constexpr size_t kServerResponseStreamsToSend = 100; |
||||
grpc::ClientContext context; |
||||
context.AddMetadata(grpc::testing::kServerResponseStreamsToSend, |
||||
std::to_string(kServerResponseStreamsToSend)); |
||||
grpc::testing::EchoRequest request; |
||||
request.set_message("ServerStreamingCall"); |
||||
std::unique_ptr<grpc::ClientReader<grpc::testing::EchoResponse>> reader = |
||||
stub->ResponseStream(&context, request); |
||||
grpc::testing::EchoResponse response; |
||||
size_t cnt = 0; |
||||
while (reader->Read(&response)) { |
||||
EXPECT_EQ(response.message(), "ServerStreamingCall" + std::to_string(cnt)); |
||||
cnt++; |
||||
} |
||||
EXPECT_EQ(cnt, kServerResponseStreamsToSend); |
||||
grpc::Status status = reader->Finish(); |
||||
EXPECT_TRUE(status.ok()); |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, ServerStreamingCallCoalescingApi) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
constexpr size_t kServerResponseStreamsToSend = 100; |
||||
grpc::ClientContext context; |
||||
context.AddMetadata(grpc::testing::kServerResponseStreamsToSend, |
||||
std::to_string(kServerResponseStreamsToSend)); |
||||
context.AddMetadata(grpc::testing::kServerUseCoalescingApi, "1"); |
||||
grpc::testing::EchoRequest request; |
||||
request.set_message("ServerStreamingCallCoalescingApi"); |
||||
std::unique_ptr<grpc::ClientReader<grpc::testing::EchoResponse>> reader = |
||||
stub->ResponseStream(&context, request); |
||||
grpc::testing::EchoResponse response; |
||||
size_t cnt = 0; |
||||
while (reader->Read(&response)) { |
||||
EXPECT_EQ(response.message(), |
||||
"ServerStreamingCallCoalescingApi" + std::to_string(cnt)); |
||||
cnt++; |
||||
} |
||||
EXPECT_EQ(cnt, kServerResponseStreamsToSend); |
||||
grpc::Status status = reader->Finish(); |
||||
EXPECT_TRUE(status.ok()); |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, |
||||
ServerStreamingCallTryCancelBeforeProcessing) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
constexpr size_t kServerResponseStreamsToSend = 100; |
||||
grpc::ClientContext context; |
||||
context.AddMetadata(grpc::testing::kServerResponseStreamsToSend, |
||||
std::to_string(kServerResponseStreamsToSend)); |
||||
context.AddMetadata(grpc::testing::kServerTryCancelRequest, |
||||
std::to_string(grpc::testing::CANCEL_BEFORE_PROCESSING)); |
||||
grpc::testing::EchoRequest request; |
||||
request.set_message("ServerStreamingCallTryCancelBeforeProcessing"); |
||||
std::unique_ptr<grpc::ClientReader<grpc::testing::EchoResponse>> reader = |
||||
stub->ResponseStream(&context, request); |
||||
grpc::testing::EchoResponse response; |
||||
EXPECT_FALSE(reader->Read(&response)); |
||||
grpc::Status status = reader->Finish(); |
||||
EXPECT_FALSE(status.ok()); |
||||
EXPECT_EQ(status.error_code(), grpc::StatusCode::CANCELLED); |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, |
||||
ServerSteramingCallTryCancelDuringProcessing) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
constexpr size_t kServerResponseStreamsToSend = 2; |
||||
grpc::ClientContext context; |
||||
context.AddMetadata(grpc::testing::kServerResponseStreamsToSend, |
||||
std::to_string(kServerResponseStreamsToSend)); |
||||
context.AddMetadata(grpc::testing::kServerTryCancelRequest, |
||||
std::to_string(grpc::testing::CANCEL_DURING_PROCESSING)); |
||||
grpc::testing::EchoRequest request; |
||||
request.set_message("ServerStreamingCallTryCancelDuringProcessing"); |
||||
std::unique_ptr<grpc::ClientReader<grpc::testing::EchoResponse>> reader = |
||||
stub->ResponseStream(&context, request); |
||||
grpc::testing::EchoResponse response; |
||||
size_t cnt = 0; |
||||
while (reader->Read(&response)) { |
||||
EXPECT_EQ( |
||||
response.message(), |
||||
"ServerStreamingCallTryCancelDuringProcessing" + std::to_string(cnt)); |
||||
cnt++; |
||||
} |
||||
grpc::Status status = reader->Finish(); |
||||
EXPECT_FALSE(status.ok()); |
||||
EXPECT_EQ(status.error_code(), grpc::StatusCode::CANCELLED); |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, |
||||
ServerSteramingCallTryCancelAfterProcessing) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
constexpr size_t kServerResponseStreamsToSend = 100; |
||||
grpc::ClientContext context; |
||||
context.AddMetadata(grpc::testing::kServerResponseStreamsToSend, |
||||
std::to_string(kServerResponseStreamsToSend)); |
||||
context.AddMetadata(grpc::testing::kServerTryCancelRequest, |
||||
std::to_string(grpc::testing::CANCEL_AFTER_PROCESSING)); |
||||
grpc::testing::EchoRequest request; |
||||
request.set_message("ServerStreamingCallTryCancelAfterProcessing"); |
||||
std::unique_ptr<grpc::ClientReader<grpc::testing::EchoResponse>> reader = |
||||
stub->ResponseStream(&context, request); |
||||
grpc::testing::EchoResponse response; |
||||
size_t cnt = 0; |
||||
while (reader->Read(&response)) { |
||||
EXPECT_EQ( |
||||
response.message(), |
||||
"ServerStreamingCallTryCancelAfterProcessing" + std::to_string(cnt)); |
||||
cnt++; |
||||
} |
||||
EXPECT_EQ(cnt, kServerResponseStreamsToSend); |
||||
grpc::Status status = reader->Finish(); |
||||
EXPECT_FALSE(status.ok()); |
||||
EXPECT_EQ(status.error_code(), grpc::StatusCode::CANCELLED); |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, ClientStreamingCall) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
grpc::ClientContext context; |
||||
grpc::testing::EchoResponse response; |
||||
std::unique_ptr<grpc::ClientWriter<grpc::testing::EchoRequest>> writer = |
||||
stub->RequestStream(&context, &response); |
||||
constexpr size_t kClientStreamingCounts = 100; |
||||
std::string expected; |
||||
for (size_t i = 0; i < kClientStreamingCounts; ++i) { |
||||
grpc::testing::EchoRequest request; |
||||
request.set_message("ClientStreamingCall" + std::to_string(i)); |
||||
EXPECT_TRUE(writer->Write(request)); |
||||
expected += "ClientStreamingCall" + std::to_string(i); |
||||
} |
||||
writer->WritesDone(); |
||||
grpc::Status status = writer->Finish(); |
||||
EXPECT_TRUE(status.ok()); |
||||
EXPECT_EQ(response.message(), expected); |
||||
} |
||||
|
||||
// Disabled because the test case is ~0.002% flaky
|
||||
TEST_P(End2EndBinderTransportTest, |
||||
DISABLED_ClientStreamingCallTryCancelBeforeProcessing) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
grpc::ClientContext context; |
||||
context.AddMetadata(grpc::testing::kServerTryCancelRequest, |
||||
std::to_string(grpc::testing::CANCEL_BEFORE_PROCESSING)); |
||||
grpc::testing::EchoResponse response; |
||||
std::unique_ptr<grpc::ClientWriter<grpc::testing::EchoRequest>> writer = |
||||
stub->RequestStream(&context, &response); |
||||
constexpr size_t kClientStreamingCounts = 100; |
||||
for (size_t i = 0; i < kClientStreamingCounts; ++i) { |
||||
grpc::testing::EchoRequest request; |
||||
request.set_message("ClientStreamingCallBeforeProcessing" + |
||||
std::to_string(i)); |
||||
writer->Write(request); |
||||
} |
||||
writer->WritesDone(); |
||||
grpc::Status status = writer->Finish(); |
||||
EXPECT_FALSE(status.ok()); |
||||
EXPECT_EQ(status.error_code(), grpc::StatusCode::CANCELLED); |
||||
} |
||||
|
||||
// Disabled because the test case is ~0.002% flaky
|
||||
TEST_P(End2EndBinderTransportTest, |
||||
DISABLED_ClientStreamingCallTryCancelDuringProcessing) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
grpc::ClientContext context; |
||||
context.AddMetadata(grpc::testing::kServerTryCancelRequest, |
||||
std::to_string(grpc::testing::CANCEL_DURING_PROCESSING)); |
||||
grpc::testing::EchoResponse response; |
||||
std::unique_ptr<grpc::ClientWriter<grpc::testing::EchoRequest>> writer = |
||||
stub->RequestStream(&context, &response); |
||||
constexpr size_t kClientStreamingCounts = 100; |
||||
for (size_t i = 0; i < kClientStreamingCounts; ++i) { |
||||
grpc::testing::EchoRequest request; |
||||
request.set_message("ClientStreamingCallDuringProcessing" + |
||||
std::to_string(i)); |
||||
writer->Write(request); |
||||
} |
||||
writer->WritesDone(); |
||||
grpc::Status status = writer->Finish(); |
||||
EXPECT_FALSE(status.ok()); |
||||
EXPECT_EQ(status.error_code(), grpc::StatusCode::CANCELLED); |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, |
||||
ClientStreamingCallTryCancelAfterProcessing) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
grpc::ClientContext context; |
||||
context.AddMetadata(grpc::testing::kServerTryCancelRequest, |
||||
std::to_string(grpc::testing::CANCEL_AFTER_PROCESSING)); |
||||
grpc::testing::EchoResponse response; |
||||
std::unique_ptr<grpc::ClientWriter<grpc::testing::EchoRequest>> writer = |
||||
stub->RequestStream(&context, &response); |
||||
constexpr size_t kClientStreamingCounts = 100; |
||||
for (size_t i = 0; i < kClientStreamingCounts; ++i) { |
||||
grpc::testing::EchoRequest request; |
||||
request.set_message("ClientStreamingCallAfterProcessing" + |
||||
std::to_string(i)); |
||||
writer->Write(request); |
||||
} |
||||
writer->WritesDone(); |
||||
grpc::Status status = writer->Finish(); |
||||
EXPECT_FALSE(status.ok()); |
||||
EXPECT_EQ(status.error_code(), grpc::StatusCode::CANCELLED); |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, BiDirStreamingCall) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
grpc::ClientContext context; |
||||
std::shared_ptr<grpc::ClientReaderWriter<grpc::testing::EchoRequest, |
||||
grpc::testing::EchoResponse>> |
||||
stream = stub->BidiStream(&context); |
||||
constexpr size_t kBiDirStreamingCounts = 100; |
||||
|
||||
struct WriterArgs { |
||||
std::shared_ptr<grpc::ClientReaderWriter<grpc::testing::EchoRequest, |
||||
grpc::testing::EchoResponse>> |
||||
stream; |
||||
size_t bi_dir_streaming_counts; |
||||
} writer_args; |
||||
|
||||
writer_args.stream = stream; |
||||
writer_args.bi_dir_streaming_counts = kBiDirStreamingCounts; |
||||
|
||||
auto writer_fn = [](void* arg) { |
||||
const WriterArgs& args = *static_cast<WriterArgs*>(arg); |
||||
for (size_t i = 0; i < args.bi_dir_streaming_counts; ++i) { |
||||
grpc::testing::EchoRequest request; |
||||
request.set_message("BiDirStreamingCall" + std::to_string(i)); |
||||
args.stream->Write(request); |
||||
} |
||||
args.stream->WritesDone(); |
||||
}; |
||||
|
||||
grpc_core::Thread writer_thread("writer-thread", writer_fn, |
||||
static_cast<void*>(&writer_args)); |
||||
writer_thread.Start(); |
||||
for (size_t i = 0; i < kBiDirStreamingCounts; ++i) { |
||||
grpc::testing::EchoResponse response; |
||||
EXPECT_TRUE(stream->Read(&response)); |
||||
EXPECT_EQ(response.message(), "BiDirStreamingCall" + std::to_string(i)); |
||||
} |
||||
grpc::Status status = stream->Finish(); |
||||
EXPECT_TRUE(status.ok()); |
||||
writer_thread.Join(); |
||||
} |
||||
|
||||
// Disabled because the test case is ~0.01% flaky
|
||||
TEST_P(End2EndBinderTransportTest, |
||||
DISABLED_BiDirStreamingCallServerFinishesHalfway) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
constexpr size_t kBiDirStreamingCounts = 100; |
||||
grpc::ClientContext context; |
||||
context.AddMetadata(grpc::testing::kServerFinishAfterNReads, |
||||
std::to_string(kBiDirStreamingCounts / 2)); |
||||
std::shared_ptr<grpc::ClientReaderWriter<grpc::testing::EchoRequest, |
||||
grpc::testing::EchoResponse>> |
||||
stream = stub->BidiStream(&context); |
||||
|
||||
struct WriterArgs { |
||||
std::shared_ptr<grpc::ClientReaderWriter<grpc::testing::EchoRequest, |
||||
grpc::testing::EchoResponse>> |
||||
stream; |
||||
size_t bi_dir_streaming_counts; |
||||
} writer_args; |
||||
|
||||
writer_args.stream = stream; |
||||
writer_args.bi_dir_streaming_counts = kBiDirStreamingCounts; |
||||
|
||||
auto writer_fn = [](void* arg) { |
||||
const WriterArgs& args = *static_cast<WriterArgs*>(arg); |
||||
for (size_t i = 0; i < args.bi_dir_streaming_counts; ++i) { |
||||
grpc::testing::EchoRequest request; |
||||
request.set_message("BiDirStreamingCallServerFinishesHalfway" + |
||||
std::to_string(i)); |
||||
if (!args.stream->Write(request)) { |
||||
return; |
||||
} |
||||
} |
||||
args.stream->WritesDone(); |
||||
}; |
||||
|
||||
grpc_core::Thread writer_thread("writer-thread", writer_fn, |
||||
static_cast<void*>(&writer_args)); |
||||
writer_thread.Start(); |
||||
for (size_t i = 0; i < kBiDirStreamingCounts / 2; ++i) { |
||||
grpc::testing::EchoResponse response; |
||||
EXPECT_TRUE(stream->Read(&response)); |
||||
EXPECT_EQ(response.message(), |
||||
"BiDirStreamingCallServerFinishesHalfway" + std::to_string(i)); |
||||
} |
||||
grpc::testing::EchoResponse response; |
||||
EXPECT_FALSE(stream->Read(&response)); |
||||
writer_thread.Join(); |
||||
grpc::Status status = stream->Finish(); |
||||
EXPECT_TRUE(status.ok()); |
||||
} |
||||
|
||||
TEST_P(End2EndBinderTransportTest, LargeMessages) { |
||||
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub = NewStub(); |
||||
for (size_t size = 1; size <= 1024 * 1024; size *= 4) { |
||||
grpc::ClientContext context; |
||||
grpc::testing::EchoRequest request; |
||||
grpc::testing::EchoResponse response; |
||||
request.set_message(std::string(size, 'a')); |
||||
grpc::Status status = stub->Echo(&context, request, &response); |
||||
EXPECT_TRUE(status.ok()); |
||||
EXPECT_EQ(response.message().size(), size); |
||||
EXPECT_TRUE(std::all_of(response.message().begin(), |
||||
response.message().end(), |
||||
[](char c) { return c == 'a'; })); |
||||
} |
||||
} |
||||
|
||||
INSTANTIATE_TEST_SUITE_P(End2EndBinderTransportTestWithDifferentDelayTimes, |
||||
End2EndBinderTransportTest, |
||||
testing::Values(absl::ZeroDuration(), |
||||
absl::Microseconds(10), |
||||
absl::Milliseconds(10))); |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
grpc::testing::TestEnvironment env(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
@ -1,276 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "test/core/transport/binder/end2end/fake_binder.h" |
||||
|
||||
#include <string> |
||||
#include <utility> |
||||
|
||||
#include "absl/log/log.h" |
||||
#include "src/core/util/crash.h" |
||||
|
||||
namespace grpc_binder { |
||||
namespace end2end_testing { |
||||
|
||||
TransactionProcessor* g_transaction_processor = nullptr; |
||||
|
||||
int32_t FakeWritableParcel::GetDataSize() const { return data_size_; } |
||||
|
||||
absl::Status FakeWritableParcel::WriteInt32(int32_t data) { |
||||
data_.push_back(data); |
||||
data_size_ += sizeof(int32_t); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status FakeWritableParcel::WriteInt64(int64_t data) { |
||||
data_.push_back(data); |
||||
data_size_ += sizeof(int64_t); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status FakeWritableParcel::WriteBinder(HasRawBinder* binder) { |
||||
data_.push_back(binder->GetRawBinder()); |
||||
data_size_ += sizeof(void*); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status FakeWritableParcel::WriteString(absl::string_view s) { |
||||
data_.push_back(std::string(s)); |
||||
data_size_ += s.size(); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status FakeWritableParcel::WriteByteArray(const int8_t* buffer, |
||||
int32_t length) { |
||||
data_.push_back(std::vector<int8_t>(buffer, buffer + length)); |
||||
data_size_ += length; |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
int32_t FakeReadableParcel::GetDataSize() const { return data_size_; } |
||||
|
||||
absl::Status FakeReadableParcel::ReadInt32(int32_t* data) { |
||||
if (data_position_ >= data_.size() || |
||||
!absl::holds_alternative<int32_t>(data_[data_position_])) { |
||||
return absl::InternalError("ReadInt32 failed"); |
||||
} |
||||
*data = absl::get<int32_t>(data_[data_position_++]); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status FakeReadableParcel::ReadInt64(int64_t* data) { |
||||
if (data_position_ >= data_.size() || |
||||
!absl::holds_alternative<int64_t>(data_[data_position_])) { |
||||
return absl::InternalError("ReadInt64 failed"); |
||||
} |
||||
*data = absl::get<int64_t>(data_[data_position_++]); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status FakeReadableParcel::ReadBinder(std::unique_ptr<Binder>* data) { |
||||
if (data_position_ >= data_.size() || |
||||
!absl::holds_alternative<void*>(data_[data_position_])) { |
||||
return absl::InternalError("ReadBinder failed"); |
||||
} |
||||
void* endpoint = absl::get<void*>(data_[data_position_++]); |
||||
if (!endpoint) return absl::InternalError("ReadBinder failed"); |
||||
*data = std::make_unique<FakeBinder>(static_cast<FakeEndpoint*>(endpoint)); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status FakeReadableParcel::ReadString(std::string* str) { |
||||
if (data_position_ >= data_.size() || |
||||
!absl::holds_alternative<std::string>(data_[data_position_])) { |
||||
return absl::InternalError("ReadString failed"); |
||||
} |
||||
*str = absl::get<std::string>(data_[data_position_++]); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status FakeReadableParcel::ReadByteArray(std::string* data) { |
||||
if (data_position_ >= data_.size() || |
||||
!absl::holds_alternative<std::vector<int8_t>>(data_[data_position_])) { |
||||
return absl::InternalError("ReadByteArray failed"); |
||||
} |
||||
const std::vector<int8_t>& byte_array = |
||||
absl::get<std::vector<int8_t>>(data_[data_position_++]); |
||||
data->resize(byte_array.size()); |
||||
for (size_t i = 0; i < byte_array.size(); ++i) { |
||||
(*data)[i] = byte_array[i]; |
||||
} |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status FakeBinder::Transact(BinderTransportTxCode tx_code) { |
||||
endpoint_->tunnel->EnQueueTransaction(endpoint_->other_end, tx_code, |
||||
input_->MoveData()); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
FakeTransactionReceiver::FakeTransactionReceiver( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb transact_cb) { |
||||
persistent_tx_receiver_ = &g_transaction_processor->NewPersistentTxReceiver( |
||||
std::move(wire_reader_ref), std::move(transact_cb), |
||||
std::make_unique<FakeBinderTunnel>()); |
||||
} |
||||
|
||||
std::unique_ptr<TransactionReceiver> FakeBinder::ConstructTxReceiver( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb cb) const { |
||||
return std::make_unique<FakeTransactionReceiver>(wire_reader_ref, cb); |
||||
} |
||||
|
||||
void* FakeTransactionReceiver::GetRawBinder() { |
||||
return persistent_tx_receiver_->tunnel_->GetSendEndpoint(); |
||||
} |
||||
|
||||
std::unique_ptr<Binder> FakeTransactionReceiver::GetSender() const { |
||||
return std::make_unique<FakeBinder>( |
||||
persistent_tx_receiver_->tunnel_->GetSendEndpoint()); |
||||
} |
||||
|
||||
PersistentFakeTransactionReceiver::PersistentFakeTransactionReceiver( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb cb, |
||||
std::unique_ptr<FakeBinderTunnel> tunnel) |
||||
: wire_reader_ref_(std::move(wire_reader_ref)), |
||||
callback_(std::move(cb)), |
||||
tunnel_(std::move(tunnel)) { |
||||
FakeEndpoint* recv_endpoint = tunnel_->GetRecvEndpoint(); |
||||
recv_endpoint->owner = this; |
||||
} |
||||
|
||||
TransactionProcessor::TransactionProcessor(absl::Duration delay) |
||||
: delay_nsec_(absl::ToInt64Nanoseconds(delay)), |
||||
tx_thread_( |
||||
"process-thread", |
||||
[](void* arg) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
auto* self = static_cast<TransactionProcessor*>(arg); |
||||
self->ProcessLoop(); |
||||
}, |
||||
this), |
||||
terminated_(false) { |
||||
tx_thread_.Start(); |
||||
} |
||||
|
||||
void TransactionProcessor::SetDelay(absl::Duration delay) { |
||||
delay_nsec_ = absl::ToInt64Nanoseconds(delay); |
||||
} |
||||
|
||||
void TransactionProcessor::Terminate() { |
||||
if (!terminated_.load(std::memory_order_seq_cst)) { |
||||
LOG(INFO) << "Terminating the processor"; |
||||
terminated_.store(true, std::memory_order_seq_cst); |
||||
tx_thread_.Join(); |
||||
LOG(INFO) << "Processor terminated"; |
||||
} |
||||
} |
||||
|
||||
void TransactionProcessor::WaitForNextTransaction() { |
||||
absl::Time now = absl::Now(); |
||||
if (now < deliver_time_) { |
||||
absl::Duration diff = deliver_time_ - now; |
||||
// Release the lock before going to sleep.
|
||||
mu_.Unlock(); |
||||
absl::SleepFor(diff); |
||||
mu_.Lock(); |
||||
} |
||||
} |
||||
|
||||
void TransactionProcessor::Flush() { |
||||
while (true) { |
||||
FakeEndpoint* target = nullptr; |
||||
BinderTransportTxCode tx_code{}; |
||||
FakeData data; |
||||
mu_.Lock(); |
||||
if (tx_queue_.empty()) { |
||||
mu_.Unlock(); |
||||
break; |
||||
} |
||||
WaitForNextTransaction(); |
||||
std::tie(target, tx_code, data) = std::move(tx_queue_.front()); |
||||
tx_queue_.pop(); |
||||
if (!tx_queue_.empty()) { |
||||
deliver_time_ = absl::Now() + GetRandomDelay(); |
||||
} |
||||
mu_.Unlock(); |
||||
auto* tx_receiver = |
||||
static_cast<PersistentFakeTransactionReceiver*>(target->owner); |
||||
auto parcel = std::make_unique<FakeReadableParcel>(std::move(data)); |
||||
tx_receiver->Receive(tx_code, parcel.get()).IgnoreError(); |
||||
} |
||||
} |
||||
|
||||
void TransactionProcessor::ProcessLoop() { |
||||
while (!terminated_.load(std::memory_order_seq_cst)) { |
||||
FakeEndpoint* target = nullptr; |
||||
BinderTransportTxCode tx_code{}; |
||||
FakeData data; |
||||
mu_.Lock(); |
||||
if (tx_queue_.empty()) { |
||||
mu_.Unlock(); |
||||
continue; |
||||
} |
||||
WaitForNextTransaction(); |
||||
std::tie(target, tx_code, data) = std::move(tx_queue_.front()); |
||||
tx_queue_.pop(); |
||||
if (!tx_queue_.empty()) { |
||||
deliver_time_ = absl::Now() + GetRandomDelay(); |
||||
} |
||||
mu_.Unlock(); |
||||
auto* tx_receiver = |
||||
static_cast<PersistentFakeTransactionReceiver*>(target->owner); |
||||
auto parcel = std::make_unique<FakeReadableParcel>(std::move(data)); |
||||
tx_receiver->Receive(tx_code, parcel.get()).IgnoreError(); |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
} |
||||
Flush(); |
||||
} |
||||
|
||||
absl::Duration TransactionProcessor::GetRandomDelay() { |
||||
int64_t delay = |
||||
absl::Uniform<int64_t>(bit_gen_, delay_nsec_ / 2, delay_nsec_); |
||||
return absl::Nanoseconds(delay); |
||||
} |
||||
|
||||
void TransactionProcessor::EnQueueTransaction(FakeEndpoint* target, |
||||
BinderTransportTxCode tx_code, |
||||
FakeData data) { |
||||
grpc_core::MutexLock lock(&mu_); |
||||
if (tx_queue_.empty()) { |
||||
// This is the first transaction in the queue. Compute its deliver time.
|
||||
deliver_time_ = absl::Now() + GetRandomDelay(); |
||||
} |
||||
tx_queue_.emplace(target, tx_code, std::move(data)); |
||||
} |
||||
|
||||
FakeBinderTunnel::FakeBinderTunnel() |
||||
: send_endpoint_(std::make_unique<FakeEndpoint>(this)), |
||||
recv_endpoint_(std::make_unique<FakeEndpoint>(this)) { |
||||
send_endpoint_->other_end = recv_endpoint_.get(); |
||||
recv_endpoint_->other_end = send_endpoint_.get(); |
||||
} |
||||
|
||||
std::pair<std::unique_ptr<Binder>, std::unique_ptr<TransactionReceiver>> |
||||
NewBinderPair(TransactionReceiver::OnTransactCb transact_cb) { |
||||
auto tx_receiver = std::make_unique<FakeTransactionReceiver>( |
||||
nullptr, std::move(transact_cb)); |
||||
std::unique_ptr<Binder> sender = tx_receiver->GetSender(); |
||||
return std::make_pair(std::move(sender), std::move(tx_receiver)); |
||||
} |
||||
|
||||
} // namespace end2end_testing
|
||||
} // namespace grpc_binder
|
@ -1,307 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// A collection of fake objects that offers in-memory simulation of data
|
||||
// transmission from one binder to another.
|
||||
//
|
||||
// Once the implementation of Binder is changed from BinderAndroid to
|
||||
// FakeBinder, we'll be able to test and fuzz our end-to-end binder transport in
|
||||
// a non-Android environment.
|
||||
//
|
||||
// The following diagram shows the high-level overview of how the in-memory
|
||||
// simulation works (FakeReceiver means FakeTransactionReceiver).
|
||||
//
|
||||
// thread boundary
|
||||
// |
|
||||
// |
|
||||
// ---------------- ---------------- | receive
|
||||
// | FakeBinder | | FakeReceiver | <--|----------------
|
||||
// ---------------- ---------------- | |
|
||||
// | ^ | ------------------------
|
||||
// | endpoint owner | | | TransactionProcessor |
|
||||
// | | | ------------------------
|
||||
// v | | ^
|
||||
// ---------------- ---------------- | |
|
||||
// | FakeEndpoint | --------> | FakeEndpoint | ---|----------------
|
||||
// ---------------- other_end ---------------- | enqueue
|
||||
// | ^ ^ | |
|
||||
// | | recv_endpoint | | |
|
||||
// | | | |
|
||||
// | | send_endpoint | |
|
||||
// v | | v
|
||||
// -------------------------------------------
|
||||
// | FakeBinderTunnel |
|
||||
// -------------------------------------------
|
||||
|
||||
#ifndef GRPC_TEST_CORE_TRANSPORT_BINDER_END2END_FAKE_BINDER_H |
||||
#define GRPC_TEST_CORE_TRANSPORT_BINDER_END2END_FAKE_BINDER_H |
||||
|
||||
#include <atomic> |
||||
#include <forward_list> |
||||
#include <memory> |
||||
#include <queue> |
||||
#include <string> |
||||
#include <thread> |
||||
#include <tuple> |
||||
#include <utility> |
||||
#include <vector> |
||||
|
||||
#include "absl/memory/memory.h" |
||||
#include "absl/random/random.h" |
||||
#include "absl/strings/str_format.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/time/time.h" |
||||
#include "absl/types/variant.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_reader.h" |
||||
#include "src/core/util/sync.h" |
||||
#include "src/core/util/thd.h" |
||||
|
||||
namespace grpc_binder { |
||||
namespace end2end_testing { |
||||
|
||||
using FakeData = std::vector< |
||||
absl::variant<int32_t, int64_t, void*, std::string, std::vector<int8_t>>>; |
||||
|
||||
// A fake writable parcel.
|
||||
//
|
||||
// It simulates the functionalities of a real writable parcel and stores all
|
||||
// written data in memory. The data can then be transferred by calling
|
||||
// MoveData().
|
||||
class FakeWritableParcel final : public WritableParcel { |
||||
public: |
||||
int32_t GetDataSize() const override; |
||||
absl::Status WriteInt32(int32_t data) override; |
||||
absl::Status WriteInt64(int64_t data) override; |
||||
absl::Status WriteBinder(HasRawBinder* binder) override; |
||||
absl::Status WriteString(absl::string_view s) override; |
||||
absl::Status WriteByteArray(const int8_t* buffer, int32_t length) override; |
||||
|
||||
FakeData MoveData() { return std::move(data_); } |
||||
|
||||
private: |
||||
FakeData data_; |
||||
int32_t data_size_ = 0; |
||||
}; |
||||
|
||||
// A fake readable parcel.
|
||||
//
|
||||
// It takes in the data transferred from a FakeWritableParcel and provides
|
||||
// methods to retrieve those data in the receiving end.
|
||||
class FakeReadableParcel final : public ReadableParcel { |
||||
public: |
||||
explicit FakeReadableParcel(FakeData data) : data_(std::move(data)) { |
||||
for (auto& d : data_) { |
||||
if (absl::holds_alternative<int32_t>(d)) { |
||||
data_size_ += sizeof(int32_t); |
||||
} else if (absl::holds_alternative<int64_t>(d)) { |
||||
data_size_ += sizeof(int64_t); |
||||
} else if (absl::holds_alternative<void*>(d)) { |
||||
data_size_ += sizeof(void*); |
||||
} else if (absl::holds_alternative<std::string>(d)) { |
||||
data_size_ += absl::get<std::string>(d).size(); |
||||
} else { |
||||
data_size_ += absl::get<std::vector<int8_t>>(d).size(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
int32_t GetDataSize() const override; |
||||
absl::Status ReadInt32(int32_t* data) override; |
||||
absl::Status ReadInt64(int64_t* data) override; |
||||
absl::Status ReadBinder(std::unique_ptr<Binder>* data) override; |
||||
absl::Status ReadByteArray(std::string* data) override; |
||||
absl::Status ReadString(std::string* str) override; |
||||
|
||||
private: |
||||
const FakeData data_; |
||||
size_t data_position_ = 0; |
||||
int32_t data_size_ = 0; |
||||
}; |
||||
|
||||
class FakeBinder; |
||||
class FakeBinderTunnel; |
||||
|
||||
// FakeEndpoint is a simple struct that holds the pointer to the other end, a
|
||||
// pointer to the tunnel and a pointer to its owner. This tells the owner where
|
||||
// the data should be sent.
|
||||
struct FakeEndpoint { |
||||
explicit FakeEndpoint(FakeBinderTunnel* tunnel) : tunnel(tunnel) {} |
||||
|
||||
FakeEndpoint* other_end; |
||||
FakeBinderTunnel* tunnel; |
||||
// The owner is either a FakeBinder (the sending part) or a
|
||||
// FakeTransactionReceiver (the receiving part). Both parts hold an endpoint
|
||||
// with |owner| pointing back to them and |other_end| pointing to each other.
|
||||
void* owner; |
||||
}; |
||||
|
||||
class PersistentFakeTransactionReceiver; |
||||
|
||||
// A fake transaction receiver.
|
||||
//
|
||||
// This is the receiving part of a pair of binders. When constructed, a binder
|
||||
// tunnle is created, and the sending part can be retrieved by calling
|
||||
// GetSender().
|
||||
//
|
||||
// It also provides a Receive() function to simulate the on-transaction
|
||||
// callback of a real Android binder.
|
||||
class FakeTransactionReceiver : public TransactionReceiver { |
||||
public: |
||||
FakeTransactionReceiver(grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb cb); |
||||
|
||||
void* GetRawBinder() override; |
||||
|
||||
std::unique_ptr<Binder> GetSender() const; |
||||
|
||||
private: |
||||
PersistentFakeTransactionReceiver* persistent_tx_receiver_; |
||||
}; |
||||
|
||||
// A "persistent" version of the FakeTransactionReceiver. That is, its lifetime
|
||||
// is managed by the processor and it outlives the wire reader and
|
||||
// grpc_binder_transport, so we can safely dereference a pointer to it in
|
||||
// ProcessLoop().
|
||||
class PersistentFakeTransactionReceiver { |
||||
public: |
||||
PersistentFakeTransactionReceiver( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb cb, |
||||
std::unique_ptr<FakeBinderTunnel> tunnel); |
||||
|
||||
absl::Status Receive(BinderTransportTxCode tx_code, ReadableParcel* parcel) { |
||||
return callback_(static_cast<transaction_code_t>(tx_code), parcel, |
||||
/*uid=*/0); |
||||
} |
||||
|
||||
private: |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref_; |
||||
TransactionReceiver::OnTransactCb callback_; |
||||
std::unique_ptr<FakeBinderTunnel> tunnel_; |
||||
|
||||
friend class FakeTransactionReceiver; |
||||
}; |
||||
|
||||
// The sending part of a binders pair. It provides a FakeWritableParcel to the
|
||||
// user, and when Transact() is called, it transfers the written data to the
|
||||
// other end of the tunnel by following the information in its endpoint.
|
||||
class FakeBinder final : public Binder { |
||||
public: |
||||
explicit FakeBinder(FakeEndpoint* endpoint) : endpoint_(endpoint) {} |
||||
|
||||
void Initialize() override {} |
||||
absl::Status PrepareTransaction() override { |
||||
input_ = std::make_unique<FakeWritableParcel>(); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status Transact(BinderTransportTxCode tx_code) override; |
||||
|
||||
WritableParcel* GetWritableParcel() const override { return input_.get(); } |
||||
|
||||
std::unique_ptr<TransactionReceiver> ConstructTxReceiver( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb transact_cb) const override; |
||||
|
||||
void* GetRawBinder() override { return endpoint_->other_end; } |
||||
|
||||
private: |
||||
FakeEndpoint* endpoint_; |
||||
std::unique_ptr<FakeWritableParcel> input_; |
||||
}; |
||||
|
||||
// A transaction processor.
|
||||
//
|
||||
// Once constructed, it'll create a another thread that deliver in-coming
|
||||
// transactions to their destinations.
|
||||
class TransactionProcessor { |
||||
public: |
||||
explicit TransactionProcessor(absl::Duration delay = absl::ZeroDuration()); |
||||
~TransactionProcessor() { Terminate(); } |
||||
|
||||
void SetDelay(absl::Duration delay); |
||||
|
||||
void Terminate(); |
||||
void ProcessLoop(); |
||||
void Flush(); |
||||
|
||||
// Issue a transaction with |target| pointing to the target endpoint. The
|
||||
// transactions will be delivered in the same order they're issued, possibly
|
||||
// with random delay to simulate real-world situation.
|
||||
void EnQueueTransaction(FakeEndpoint* target, BinderTransportTxCode tx_code, |
||||
FakeData data); |
||||
|
||||
PersistentFakeTransactionReceiver& NewPersistentTxReceiver( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb cb, |
||||
std::unique_ptr<FakeBinderTunnel> tunnel) { |
||||
grpc_core::MutexLock lock(&tx_receiver_mu_); |
||||
storage_.emplace_front(wire_reader_ref, cb, std::move(tunnel)); |
||||
return storage_.front(); |
||||
} |
||||
|
||||
private: |
||||
absl::Duration GetRandomDelay(); |
||||
void WaitForNextTransaction() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); |
||||
|
||||
grpc_core::Mutex mu_; |
||||
std::queue<std::tuple<FakeEndpoint*, BinderTransportTxCode, FakeData>> |
||||
tx_queue_ ABSL_GUARDED_BY(mu_); |
||||
absl::Time deliver_time_ ABSL_GUARDED_BY(mu_); |
||||
int64_t delay_nsec_; |
||||
absl::BitGen bit_gen_; |
||||
grpc_core::Thread tx_thread_; |
||||
std::atomic<bool> terminated_; |
||||
|
||||
grpc_core::Mutex tx_receiver_mu_; |
||||
// Use forward_list to avoid invalid pointers resulted by reallocation in
|
||||
// containers such as std::vector.
|
||||
std::forward_list<PersistentFakeTransactionReceiver> storage_ |
||||
ABSL_GUARDED_BY(tx_receiver_mu_); |
||||
}; |
||||
|
||||
// The global (shared) processor. Test suite should be responsible of
|
||||
// creating/deleting it.
|
||||
extern TransactionProcessor* g_transaction_processor; |
||||
|
||||
// A binder tunnel.
|
||||
//
|
||||
// It is a simple helper that creates and links two endpoints.
|
||||
class FakeBinderTunnel { |
||||
public: |
||||
FakeBinderTunnel(); |
||||
|
||||
void EnQueueTransaction(FakeEndpoint* target, BinderTransportTxCode tx_code, |
||||
FakeData data) { |
||||
g_transaction_processor->EnQueueTransaction(target, tx_code, |
||||
std::move(data)); |
||||
} |
||||
|
||||
FakeEndpoint* GetSendEndpoint() const { return send_endpoint_.get(); } |
||||
FakeEndpoint* GetRecvEndpoint() const { return recv_endpoint_.get(); } |
||||
|
||||
private: |
||||
std::unique_ptr<FakeEndpoint> send_endpoint_; |
||||
std::unique_ptr<FakeEndpoint> recv_endpoint_; |
||||
}; |
||||
|
||||
// A helper function for constructing a pair of connected binders.
|
||||
std::pair<std::unique_ptr<Binder>, std::unique_ptr<TransactionReceiver>> |
||||
NewBinderPair(TransactionReceiver::OnTransactCb transact_cb); |
||||
|
||||
} // namespace end2end_testing
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_TEST_CORE_TRANSPORT_BINDER_END2END_FAKE_BINDER_H
|
@ -1,349 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "test/core/transport/binder/end2end/fake_binder.h" |
||||
|
||||
#include <gmock/gmock.h> |
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <algorithm> |
||||
#include <random> |
||||
#include <string> |
||||
#include <utility> |
||||
|
||||
#include "absl/strings/str_format.h" |
||||
#include "absl/time/time.h" |
||||
#include "test/core/test_util/test_config.h" |
||||
|
||||
namespace grpc_binder { |
||||
namespace end2end_testing { |
||||
namespace { |
||||
|
||||
class FakeBinderTest : public ::testing::TestWithParam<absl::Duration> { |
||||
public: |
||||
FakeBinderTest() { |
||||
g_transaction_processor = new TransactionProcessor(GetParam()); |
||||
} |
||||
~FakeBinderTest() override { delete g_transaction_processor; } |
||||
}; |
||||
|
||||
} // namespace
|
||||
|
||||
TEST_P(FakeBinderTest, SendInt32) { |
||||
constexpr int kValue = 0x1234; |
||||
constexpr int kTxCode = 0x4321; |
||||
int called = 0; |
||||
std::unique_ptr<Binder> sender; |
||||
std::unique_ptr<TransactionReceiver> tx_receiver; |
||||
std::tie(sender, tx_receiver) = NewBinderPair( |
||||
[&](transaction_code_t tx_code, ReadableParcel* parcel, int /*uid*/) { |
||||
EXPECT_EQ(tx_code, kTxCode); |
||||
int value = 0; |
||||
EXPECT_TRUE(parcel->ReadInt32(&value).ok()); |
||||
EXPECT_EQ(value, kValue); |
||||
called++; |
||||
return absl::OkStatus(); |
||||
}); |
||||
|
||||
EXPECT_TRUE(sender->PrepareTransaction().ok()); |
||||
WritableParcel* parcel = sender->GetWritableParcel(); |
||||
EXPECT_TRUE(parcel->WriteInt32(kValue).ok()); |
||||
EXPECT_TRUE(sender->Transact(BinderTransportTxCode(kTxCode)).ok()); |
||||
|
||||
g_transaction_processor->Terminate(); |
||||
EXPECT_EQ(called, 1); |
||||
} |
||||
|
||||
TEST_P(FakeBinderTest, SendString) { |
||||
constexpr char kValue[] = "example-string"; |
||||
constexpr int kTxCode = 0x4321; |
||||
int called = 0; |
||||
std::unique_ptr<Binder> sender; |
||||
std::unique_ptr<TransactionReceiver> tx_receiver; |
||||
std::tie(sender, tx_receiver) = NewBinderPair( |
||||
[&](transaction_code_t tx_code, ReadableParcel* parcel, int /*uid*/) { |
||||
EXPECT_EQ(tx_code, kTxCode); |
||||
std::string value; |
||||
EXPECT_TRUE(parcel->ReadString(&value).ok()); |
||||
EXPECT_STREQ(value.c_str(), kValue); |
||||
called++; |
||||
return absl::OkStatus(); |
||||
}); |
||||
|
||||
EXPECT_TRUE(sender->PrepareTransaction().ok()); |
||||
WritableParcel* parcel = sender->GetWritableParcel(); |
||||
EXPECT_TRUE(parcel->WriteString(kValue).ok()); |
||||
EXPECT_TRUE(sender->Transact(BinderTransportTxCode(kTxCode)).ok()); |
||||
|
||||
g_transaction_processor->Terminate(); |
||||
EXPECT_EQ(called, 1); |
||||
} |
||||
|
||||
TEST_P(FakeBinderTest, SendByteArray) { |
||||
constexpr char kValue[] = "example-byte-array"; |
||||
constexpr int kTxCode = 0x4321; |
||||
int called = 0; |
||||
std::unique_ptr<Binder> sender; |
||||
std::unique_ptr<TransactionReceiver> tx_receiver; |
||||
std::tie(sender, tx_receiver) = NewBinderPair( |
||||
[&](transaction_code_t tx_code, ReadableParcel* parcel, int /*uid*/) { |
||||
EXPECT_EQ(tx_code, kTxCode); |
||||
std::string value; |
||||
EXPECT_TRUE(parcel->ReadByteArray(&value).ok()); |
||||
EXPECT_EQ(value, kValue); |
||||
called++; |
||||
return absl::OkStatus(); |
||||
}); |
||||
|
||||
EXPECT_TRUE(sender->PrepareTransaction().ok()); |
||||
WritableParcel* parcel = sender->GetWritableParcel(); |
||||
EXPECT_TRUE(parcel |
||||
->WriteByteArray(reinterpret_cast<const int8_t*>(kValue), |
||||
strlen(kValue)) |
||||
.ok()); |
||||
EXPECT_TRUE(sender->Transact(BinderTransportTxCode(kTxCode)).ok()); |
||||
|
||||
g_transaction_processor->Terminate(); |
||||
EXPECT_EQ(called, 1); |
||||
} |
||||
|
||||
TEST_P(FakeBinderTest, SendMultipleItems) { |
||||
constexpr char kByteArray[] = "example-byte-array"; |
||||
constexpr char kString[] = "example-string"; |
||||
constexpr int kValue = 0x1234; |
||||
constexpr int kTxCode = 0x4321; |
||||
int called = 0; |
||||
std::unique_ptr<Binder> sender; |
||||
std::unique_ptr<TransactionReceiver> tx_receiver; |
||||
std::tie(sender, tx_receiver) = NewBinderPair( |
||||
[&](transaction_code_t tx_code, ReadableParcel* parcel, int /*uid*/) { |
||||
int value_result; |
||||
EXPECT_EQ(tx_code, kTxCode); |
||||
EXPECT_TRUE(parcel->ReadInt32(&value_result).ok()); |
||||
EXPECT_EQ(value_result, kValue); |
||||
std::string byte_array_result; |
||||
EXPECT_TRUE(parcel->ReadByteArray(&byte_array_result).ok()); |
||||
EXPECT_EQ(byte_array_result, kByteArray); |
||||
std::string string_result; |
||||
EXPECT_TRUE(parcel->ReadString(&string_result).ok()); |
||||
EXPECT_STREQ(string_result.c_str(), kString); |
||||
called++; |
||||
return absl::OkStatus(); |
||||
}); |
||||
|
||||
EXPECT_TRUE(sender->PrepareTransaction().ok()); |
||||
WritableParcel* parcel = sender->GetWritableParcel(); |
||||
EXPECT_TRUE(parcel->WriteInt32(kValue).ok()); |
||||
EXPECT_TRUE(parcel |
||||
->WriteByteArray(reinterpret_cast<const int8_t*>(kByteArray), |
||||
strlen(kByteArray)) |
||||
.ok()); |
||||
EXPECT_TRUE(parcel->WriteString(kString).ok()); |
||||
EXPECT_TRUE(sender->Transact(BinderTransportTxCode(kTxCode)).ok()); |
||||
|
||||
g_transaction_processor->Terminate(); |
||||
EXPECT_EQ(called, 1); |
||||
} |
||||
|
||||
TEST_P(FakeBinderTest, SendBinder) { |
||||
constexpr int kValue = 0x1234; |
||||
constexpr int kTxCode = 0x4321; |
||||
int called = 0; |
||||
std::unique_ptr<Binder> sender; |
||||
std::unique_ptr<TransactionReceiver> tx_receiver; |
||||
std::tie(sender, tx_receiver) = NewBinderPair( |
||||
[&](transaction_code_t tx_code, ReadableParcel* parcel, int /*uid*/) { |
||||
EXPECT_EQ(tx_code, kTxCode); |
||||
std::unique_ptr<Binder> binder; |
||||
EXPECT_TRUE(parcel->ReadBinder(&binder).ok()); |
||||
EXPECT_TRUE(binder->PrepareTransaction().ok()); |
||||
WritableParcel* writable_parcel = binder->GetWritableParcel(); |
||||
EXPECT_TRUE(writable_parcel->WriteInt32(kValue).ok()); |
||||
EXPECT_TRUE(binder->Transact(BinderTransportTxCode(kTxCode + 1)).ok()); |
||||
called++; |
||||
return absl::OkStatus(); |
||||
}); |
||||
|
||||
int called2 = 0; |
||||
std::unique_ptr<TransactionReceiver> tx_receiver2 = |
||||
std::make_unique<FakeTransactionReceiver>( |
||||
nullptr, |
||||
[&](transaction_code_t tx_code, ReadableParcel* parcel, int /*uid*/) { |
||||
int value; |
||||
EXPECT_TRUE(parcel->ReadInt32(&value).ok()); |
||||
EXPECT_EQ(value, kValue); |
||||
EXPECT_EQ(tx_code, kTxCode + 1); |
||||
called2++; |
||||
return absl::OkStatus(); |
||||
}); |
||||
EXPECT_TRUE(sender->PrepareTransaction().ok()); |
||||
WritableParcel* parcel = sender->GetWritableParcel(); |
||||
EXPECT_TRUE(parcel->WriteBinder(tx_receiver2.get()).ok()); |
||||
EXPECT_TRUE(sender->Transact(BinderTransportTxCode(kTxCode)).ok()); |
||||
|
||||
g_transaction_processor->Terminate(); |
||||
EXPECT_EQ(called, 1); |
||||
EXPECT_EQ(called2, 1); |
||||
} |
||||
|
||||
TEST_P(FakeBinderTest, SendTransactionAfterDestruction) { |
||||
constexpr int kValue = 0x1234; |
||||
constexpr int kTxCode = 0x4321; |
||||
std::unique_ptr<Binder> sender; |
||||
int called = 0; |
||||
{ |
||||
std::unique_ptr<TransactionReceiver> tx_receiver; |
||||
std::tie(sender, tx_receiver) = NewBinderPair( |
||||
[&](transaction_code_t tx_code, ReadableParcel* parcel, int /*uid*/) { |
||||
EXPECT_EQ(tx_code, kTxCode); |
||||
int value; |
||||
EXPECT_TRUE(parcel->ReadInt32(&value).ok()); |
||||
EXPECT_EQ(value, kValue + called); |
||||
called++; |
||||
return absl::OkStatus(); |
||||
}); |
||||
EXPECT_TRUE(sender->PrepareTransaction().ok()); |
||||
WritableParcel* parcel = sender->GetWritableParcel(); |
||||
EXPECT_TRUE(parcel->WriteInt32(kValue).ok()); |
||||
EXPECT_TRUE(sender->Transact(BinderTransportTxCode(kTxCode)).ok()); |
||||
} |
||||
// tx_receiver gets destructed here. This additional transaction should
|
||||
// *still* be received.
|
||||
EXPECT_TRUE(sender->PrepareTransaction().ok()); |
||||
WritableParcel* parcel = sender->GetWritableParcel(); |
||||
EXPECT_TRUE(parcel->WriteInt32(kValue + 1).ok()); |
||||
EXPECT_TRUE(sender->Transact(BinderTransportTxCode(kTxCode)).ok()); |
||||
|
||||
g_transaction_processor->Terminate(); |
||||
EXPECT_EQ(called, 2); |
||||
} |
||||
|
||||
namespace { |
||||
|
||||
struct ThreadArgument { |
||||
int tid; |
||||
std::vector<std::vector<std::pair<std::unique_ptr<Binder>, |
||||
std::unique_ptr<TransactionReceiver>>>>* |
||||
global_binder_pairs; |
||||
std::vector<std::vector<int>>* global_cnts; |
||||
int tx_code; |
||||
int num_pairs_per_thread; |
||||
int num_transactions_per_pair; |
||||
grpc_core::Mutex* mu; |
||||
}; |
||||
|
||||
} // namespace
|
||||
|
||||
// Verify that this system works correctly in a concurrent environment.
|
||||
//
|
||||
// In end-to-end tests, there will be at least two threads, one from client to
|
||||
// server and vice versa. Thus, it's important for us to make sure that the
|
||||
// simulation is correct in such setup.
|
||||
TEST_P(FakeBinderTest, StressTest) { |
||||
constexpr int kTxCode = 0x4321; |
||||
constexpr int kNumThreads = 16; |
||||
constexpr int kNumPairsPerThread = 128; |
||||
constexpr int kNumTransactionsPerPair = 128; |
||||
std::vector<ThreadArgument> args(kNumThreads); |
||||
|
||||
grpc_core::Mutex mu; |
||||
std::vector<std::vector< |
||||
std::pair<std::unique_ptr<Binder>, std::unique_ptr<TransactionReceiver>>>> |
||||
global_binder_pairs(kNumThreads); |
||||
std::vector<std::vector<int>> global_cnts( |
||||
kNumThreads, std::vector<int>(kNumPairsPerThread, 0)); |
||||
|
||||
auto th_function = [](void* arg) { |
||||
ThreadArgument* th_arg = static_cast<ThreadArgument*>(arg); |
||||
int tid = th_arg->tid; |
||||
std::vector<std::pair<std::unique_ptr<Binder>, |
||||
std::unique_ptr<TransactionReceiver>>> |
||||
binder_pairs; |
||||
for (int p = 0; p < th_arg->num_pairs_per_thread; ++p) { |
||||
std::unique_ptr<Binder> binder; |
||||
std::unique_ptr<TransactionReceiver> tx_receiver; |
||||
int expected_tx_code = th_arg->tx_code; |
||||
std::vector<std::vector<int>>* cnt = th_arg->global_cnts; |
||||
std::tie(binder, tx_receiver) = |
||||
NewBinderPair([tid, p, cnt, expected_tx_code]( |
||||
transaction_code_t tx_code, ReadableParcel* parcel, |
||||
int /*uid*/) mutable { |
||||
EXPECT_EQ(tx_code, expected_tx_code); |
||||
int value; |
||||
EXPECT_TRUE(parcel->ReadInt32(&value).ok()); |
||||
EXPECT_EQ(tid, value); |
||||
EXPECT_TRUE(parcel->ReadInt32(&value).ok()); |
||||
EXPECT_EQ(p, value); |
||||
EXPECT_TRUE(parcel->ReadInt32(&value).ok()); |
||||
EXPECT_EQ((*cnt)[tid][p], value); |
||||
(*cnt)[tid][p]++; |
||||
return absl::OkStatus(); |
||||
}); |
||||
binder_pairs.emplace_back(std::move(binder), std::move(tx_receiver)); |
||||
} |
||||
std::vector<int> order; |
||||
for (int i = 0; i < th_arg->num_pairs_per_thread; ++i) { |
||||
for (int j = 0; j < th_arg->num_transactions_per_pair; ++j) { |
||||
order.emplace_back(i); |
||||
} |
||||
} |
||||
std::mt19937 rng(tid); |
||||
std::shuffle(order.begin(), order.end(), rng); |
||||
std::vector<int> tx_cnt(th_arg->num_pairs_per_thread); |
||||
for (int p : order) { |
||||
EXPECT_TRUE(binder_pairs[p].first->PrepareTransaction().ok()); |
||||
WritableParcel* parcel = binder_pairs[p].first->GetWritableParcel(); |
||||
EXPECT_TRUE(parcel->WriteInt32(th_arg->tid).ok()); |
||||
EXPECT_TRUE(parcel->WriteInt32(p).ok()); |
||||
EXPECT_TRUE(parcel->WriteInt32(tx_cnt[p]++).ok()); |
||||
EXPECT_TRUE(binder_pairs[p] |
||||
.first->Transact(BinderTransportTxCode(th_arg->tx_code)) |
||||
.ok()); |
||||
} |
||||
th_arg->mu->Lock(); |
||||
(*th_arg->global_binder_pairs)[tid] = std::move(binder_pairs); |
||||
th_arg->mu->Unlock(); |
||||
}; |
||||
|
||||
std::vector<grpc_core::Thread> thrs(kNumThreads); |
||||
std::vector<std::string> thr_names(kNumThreads); |
||||
for (int i = 0; i < kNumThreads; ++i) { |
||||
args[i].tid = i; |
||||
args[i].global_binder_pairs = &global_binder_pairs; |
||||
args[i].global_cnts = &global_cnts; |
||||
args[i].tx_code = kTxCode; |
||||
args[i].num_pairs_per_thread = kNumPairsPerThread; |
||||
args[i].num_transactions_per_pair = kNumTransactionsPerPair; |
||||
args[i].mu = μ |
||||
thr_names[i] = absl::StrFormat("thread-%d", i); |
||||
thrs[i] = grpc_core::Thread(thr_names[i].c_str(), th_function, &args[i]); |
||||
} |
||||
for (auto& th : thrs) th.Start(); |
||||
for (auto& th : thrs) th.Join(); |
||||
g_transaction_processor->Terminate(); |
||||
} |
||||
|
||||
INSTANTIATE_TEST_SUITE_P(FakeBinderTestWithDifferentDelayTimes, FakeBinderTest, |
||||
testing::Values(absl::ZeroDuration(), |
||||
absl::Nanoseconds(10), |
||||
absl::Microseconds(10))); |
||||
|
||||
} // namespace end2end_testing
|
||||
} // namespace grpc_binder
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
grpc::testing::TestEnvironment env(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
@ -1,101 +0,0 @@ |
||||
# Copyright 2021 gRPC authors. |
||||
# |
||||
# 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. |
||||
|
||||
load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_package", "grpc_proto_library") |
||||
load("//test/core/test_util:grpc_fuzzer.bzl", "grpc_proto_fuzzer") |
||||
|
||||
grpc_package( |
||||
name = "test/core/transport/binder/end2end/fuzzers", |
||||
features = [ |
||||
"layering_check", |
||||
], |
||||
) |
||||
|
||||
licenses(["notice"]) |
||||
|
||||
# Protobuf messages for generating inputs. We manually define proto |
||||
# library rule here so the same proto file can be shared between multiple |
||||
# grpc_proto_fuzzer targets |
||||
grpc_proto_library( |
||||
name = "binder_transport_fuzzer_proto", |
||||
srcs = ["binder_transport_fuzzer.proto"], |
||||
) |
||||
|
||||
grpc_cc_library( |
||||
name = "fuzzer_utils", |
||||
srcs = ["fuzzer_utils.cc"], |
||||
external_deps = [ |
||||
"absl/log:check", |
||||
"absl/log:log", |
||||
], |
||||
language = "c++", |
||||
public_hdrs = ["fuzzer_utils.h"], |
||||
deps = [ |
||||
"binder_transport_fuzzer_proto", |
||||
"//:gpr", |
||||
"//:grpc++", |
||||
"//:grpc++_base", |
||||
"//:grpc_base", |
||||
"//test/core/test_util:grpc_test_util", |
||||
], |
||||
) |
||||
|
||||
grpc_proto_fuzzer( |
||||
name = "binder_transport_client_fuzzer", |
||||
srcs = [ |
||||
"client_fuzzer.cc", |
||||
], |
||||
corpus = "binder_transport_client_fuzzer_corpus", |
||||
external_deps = ["absl/log:check"], |
||||
owner = "binder", |
||||
proto = "client.proto", |
||||
tags = [ |
||||
"no_mac", |
||||
"no_windows", |
||||
], |
||||
deps = [ |
||||
"binder_transport_fuzzer_proto", |
||||
":fuzzer_utils", |
||||
"//:gpr", |
||||
"//:grpc++", |
||||
"//:grpc++_base", |
||||
"//:grpc_base", |
||||
"//test/core/test_util:grpc_test_util", |
||||
], |
||||
) |
||||
|
||||
grpc_proto_fuzzer( |
||||
name = "binder_transport_server_fuzzer", |
||||
srcs = [ |
||||
"server_fuzzer.cc", |
||||
], |
||||
corpus = "binder_transport_server_fuzzer_corpus", |
||||
external_deps = ["absl/log:check"], |
||||
owner = "binder", |
||||
proto = "server.proto", |
||||
tags = [ |
||||
"no_mac", |
||||
"no_windows", |
||||
], |
||||
deps = [ |
||||
"binder_transport_fuzzer_proto", |
||||
":fuzzer_utils", |
||||
"//:gpr", |
||||
"//:grpc++", |
||||
"//:grpc++_base", |
||||
"//:grpc_base", |
||||
"//src/core:slice", |
||||
"//test/core/test_util:grpc_test_util", |
||||
], |
||||
) |
@ -1,81 +0,0 @@ |
||||
// Copyright 2021 gRPC authors. |
||||
// |
||||
// 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 binder_transport_fuzzer; |
||||
|
||||
message Binder {} |
||||
|
||||
message Value { |
||||
oneof data_type { |
||||
int32 i32 = 1; |
||||
int64 i64 = 2; |
||||
bytes byte_array = 3; |
||||
// Strings in Parcel could also contain non UTF-8 data so we use bytes |
||||
// to represent string here |
||||
bytes str = 4; |
||||
Binder binder = 5; |
||||
} |
||||
} |
||||
|
||||
message Parcel { |
||||
repeated Value values = 1; |
||||
|
||||
// Simulates the return value of AParcel_getDataSize |
||||
// (The value generated by protobuf libprotobuf-mutator might not always make sense |
||||
// but the transport implementation should handle that) |
||||
int32 data_size = 2; |
||||
} |
||||
|
||||
enum TransactionCode { |
||||
INVALID = 0; |
||||
SETUP_TRANSPORT = 1; |
||||
SHUTDOWN_TRANSPORT = 2; |
||||
ACKNOWLEDGE_BYTES = 3; |
||||
PING = 4; |
||||
PING_RESPONSE = 5; |
||||
} |
||||
|
||||
message Transaction { |
||||
TransactionCode code = 1; |
||||
int32 uid = 2; |
||||
Parcel parcel = 3; |
||||
} |
||||
|
||||
// Special parcel that used for setting up transport. |
||||
// TODO(mingcl): Consider also fuzzing the setup transport code path |
||||
message SetupTransportParcel { |
||||
int32 version = 1; |
||||
|
||||
// Simulates the return value of AParcel_getDataSize |
||||
// (The value generated by protobuf libprotobuf-mutator might not always make sense |
||||
// but the transport implementation should handle that) |
||||
int32 data_size = 2; |
||||
} |
||||
|
||||
message SetupTransportTransaction { |
||||
int32 uid = 1; |
||||
SetupTransportParcel parcel = 2; |
||||
} |
||||
|
||||
message IncomingParcels { |
||||
SetupTransportTransaction setup_transport_transaction = 1; |
||||
repeated Transaction transactions = 2; |
||||
} |
||||
|
||||
message Input { |
||||
IncomingParcels incoming_parcels = 1; |
||||
} |
||||
|
@ -1,17 +0,0 @@ |
||||
// Copyright 2021 gRPC authors. |
||||
// |
||||
// 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 binder_transport_fuzzer; |
@ -1,158 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 <grpc/grpc.h> |
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
|
||||
#include <thread> |
||||
#include <utility> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/memory/memory.h" |
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
#include "src/core/lib/config/core_configuration.h" |
||||
#include "src/core/lib/iomgr/executor.h" |
||||
#include "src/core/lib/surface/channel.h" |
||||
#include "src/core/lib/surface/channel_create.h" |
||||
#include "src/libfuzzer/libfuzzer_macro.h" |
||||
#include "test/core/test_util/test_config.h" |
||||
#include "test/core/transport/binder/end2end/fuzzers/binder_transport_fuzzer.pb.h" |
||||
#include "test/core/transport/binder/end2end/fuzzers/fuzzer_utils.h" |
||||
|
||||
bool squelch = true; |
||||
bool leak_check = true; |
||||
|
||||
static void* tag(intptr_t t) { return reinterpret_cast<void*>(t); } |
||||
|
||||
DEFINE_PROTO_FUZZER(const binder_transport_fuzzer::Input& input) { |
||||
if (squelch) { |
||||
grpc_disable_all_absl_logs(); |
||||
} |
||||
grpc_init(); |
||||
{ |
||||
// Copied and modified from grpc/test/core/end2end/fuzzers/client_fuzzer.cc
|
||||
grpc_core::ExecCtx exec_ctx; |
||||
grpc_core::Executor::SetThreadingAll(false); |
||||
|
||||
grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr); |
||||
grpc_core::Transport* client_transport = |
||||
grpc_create_binder_transport_client( |
||||
std::make_unique<grpc_binder::fuzzing::BinderForFuzzing>( |
||||
input.incoming_parcels()), |
||||
std::make_shared< |
||||
grpc::experimental::binder::UntrustedSecurityPolicy>()); |
||||
grpc_arg authority_arg = grpc_channel_arg_string_create( |
||||
const_cast<char*>(GRPC_ARG_DEFAULT_AUTHORITY), |
||||
const_cast<char*>("test-authority")); |
||||
grpc_channel_args* args = |
||||
grpc_channel_args_copy_and_add(nullptr, &authority_arg, 1); |
||||
auto channel_args = grpc_core::CoreConfiguration::Get() |
||||
.channel_args_preconditioning() |
||||
.PreconditionChannelArgs(args); |
||||
auto channel = |
||||
grpc_core::ChannelCreate("test-target", channel_args, |
||||
GRPC_CLIENT_DIRECT_CHANNEL, client_transport) |
||||
->release() |
||||
->c_ptr(); |
||||
grpc_channel_args_destroy(args); |
||||
grpc_slice host = grpc_slice_from_static_string("localhost"); |
||||
grpc_call* call = grpc_channel_create_call( |
||||
channel, nullptr, 0, cq, grpc_slice_from_static_string("/foo"), &host, |
||||
gpr_inf_future(GPR_CLOCK_REALTIME), nullptr); |
||||
grpc_metadata_array initial_metadata_recv; |
||||
grpc_metadata_array_init(&initial_metadata_recv); |
||||
grpc_byte_buffer* response_payload_recv = nullptr; |
||||
grpc_metadata_array trailing_metadata_recv; |
||||
grpc_metadata_array_init(&trailing_metadata_recv); |
||||
grpc_status_code status; |
||||
grpc_slice details = grpc_empty_slice(); |
||||
|
||||
grpc_op ops[6]; |
||||
memset(ops, 0, sizeof(ops)); |
||||
grpc_op* op = ops; |
||||
op->op = GRPC_OP_SEND_INITIAL_METADATA; |
||||
op->data.send_initial_metadata.count = 0; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
op->op = GRPC_OP_RECV_INITIAL_METADATA; |
||||
op->data.recv_initial_metadata.recv_initial_metadata = |
||||
&initial_metadata_recv; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
op->op = GRPC_OP_RECV_MESSAGE; |
||||
op->data.recv_message.recv_message = &response_payload_recv; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; |
||||
op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; |
||||
op->data.recv_status_on_client.status = &status; |
||||
op->data.recv_status_on_client.status_details = &details; |
||||
op->flags = 0; |
||||
op->reserved = nullptr; |
||||
op++; |
||||
grpc_call_error error = grpc_call_start_batch( |
||||
call, ops, static_cast<size_t>(op - ops), tag(1), nullptr); |
||||
int requested_calls = 1; |
||||
CHECK_EQ(error, GRPC_CALL_OK); |
||||
grpc_event ev; |
||||
while (true) { |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
ev = grpc_completion_queue_next(cq, gpr_inf_past(GPR_CLOCK_REALTIME), |
||||
nullptr); |
||||
switch (ev.type) { |
||||
case GRPC_QUEUE_TIMEOUT: |
||||
goto done; |
||||
case GRPC_QUEUE_SHUTDOWN: |
||||
break; |
||||
case GRPC_OP_COMPLETE: |
||||
requested_calls--; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
done: |
||||
if (requested_calls) { |
||||
grpc_call_cancel(call, nullptr); |
||||
} |
||||
grpc_binder::fuzzing::JoinFuzzingThread(); |
||||
for (int i = 0; i < requested_calls; i++) { |
||||
ev = grpc_completion_queue_next(cq, gpr_inf_past(GPR_CLOCK_REALTIME), |
||||
nullptr); |
||||
CHECK(ev.type == GRPC_OP_COMPLETE); |
||||
} |
||||
grpc_completion_queue_shutdown(cq); |
||||
for (int i = 0; i < requested_calls; i++) { |
||||
ev = grpc_completion_queue_next(cq, gpr_inf_past(GPR_CLOCK_REALTIME), |
||||
nullptr); |
||||
CHECK(ev.type == GRPC_QUEUE_SHUTDOWN); |
||||
} |
||||
grpc_call_unref(call); |
||||
grpc_completion_queue_destroy(cq); |
||||
grpc_metadata_array_destroy(&initial_metadata_recv); |
||||
grpc_metadata_array_destroy(&trailing_metadata_recv); |
||||
grpc_slice_unref(details); |
||||
grpc_channel_destroy(channel); |
||||
if (response_payload_recv != nullptr) { |
||||
grpc_byte_buffer_destroy(response_payload_recv); |
||||
} |
||||
} |
||||
grpc_shutdown(); |
||||
} |
@ -1,157 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "test/core/transport/binder/end2end/fuzzers/fuzzer_utils.h" |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
|
||||
namespace grpc_binder { |
||||
namespace fuzzing { |
||||
|
||||
namespace { |
||||
|
||||
std::thread* g_fuzzing_thread = nullptr; |
||||
|
||||
template <typename... Args> |
||||
void CreateFuzzingThread(Args&&... args) { |
||||
CHECK_EQ(g_fuzzing_thread, nullptr); |
||||
g_fuzzing_thread = new std::thread(std::forward<Args>(args)...); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
void JoinFuzzingThread() { |
||||
if (g_fuzzing_thread) { |
||||
g_fuzzing_thread->join(); |
||||
delete g_fuzzing_thread; |
||||
g_fuzzing_thread = nullptr; |
||||
} |
||||
} |
||||
|
||||
int32_t ReadableParcelForFuzzing::GetDataSize() const { |
||||
return parcel_data_size_; |
||||
} |
||||
|
||||
absl::Status ReadableParcelForFuzzing::ReadInt32(int32_t* data) { |
||||
if (consumed_data_size_ >= kParcelDataSizeLimit) { |
||||
return absl::InternalError("Parcel size limit exceeds"); |
||||
} |
||||
if (values_.empty() || !values_.front().has_i32()) { |
||||
return absl::InternalError("error"); |
||||
} |
||||
*data = values_.front().i32(); |
||||
values_.pop(); |
||||
consumed_data_size_ += sizeof(int32_t); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status ReadableParcelForFuzzing::ReadInt64(int64_t* data) { |
||||
if (consumed_data_size_ >= kParcelDataSizeLimit) { |
||||
return absl::InternalError("Parcel size limit exceeds"); |
||||
} |
||||
if (values_.empty() || !values_.front().has_i64()) { |
||||
return absl::InternalError("error"); |
||||
} |
||||
*data = values_.front().i64(); |
||||
values_.pop(); |
||||
consumed_data_size_ += sizeof(int64_t); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status ReadableParcelForFuzzing::ReadBinder( |
||||
std::unique_ptr<Binder>* binder) { |
||||
if (consumed_data_size_ >= kParcelDataSizeLimit) { |
||||
return absl::InternalError("Parcel size limit exceeds"); |
||||
} |
||||
if (values_.empty() || !values_.front().has_binder()) { |
||||
return absl::InternalError("error"); |
||||
} |
||||
*binder = std::make_unique<BinderForFuzzing>(); |
||||
values_.pop(); |
||||
consumed_data_size_ += sizeof(void*); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status ReadableParcelForFuzzing::ReadByteArray(std::string* data) { |
||||
if (consumed_data_size_ >= kParcelDataSizeLimit) { |
||||
return absl::InternalError("Parcel size limit exceeds"); |
||||
} |
||||
if (values_.empty() || !values_.front().has_byte_array()) { |
||||
return absl::InternalError("error"); |
||||
} |
||||
*data = values_.front().byte_array(); |
||||
values_.pop(); |
||||
consumed_data_size_ += data->size(); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status ReadableParcelForFuzzing::ReadString(std::string* data) { |
||||
if (consumed_data_size_ >= kParcelDataSizeLimit) { |
||||
return absl::InternalError("Parcel size limit exceeds"); |
||||
} |
||||
if (values_.empty() || !values_.front().has_str()) { |
||||
return absl::InternalError("error"); |
||||
} |
||||
*data = values_.front().str(); |
||||
values_.pop(); |
||||
consumed_data_size_ += data->size(); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
void FuzzingLoop( |
||||
binder_transport_fuzzer::IncomingParcels incoming_parcels, |
||||
grpc_core::RefCountedPtr<grpc_binder::WireReader> wire_reader_ref, |
||||
grpc_binder::TransactionReceiver::OnTransactCb callback) { |
||||
{ |
||||
// Send SETUP_TRANSPORT request.
|
||||
std::unique_ptr<grpc_binder::ReadableParcel> parcel = |
||||
std::make_unique<ReadableParcelForFuzzing>( |
||||
incoming_parcels.setup_transport_transaction().parcel()); |
||||
callback(static_cast<transaction_code_t>( |
||||
grpc_binder::BinderTransportTxCode::SETUP_TRANSPORT), |
||||
parcel.get(), |
||||
/*uid=*/incoming_parcels.setup_transport_transaction().uid()) |
||||
.IgnoreError(); |
||||
} |
||||
for (const auto& tx_iter : incoming_parcels.transactions()) { |
||||
transaction_code_t tx_code = tx_iter.code(); |
||||
std::unique_ptr<grpc_binder::ReadableParcel> parcel = |
||||
std::make_unique<ReadableParcelForFuzzing>(tx_iter.parcel()); |
||||
callback(tx_code, parcel.get(), |
||||
/*uid=*/tx_iter.uid()) |
||||
.IgnoreError(); |
||||
} |
||||
wire_reader_ref = nullptr; |
||||
} |
||||
|
||||
TransactionReceiverForFuzzing::TransactionReceiverForFuzzing( |
||||
binder_transport_fuzzer::IncomingParcels incoming_parcels, |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb cb) { |
||||
LOG(INFO) << "Construct TransactionReceiverForFuzzing"; |
||||
CreateFuzzingThread(FuzzingLoop, std::move(incoming_parcels), |
||||
std::move(wire_reader_ref), std::move(cb)); |
||||
} |
||||
|
||||
std::unique_ptr<TransactionReceiver> BinderForFuzzing::ConstructTxReceiver( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb cb) const { |
||||
auto tx_receiver = std::make_unique<TransactionReceiverForFuzzing>( |
||||
incoming_parcels_, wire_reader_ref, cb); |
||||
return tx_receiver; |
||||
} |
||||
|
||||
} // namespace fuzzing
|
||||
} // namespace grpc_binder
|
@ -1,152 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_TEST_CORE_TRANSPORT_BINDER_END2END_FUZZERS_FUZZER_UTILS_H |
||||
#define GRPC_TEST_CORE_TRANSPORT_BINDER_END2END_FUZZERS_FUZZER_UTILS_H |
||||
|
||||
#include <memory> |
||||
#include <queue> |
||||
#include <string> |
||||
#include <thread> |
||||
#include <vector> |
||||
|
||||
#include "absl/memory/memory.h" |
||||
#include "absl/status/status.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_reader.h" |
||||
#include "src/core/util/crash.h" |
||||
#include "test/core/transport/binder/end2end/fuzzers/binder_transport_fuzzer.pb.h" |
||||
|
||||
namespace grpc_binder { |
||||
namespace fuzzing { |
||||
|
||||
// A WritableParcel implementation that simply does nothing. Don't use
|
||||
// MockWritableParcel here since capturing calls is expensive.
|
||||
class NoOpWritableParcel : public WritableParcel { |
||||
public: |
||||
int32_t GetDataSize() const override { return 0; } |
||||
absl::Status WriteInt32(int32_t /*data*/) override { |
||||
return absl::OkStatus(); |
||||
} |
||||
absl::Status WriteInt64(int64_t /*data*/) override { |
||||
return absl::OkStatus(); |
||||
} |
||||
absl::Status WriteBinder(HasRawBinder* /*binder*/) override { |
||||
return absl::OkStatus(); |
||||
} |
||||
absl::Status WriteString(absl::string_view /*s*/) override { |
||||
return absl::OkStatus(); |
||||
} |
||||
absl::Status WriteByteArray(const int8_t* /*buffer*/, |
||||
int32_t /*length*/) override { |
||||
return absl::OkStatus(); |
||||
} |
||||
}; |
||||
|
||||
// Binder implementation used in fuzzing.
|
||||
//
|
||||
// Most of its the functionalities are no-op, except ConstructTxReceiver now
|
||||
// returns a TransactionReceiverForFuzzing.
|
||||
class BinderForFuzzing : public Binder { |
||||
public: |
||||
BinderForFuzzing() : input_(std::make_unique<NoOpWritableParcel>()) {} |
||||
|
||||
explicit BinderForFuzzing(const binder_transport_fuzzer::IncomingParcels& p) |
||||
: incoming_parcels_(p), input_(std::make_unique<NoOpWritableParcel>()) {} |
||||
|
||||
void Initialize() override {} |
||||
absl::Status PrepareTransaction() override { return absl::OkStatus(); } |
||||
|
||||
absl::Status Transact(BinderTransportTxCode /*tx_code*/) override { |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
std::unique_ptr<TransactionReceiver> ConstructTxReceiver( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb cb) const override; |
||||
|
||||
WritableParcel* GetWritableParcel() const override { return input_.get(); } |
||||
void* GetRawBinder() override { return nullptr; } |
||||
|
||||
private: |
||||
binder_transport_fuzzer::IncomingParcels incoming_parcels_; |
||||
std::unique_ptr<WritableParcel> input_; |
||||
}; |
||||
|
||||
// ReadableParcel implementation used in fuzzing.
|
||||
//
|
||||
// It consumes a Parcel generated by mutator, and returns the data in the Parcel
|
||||
// upon user's requests.
|
||||
class ReadableParcelForFuzzing : public ReadableParcel { |
||||
public: |
||||
explicit ReadableParcelForFuzzing(const binder_transport_fuzzer::Parcel& p) |
||||
: parcel_data_size_(p.data_size()), consumed_data_size_(0) { |
||||
for (const auto& v : p.values()) { |
||||
values_.push(v); |
||||
} |
||||
} |
||||
|
||||
// Construct from SetupTransportParcel, which have fixed types of data in it.
|
||||
explicit ReadableParcelForFuzzing( |
||||
const binder_transport_fuzzer::SetupTransportParcel& p) |
||||
: parcel_data_size_(p.data_size()), consumed_data_size_(0) { |
||||
// Creates value for protocol version and put it into the queue
|
||||
binder_transport_fuzzer::Value version_value; |
||||
version_value.set_i32(p.version()); |
||||
values_.push(version_value); |
||||
|
||||
// Creates a binder value and put it into the queue
|
||||
binder_transport_fuzzer::Value binder_value; |
||||
binder_value.mutable_binder(); // sets one-of field
|
||||
values_.push(binder_value); |
||||
} |
||||
|
||||
int32_t GetDataSize() const override; |
||||
absl::Status ReadInt32(int32_t* data) override; |
||||
absl::Status ReadInt64(int64_t* data) override; |
||||
absl::Status ReadBinder(std::unique_ptr<Binder>* binder) override; |
||||
absl::Status ReadByteArray(std::string* data) override; |
||||
absl::Status ReadString(std::string* data) override; |
||||
|
||||
private: |
||||
// Stores data/objects in binder in their order. Since we don't support random
|
||||
// access using a std::queue is enough here.
|
||||
std::queue<binder_transport_fuzzer::Value> values_; |
||||
|
||||
const int32_t parcel_data_size_; |
||||
|
||||
static constexpr size_t kParcelDataSizeLimit = 1024 * 1024; |
||||
size_t consumed_data_size_; |
||||
}; |
||||
|
||||
void JoinFuzzingThread(); |
||||
|
||||
// TransactionReceiver implementation used in fuzzing.
|
||||
//
|
||||
// When constructed, start sending fuzzed requests to the client. When all the
|
||||
// bytes are consumed, the reference to WireReader will be released.
|
||||
class TransactionReceiverForFuzzing : public TransactionReceiver { |
||||
public: |
||||
TransactionReceiverForFuzzing( |
||||
binder_transport_fuzzer::IncomingParcels incoming_parcels, |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb cb); |
||||
|
||||
void* GetRawBinder() override { return nullptr; } |
||||
}; |
||||
|
||||
} // namespace fuzzing
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_TEST_CORE_TRANSPORT_BINDER_END2END_FUZZERS_FUZZER_UTILS_H
|
@ -1,17 +0,0 @@ |
||||
// Copyright 2021 gRPC authors. |
||||
// |
||||
// 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 binder_transport_fuzzer; |
@ -1,132 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 <grpc/grpc.h> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
#include "src/core/lib/config/core_configuration.h" |
||||
#include "src/core/lib/iomgr/executor.h" |
||||
#include "src/core/lib/slice/slice_internal.h" |
||||
#include "src/core/server/server.h" |
||||
#include "src/libfuzzer/libfuzzer_macro.h" |
||||
#include "test/core/test_util/test_config.h" |
||||
#include "test/core/transport/binder/end2end/fuzzers/binder_transport_fuzzer.pb.h" |
||||
#include "test/core/transport/binder/end2end/fuzzers/fuzzer_utils.h" |
||||
|
||||
bool squelch = true; |
||||
bool leak_check = true; |
||||
|
||||
static void* tag(intptr_t t) { return reinterpret_cast<void*>(t); } |
||||
|
||||
DEFINE_PROTO_FUZZER(const binder_transport_fuzzer::Input& input) { |
||||
if (squelch) { |
||||
grpc_disable_all_absl_logs(); |
||||
} |
||||
grpc_init(); |
||||
{ |
||||
// Copied and modified from grpc/test/core/end2end/fuzzers/server_fuzzer.cc
|
||||
grpc_core::ExecCtx exec_ctx; |
||||
grpc_core::Executor::SetThreadingAll(false); |
||||
|
||||
grpc_server* server = grpc_server_create(nullptr, nullptr); |
||||
grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr); |
||||
grpc_server_register_completion_queue(server, cq, nullptr); |
||||
// TODO(ctiller): add more registered methods (one for POST, one for PUT)
|
||||
grpc_server_register_method(server, "/reg", nullptr, {}, 0); |
||||
grpc_server_start(server); |
||||
grpc_core::Transport* server_transport = |
||||
grpc_create_binder_transport_server( |
||||
std::make_unique<grpc_binder::fuzzing::BinderForFuzzing>( |
||||
input.incoming_parcels()), |
||||
std::make_shared< |
||||
grpc::experimental::binder::UntrustedSecurityPolicy>()); |
||||
grpc_core::ChannelArgs channel_args = grpc_core::CoreConfiguration::Get() |
||||
.channel_args_preconditioning() |
||||
.PreconditionChannelArgs(nullptr); |
||||
(void)grpc_core::Server::FromC(server)->SetupTransport( |
||||
server_transport, nullptr, channel_args, nullptr); |
||||
grpc_call* call1 = nullptr; |
||||
grpc_call_details call_details1; |
||||
grpc_metadata_array request_metadata1; |
||||
grpc_call_details_init(&call_details1); |
||||
grpc_metadata_array_init(&request_metadata1); |
||||
int requested_calls = 0; |
||||
|
||||
CHECK(GRPC_CALL_OK == |
||||
grpc_server_request_call(server, &call1, &call_details1, |
||||
&request_metadata1, cq, cq, tag(1))); |
||||
requested_calls++; |
||||
|
||||
grpc_event ev; |
||||
while (true) { |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
ev = grpc_completion_queue_next(cq, gpr_inf_past(GPR_CLOCK_REALTIME), |
||||
nullptr); |
||||
switch (ev.type) { |
||||
case GRPC_QUEUE_TIMEOUT: |
||||
goto done; |
||||
case GRPC_QUEUE_SHUTDOWN: |
||||
break; |
||||
case GRPC_OP_COMPLETE: |
||||
if (ev.tag == tag(1)) { |
||||
requested_calls--; |
||||
// TODO(ctiller): keep reading that call!
|
||||
} |
||||
break; |
||||
} |
||||
} |
||||
|
||||
done: |
||||
grpc_binder::fuzzing::JoinFuzzingThread(); |
||||
if (call1 != nullptr) grpc_call_unref(call1); |
||||
grpc_call_details_destroy(&call_details1); |
||||
grpc_metadata_array_destroy(&request_metadata1); |
||||
grpc_server_shutdown_and_notify(server, cq, tag(0xdead)); |
||||
grpc_server_cancel_all_calls(server); |
||||
grpc_core::Timestamp deadline = |
||||
grpc_core::Timestamp::Now() + grpc_core::Duration::Seconds(5); |
||||
for (int i = 0; i <= requested_calls; i++) { |
||||
// A single grpc_completion_queue_next might not be sufficient for getting
|
||||
// the tag from shutdown, because we might potentially get blocked by
|
||||
// an operation happening on the timer thread.
|
||||
// For example, the deadline timer might expire, leading to the timer
|
||||
// thread trying to cancel the RPC and thereby acquiring a few references
|
||||
// to the call. This will prevent the shutdown to complete till the timer
|
||||
// thread releases those references.
|
||||
// As a solution, we are going to keep performing a cq_next for a
|
||||
// liberal period of 5 seconds for the timer thread to complete its work.
|
||||
do { |
||||
ev = grpc_completion_queue_next(cq, gpr_inf_past(GPR_CLOCK_REALTIME), |
||||
nullptr); |
||||
grpc_core::ExecCtx::Get()->InvalidateNow(); |
||||
} while (ev.type != GRPC_OP_COMPLETE && |
||||
grpc_core::Timestamp::Now() < deadline); |
||||
CHECK(ev.type == GRPC_OP_COMPLETE); |
||||
} |
||||
grpc_completion_queue_shutdown(cq); |
||||
for (int i = 0; i <= requested_calls; i++) { |
||||
do { |
||||
ev = grpc_completion_queue_next(cq, gpr_inf_past(GPR_CLOCK_REALTIME), |
||||
nullptr); |
||||
grpc_core::ExecCtx::Get()->InvalidateNow(); |
||||
} while (ev.type != GRPC_QUEUE_SHUTDOWN && |
||||
grpc_core::Timestamp::Now() < deadline); |
||||
CHECK(ev.type == GRPC_QUEUE_SHUTDOWN); |
||||
} |
||||
grpc_server_destroy(server); |
||||
grpc_completion_queue_destroy(cq); |
||||
} |
||||
grpc_shutdown(); |
||||
} |
@ -1,133 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "test/core/transport/binder/end2end/testing_channel_create.h" |
||||
|
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
|
||||
#include <utility> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_reader_impl.h" |
||||
#include "src/core/lib/channel/channel_args.h" |
||||
#include "src/core/lib/config/core_configuration.h" |
||||
#include "src/core/lib/surface/channel.h" |
||||
#include "src/core/lib/surface/channel_create.h" |
||||
#include "src/core/lib/transport/error_utils.h" |
||||
|
||||
namespace grpc_binder { |
||||
namespace end2end_testing { |
||||
|
||||
namespace { |
||||
// Since we assume the first half of the transport setup is completed before the
|
||||
// server side enters WireReader::SetupTransport, we need this helper to wait
|
||||
// and finish that part of the negotiation for us.
|
||||
class ServerSetupTransportHelper { |
||||
public: |
||||
ServerSetupTransportHelper() |
||||
: wire_reader_(std::make_unique<WireReaderImpl>( |
||||
/*transport_stream_receiver=*/nullptr, /*is_client=*/false, |
||||
std::make_shared< |
||||
grpc::experimental::binder::UntrustedSecurityPolicy>())) { |
||||
std::tie(endpoint_binder_, tx_receiver_) = NewBinderPair( |
||||
[this](transaction_code_t tx_code, ReadableParcel* parcel, int uid) { |
||||
return this->wire_reader_->ProcessTransaction(tx_code, parcel, uid); |
||||
}); |
||||
} |
||||
std::unique_ptr<Binder> WaitForClientBinder() { |
||||
return wire_reader_->RecvSetupTransport(); |
||||
} |
||||
|
||||
std::unique_ptr<Binder> GetEndpointBinderForClient() { |
||||
return std::move(endpoint_binder_); |
||||
} |
||||
|
||||
private: |
||||
std::unique_ptr<WireReaderImpl> wire_reader_; |
||||
// The endpoint binder for client.
|
||||
std::unique_ptr<Binder> endpoint_binder_; |
||||
std::unique_ptr<TransactionReceiver> tx_receiver_; |
||||
}; |
||||
} // namespace
|
||||
|
||||
std::pair<grpc_core::Transport*, grpc_core::Transport*> |
||||
CreateClientServerBindersPairForTesting() { |
||||
ServerSetupTransportHelper helper; |
||||
std::unique_ptr<Binder> endpoint_binder = helper.GetEndpointBinderForClient(); |
||||
grpc_core::Transport* client_transport = nullptr; |
||||
|
||||
struct ThreadArgs { |
||||
std::unique_ptr<Binder> endpoint_binder; |
||||
grpc_core::Transport** client_transport; |
||||
} args; |
||||
|
||||
args.endpoint_binder = std::move(endpoint_binder); |
||||
args.client_transport = &client_transport; |
||||
|
||||
grpc_core::Thread client_thread( |
||||
"client-thread", |
||||
[](void* arg) { |
||||
ThreadArgs* args = static_cast<ThreadArgs*>(arg); |
||||
std::unique_ptr<Binder> endpoint_binder = |
||||
std::move(args->endpoint_binder); |
||||
*args->client_transport = grpc_create_binder_transport_client( |
||||
std::move(endpoint_binder), |
||||
std::make_shared< |
||||
grpc::experimental::binder::UntrustedSecurityPolicy>()); |
||||
}, |
||||
&args); |
||||
client_thread.Start(); |
||||
grpc_core::Transport* server_transport = grpc_create_binder_transport_server( |
||||
helper.WaitForClientBinder(), |
||||
std::make_shared<grpc::experimental::binder::UntrustedSecurityPolicy>()); |
||||
client_thread.Join(); |
||||
return std::make_pair(client_transport, server_transport); |
||||
} |
||||
|
||||
std::shared_ptr<grpc::Channel> BinderChannelForTesting( |
||||
grpc::Server* server, const grpc::ChannelArguments& args) { |
||||
grpc_channel_args channel_args = args.c_channel_args(); |
||||
return grpc::CreateChannelInternal( |
||||
"", |
||||
grpc_binder_channel_create_for_testing(server->c_server(), &channel_args, |
||||
nullptr), |
||||
std::vector<std::unique_ptr< |
||||
grpc::experimental::ClientInterceptorFactoryInterface>>()); |
||||
} |
||||
|
||||
} // namespace end2end_testing
|
||||
} // namespace grpc_binder
|
||||
|
||||
grpc_channel* grpc_binder_channel_create_for_testing( |
||||
grpc_server* server, const grpc_channel_args* args, void* /*reserved*/) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
|
||||
auto server_args = grpc_core::CoreConfiguration::Get() |
||||
.channel_args_preconditioning() |
||||
.PreconditionChannelArgs(args); |
||||
auto client_args = |
||||
server_args.Set(GRPC_ARG_DEFAULT_AUTHORITY, "test.authority"); |
||||
|
||||
grpc_core::Transport *client_transport, *server_transport; |
||||
std::tie(client_transport, server_transport) = |
||||
grpc_binder::end2end_testing::CreateClientServerBindersPairForTesting(); |
||||
grpc_error_handle error = grpc_core::Server::FromC(server)->SetupTransport( |
||||
server_transport, nullptr, server_args, nullptr); |
||||
CHECK_OK(error); |
||||
auto channel = grpc_core::ChannelCreate( |
||||
"binder", client_args, GRPC_CLIENT_DIRECT_CHANNEL, client_transport); |
||||
CHECK_OK(channel); |
||||
return channel->release()->c_ptr(); |
||||
} |
@ -1,41 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_TEST_CORE_TRANSPORT_BINDER_END2END_TESTING_CHANNEL_CREATE_H |
||||
#define GRPC_TEST_CORE_TRANSPORT_BINDER_END2END_TESTING_CHANNEL_CREATE_H |
||||
|
||||
#include <grpcpp/grpcpp.h> |
||||
|
||||
#include <utility> |
||||
|
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
#include "src/core/server/server.h" |
||||
#include "test/core/transport/binder/end2end/fake_binder.h" |
||||
|
||||
namespace grpc_binder { |
||||
namespace end2end_testing { |
||||
|
||||
std::pair<grpc_core::Transport*, grpc_core::Transport*> |
||||
CreateClientServerBindersPairForTesting(); |
||||
|
||||
std::shared_ptr<grpc::Channel> BinderChannelForTesting( |
||||
grpc::Server* server, const grpc::ChannelArguments& args); |
||||
|
||||
} // namespace end2end_testing
|
||||
} // namespace grpc_binder
|
||||
|
||||
grpc_channel* grpc_binder_channel_create_for_testing( |
||||
grpc_server* server, const grpc_channel_args* args, void* /*reserved*/); |
||||
|
||||
#endif // GRPC_TEST_CORE_TRANSPORT_BINDER_END2END_TESTING_CHANNEL_CREATE_H
|
@ -1,74 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "src/core/ext/transport/binder/client/endpoint_binder_pool.h" |
||||
|
||||
#include <gmock/gmock.h> |
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <cassert> |
||||
#include <string> |
||||
#include <utility> |
||||
#include <vector> |
||||
|
||||
#include "absl/memory/memory.h" |
||||
#include "test/core/test_util/test_config.h" |
||||
#include "test/core/transport/binder/mock_objects.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
class CallbackChecker { |
||||
public: |
||||
MOCK_METHOD(void, Cb, (std::unique_ptr<grpc_binder::Binder>), ()); |
||||
}; |
||||
|
||||
TEST(EndpointBinderPoolTest, AddBeforeGet) { |
||||
EndpointBinderPool pool; |
||||
auto b = std::make_unique<grpc_binder::MockBinder>(); |
||||
CallbackChecker cc; |
||||
pool.AddEndpointBinder("test", std::move(b)); |
||||
// TODO(mingcl): Use pointer matcher to verify it is `b` being passed back
|
||||
// here. It is only available in newer gtest version
|
||||
EXPECT_CALL(cc, Cb(testing::_)); |
||||
pool.GetEndpointBinder( |
||||
"test", std::bind(&CallbackChecker::Cb, &cc, std::placeholders::_1)); |
||||
} |
||||
|
||||
TEST(EndpointBinderPoolTest, GetBeforeAdd) { |
||||
EndpointBinderPool pool; |
||||
auto b = std::make_unique<grpc_binder::MockBinder>(); |
||||
CallbackChecker cc; |
||||
EXPECT_CALL(cc, Cb(testing::_)).Times(0); |
||||
pool.GetEndpointBinder( |
||||
"test", std::bind(&CallbackChecker::Cb, &cc, std::placeholders::_1)); |
||||
EXPECT_CALL(cc, Cb(testing::_)).Times(1); |
||||
pool.AddEndpointBinder("test", std::move(b)); |
||||
} |
||||
|
||||
TEST(EndpointBinderPoolTest, ExpectNotCalled) { |
||||
EndpointBinderPool pool; |
||||
auto b = std::make_unique<grpc_binder::MockBinder>(); |
||||
CallbackChecker cc; |
||||
EXPECT_CALL(cc, Cb(testing::_)).Times(0); |
||||
pool.GetEndpointBinder( |
||||
"test", std::bind(&CallbackChecker::Cb, &cc, std::placeholders::_1)); |
||||
} |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
grpc::testing::TestEnvironment env(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
@ -1,55 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "test/core/transport/binder/mock_objects.h" |
||||
|
||||
#include <memory> |
||||
|
||||
#include "absl/memory/memory.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
using ::testing::Return; |
||||
|
||||
MockReadableParcel::MockReadableParcel() { |
||||
ON_CALL(*this, ReadBinder).WillByDefault([](std::unique_ptr<Binder>* binder) { |
||||
*binder = std::make_unique<MockBinder>(); |
||||
return absl::OkStatus(); |
||||
}); |
||||
ON_CALL(*this, ReadInt32).WillByDefault(Return(absl::OkStatus())); |
||||
ON_CALL(*this, ReadByteArray).WillByDefault(Return(absl::OkStatus())); |
||||
ON_CALL(*this, ReadString).WillByDefault(Return(absl::OkStatus())); |
||||
} |
||||
|
||||
MockWritableParcel::MockWritableParcel() { |
||||
ON_CALL(*this, WriteInt32).WillByDefault(Return(absl::OkStatus())); |
||||
ON_CALL(*this, WriteBinder).WillByDefault(Return(absl::OkStatus())); |
||||
ON_CALL(*this, WriteString).WillByDefault(Return(absl::OkStatus())); |
||||
ON_CALL(*this, WriteByteArray).WillByDefault(Return(absl::OkStatus())); |
||||
} |
||||
|
||||
MockBinder::MockBinder() { |
||||
ON_CALL(*this, PrepareTransaction).WillByDefault(Return(absl::OkStatus())); |
||||
ON_CALL(*this, Transact).WillByDefault(Return(absl::OkStatus())); |
||||
ON_CALL(*this, GetWritableParcel).WillByDefault(Return(&mock_input_)); |
||||
ON_CALL(*this, ConstructTxReceiver) |
||||
.WillByDefault( |
||||
[this](grpc_core::RefCountedPtr<WireReader> /*wire_reader_ref*/, |
||||
TransactionReceiver::OnTransactCb cb) { |
||||
return std::make_unique<MockTransactionReceiver>( |
||||
cb, BinderTransportTxCode::SETUP_TRANSPORT, &mock_output_); |
||||
}); |
||||
} |
||||
|
||||
} // namespace grpc_binder
|
@ -1,121 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_TEST_CORE_TRANSPORT_BINDER_MOCK_OBJECTS_H |
||||
#define GRPC_TEST_CORE_TRANSPORT_BINDER_MOCK_OBJECTS_H |
||||
|
||||
#include <gmock/gmock.h> |
||||
|
||||
#include "src/core/ext/transport/binder/utils/transport_stream_receiver.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder_constants.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_reader.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_writer.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
class MockWritableParcel : public WritableParcel { |
||||
public: |
||||
MOCK_METHOD(int32_t, GetDataSize, (), (const, override)); |
||||
MOCK_METHOD(absl::Status, WriteInt32, (int32_t), (override)); |
||||
MOCK_METHOD(absl::Status, WriteInt64, (int64_t), (override)); |
||||
MOCK_METHOD(absl::Status, WriteBinder, (HasRawBinder*), (override)); |
||||
MOCK_METHOD(absl::Status, WriteString, (absl::string_view), (override)); |
||||
MOCK_METHOD(absl::Status, WriteByteArray, (const int8_t*, int32_t), |
||||
(override)); |
||||
|
||||
MockWritableParcel(); |
||||
}; |
||||
|
||||
class MockReadableParcel : public ReadableParcel { |
||||
public: |
||||
MOCK_METHOD(int32_t, GetDataSize, (), (const, override)); |
||||
MOCK_METHOD(absl::Status, ReadInt32, (int32_t*), (override)); |
||||
MOCK_METHOD(absl::Status, ReadInt64, (int64_t*), (override)); |
||||
MOCK_METHOD(absl::Status, ReadBinder, (std::unique_ptr<Binder>*), (override)); |
||||
MOCK_METHOD(absl::Status, ReadByteArray, (std::string*), (override)); |
||||
MOCK_METHOD(absl::Status, ReadString, (std::string*), (override)); |
||||
|
||||
MockReadableParcel(); |
||||
}; |
||||
|
||||
class MockBinder : public Binder { |
||||
public: |
||||
MOCK_METHOD(void, Initialize, (), (override)); |
||||
MOCK_METHOD(absl::Status, PrepareTransaction, (), (override)); |
||||
MOCK_METHOD(absl::Status, Transact, (BinderTransportTxCode), (override)); |
||||
MOCK_METHOD(WritableParcel*, GetWritableParcel, (), (const, override)); |
||||
MOCK_METHOD(std::unique_ptr<TransactionReceiver>, ConstructTxReceiver, |
||||
(grpc_core::RefCountedPtr<WireReader>, |
||||
TransactionReceiver::OnTransactCb), |
||||
(const, override)); |
||||
MOCK_METHOD(void*, GetRawBinder, (), (override)); |
||||
|
||||
MockBinder(); |
||||
MockWritableParcel& GetWriter() { return mock_input_; } |
||||
MockReadableParcel& GetReader() { return mock_output_; } |
||||
|
||||
private: |
||||
MockWritableParcel mock_input_; |
||||
MockReadableParcel mock_output_; |
||||
}; |
||||
|
||||
// TODO(waynetu): Implement transaction injection later for more thorough
|
||||
// testing.
|
||||
class MockTransactionReceiver : public TransactionReceiver { |
||||
public: |
||||
explicit MockTransactionReceiver(OnTransactCb transact_cb, |
||||
BinderTransportTxCode code, |
||||
MockReadableParcel* output) { |
||||
if (code == BinderTransportTxCode::SETUP_TRANSPORT) { |
||||
EXPECT_CALL(*output, ReadInt32).WillOnce([](int32_t* version) { |
||||
*version = 1; |
||||
return absl::OkStatus(); |
||||
}); |
||||
} |
||||
transact_cb(static_cast<transaction_code_t>(code), output, /*uid=*/0) |
||||
.IgnoreError(); |
||||
} |
||||
|
||||
MOCK_METHOD(void*, GetRawBinder, (), (override)); |
||||
}; |
||||
|
||||
class MockWireWriter : public WireWriter { |
||||
public: |
||||
MOCK_METHOD(absl::Status, RpcCall, (std::unique_ptr<Transaction>), |
||||
(override)); |
||||
MOCK_METHOD(absl::Status, SendAck, (int64_t), (override)); |
||||
MOCK_METHOD(void, OnAckReceived, (int64_t), (override)); |
||||
}; |
||||
|
||||
class MockTransportStreamReceiver : public TransportStreamReceiver { |
||||
public: |
||||
MOCK_METHOD(void, RegisterRecvInitialMetadata, |
||||
(StreamIdentifier, InitialMetadataCallbackType), (override)); |
||||
MOCK_METHOD(void, RegisterRecvMessage, |
||||
(StreamIdentifier, MessageDataCallbackType), (override)); |
||||
MOCK_METHOD(void, RegisterRecvTrailingMetadata, |
||||
(StreamIdentifier, TrailingMetadataCallbackType), (override)); |
||||
MOCK_METHOD(void, NotifyRecvInitialMetadata, |
||||
(StreamIdentifier, absl::StatusOr<Metadata>), (override)); |
||||
MOCK_METHOD(void, NotifyRecvMessage, |
||||
(StreamIdentifier, absl::StatusOr<std::string>), (override)); |
||||
MOCK_METHOD(void, NotifyRecvTrailingMetadata, |
||||
(StreamIdentifier, absl::StatusOr<Metadata>, int), (override)); |
||||
MOCK_METHOD(void, CancelStream, (StreamIdentifier), (override)); |
||||
}; |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_TEST_CORE_TRANSPORT_BINDER_MOCK_OBJECTS_H
|
@ -1,287 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 <gmock/gmock.h> |
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <cassert> |
||||
#include <string> |
||||
#include <utility> |
||||
#include <vector> |
||||
|
||||
#include "absl/memory/memory.h" |
||||
#include "src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h" |
||||
#include "test/core/test_util/test_config.h" |
||||
|
||||
namespace grpc_binder { |
||||
namespace { |
||||
|
||||
// TODO(waynetu): These are hacks to make callbacks aware of their stream IDs
|
||||
// and sequence numbers. Remove/Refactor these hacks when possible.
|
||||
template <typename T> |
||||
std::pair<StreamIdentifier, int> Decode(const T& /*data*/) { |
||||
assert(false && "This should not be called"); |
||||
return {}; |
||||
} |
||||
|
||||
template <> |
||||
std::pair<StreamIdentifier, int> Decode<std::string>(const std::string& data) { |
||||
assert(data.size() == sizeof(StreamIdentifier) + sizeof(int)); |
||||
StreamIdentifier id{}; |
||||
int seq_num{}; |
||||
std::memcpy(&id, data.data(), sizeof(StreamIdentifier)); |
||||
std::memcpy(&seq_num, data.data() + sizeof(StreamIdentifier), sizeof(int)); |
||||
return std::make_pair(id, seq_num); |
||||
} |
||||
|
||||
template <> |
||||
std::pair<StreamIdentifier, int> Decode<Metadata>(const Metadata& data) { |
||||
assert(data.size() == 1); |
||||
const std::string& encoding = data[0].first; |
||||
return Decode(encoding); |
||||
} |
||||
|
||||
template <typename T> |
||||
T Encode(StreamIdentifier /*id*/, int /*seq_num*/) { |
||||
assert(false && "This should not be called"); |
||||
return {}; |
||||
} |
||||
|
||||
template <> |
||||
std::string Encode<std::string>(StreamIdentifier id, int seq_num) { |
||||
char result[sizeof(StreamIdentifier) + sizeof(int)]; |
||||
std::memcpy(result, &id, sizeof(StreamIdentifier)); |
||||
std::memcpy(result + sizeof(StreamIdentifier), &seq_num, sizeof(int)); |
||||
return std::string(result, sizeof(StreamIdentifier) + sizeof(int)); |
||||
} |
||||
|
||||
template <> |
||||
Metadata Encode<Metadata>(StreamIdentifier id, int seq_num) { |
||||
return {{Encode<std::string>(id, seq_num), ""}}; |
||||
} |
||||
|
||||
MATCHER_P2(StreamIdAndSeqNumMatch, id, seq_num, "") { |
||||
auto p = Decode(arg.value()); |
||||
return p.first == id && p.second == seq_num; |
||||
} |
||||
|
||||
// MockCallback is used to verify the every callback passed to transaction
|
||||
// receiver will eventually be invoked with the artifact of its corresponding
|
||||
// binder transaction.
|
||||
template <typename FirstArg, typename... TrailingArgs> |
||||
class MockCallback { |
||||
public: |
||||
explicit MockCallback(StreamIdentifier id, int seq_num) |
||||
: id_(id), seq_num_(seq_num) {} |
||||
|
||||
MOCK_METHOD(void, ActualCallback, (FirstArg), ()); |
||||
|
||||
std::function<void(FirstArg, TrailingArgs...)> GetHandle() { |
||||
return [this](FirstArg first_arg, TrailingArgs...) { |
||||
this->ActualCallback(first_arg); |
||||
}; |
||||
} |
||||
|
||||
void ExpectCallbackInvocation() { |
||||
EXPECT_CALL(*this, ActualCallback(StreamIdAndSeqNumMatch(id_, seq_num_))); |
||||
} |
||||
|
||||
private: |
||||
StreamIdentifier id_; |
||||
int seq_num_; |
||||
}; |
||||
|
||||
using MockInitialMetadataCallback = MockCallback<absl::StatusOr<Metadata>>; |
||||
using MockMessageCallback = MockCallback<absl::StatusOr<std::string>>; |
||||
using MockTrailingMetadataCallback = |
||||
MockCallback<absl::StatusOr<Metadata>, int>; |
||||
|
||||
class MockOpBatch { |
||||
public: |
||||
MockOpBatch(StreamIdentifier id, int flag, int seq_num) |
||||
: id_(id), flag_(flag), seq_num_(seq_num) { |
||||
if (flag_ & kFlagPrefix) { |
||||
initial_metadata_callback_ = |
||||
std::make_unique<MockInitialMetadataCallback>(id_, seq_num_); |
||||
} |
||||
if (flag_ & kFlagMessageData) { |
||||
message_callback_ = std::make_unique<MockMessageCallback>(id_, seq_num_); |
||||
} |
||||
if (flag_ & kFlagSuffix) { |
||||
trailing_metadata_callback_ = |
||||
std::make_unique<MockTrailingMetadataCallback>(id_, seq_num_); |
||||
} |
||||
} |
||||
|
||||
void Complete(TransportStreamReceiver& receiver) { |
||||
if (flag_ & kFlagPrefix) { |
||||
initial_metadata_callback_->ExpectCallbackInvocation(); |
||||
receiver.NotifyRecvInitialMetadata(id_, Encode<Metadata>(id_, seq_num_)); |
||||
} |
||||
if (flag_ & kFlagMessageData) { |
||||
message_callback_->ExpectCallbackInvocation(); |
||||
receiver.NotifyRecvMessage(id_, Encode<std::string>(id_, seq_num_)); |
||||
} |
||||
if (flag_ & kFlagSuffix) { |
||||
trailing_metadata_callback_->ExpectCallbackInvocation(); |
||||
receiver.NotifyRecvTrailingMetadata(id_, Encode<Metadata>(id_, seq_num_), |
||||
0); |
||||
} |
||||
} |
||||
|
||||
void RequestRecv(TransportStreamReceiver& receiver) { |
||||
if (flag_ & kFlagPrefix) { |
||||
receiver.RegisterRecvInitialMetadata( |
||||
id_, initial_metadata_callback_->GetHandle()); |
||||
} |
||||
if (flag_ & kFlagMessageData) { |
||||
receiver.RegisterRecvMessage(id_, message_callback_->GetHandle()); |
||||
} |
||||
if (flag_ & kFlagSuffix) { |
||||
receiver.RegisterRecvTrailingMetadata( |
||||
id_, trailing_metadata_callback_->GetHandle()); |
||||
} |
||||
} |
||||
|
||||
MockOpBatch NextBatch(int flag) const { |
||||
return MockOpBatch(id_, flag, seq_num_ + 1); |
||||
} |
||||
|
||||
private: |
||||
std::unique_ptr<MockInitialMetadataCallback> initial_metadata_callback_; |
||||
std::unique_ptr<MockMessageCallback> message_callback_; |
||||
std::unique_ptr<MockTrailingMetadataCallback> trailing_metadata_callback_; |
||||
int id_, flag_, seq_num_; |
||||
}; |
||||
|
||||
class TransportStreamReceiverTest : public ::testing::Test { |
||||
protected: |
||||
MockOpBatch NewGrpcStream(int flag) { |
||||
return MockOpBatch(current_id_++, flag, 0); |
||||
} |
||||
|
||||
StreamIdentifier current_id_ = 0; |
||||
}; |
||||
|
||||
const int kFlagAll = kFlagPrefix | kFlagMessageData | kFlagSuffix; |
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(TransportStreamReceiverTest, MultipleStreamRequestThenComplete) { |
||||
TransportStreamReceiverImpl receiver(/*is_client=*/true); |
||||
MockOpBatch t0 = NewGrpcStream(kFlagAll); |
||||
t0.RequestRecv(receiver); |
||||
t0.Complete(receiver); |
||||
} |
||||
|
||||
TEST_F(TransportStreamReceiverTest, MultipleStreamCompleteThenRequest) { |
||||
TransportStreamReceiverImpl receiver(/*is_client=*/true); |
||||
MockOpBatch t0 = NewGrpcStream(kFlagAll); |
||||
t0.Complete(receiver); |
||||
t0.RequestRecv(receiver); |
||||
} |
||||
|
||||
TEST_F(TransportStreamReceiverTest, MultipleStreamInterleaved) { |
||||
TransportStreamReceiverImpl receiver(/*is_client=*/true); |
||||
MockOpBatch t0 = NewGrpcStream(kFlagAll); |
||||
MockOpBatch t1 = NewGrpcStream(kFlagAll); |
||||
t1.Complete(receiver); |
||||
t0.Complete(receiver); |
||||
t0.RequestRecv(receiver); |
||||
t1.RequestRecv(receiver); |
||||
} |
||||
|
||||
TEST_F(TransportStreamReceiverTest, MultipleStreamInterleavedReversed) { |
||||
TransportStreamReceiverImpl receiver(/*is_client=*/true); |
||||
MockOpBatch t0 = NewGrpcStream(kFlagAll); |
||||
MockOpBatch t1 = NewGrpcStream(kFlagAll); |
||||
t0.RequestRecv(receiver); |
||||
t1.RequestRecv(receiver); |
||||
t1.Complete(receiver); |
||||
t0.Complete(receiver); |
||||
} |
||||
|
||||
TEST_F(TransportStreamReceiverTest, MultipleStreamMoreInterleaved) { |
||||
TransportStreamReceiverImpl receiver(/*is_client=*/true); |
||||
MockOpBatch t0 = NewGrpcStream(kFlagAll); |
||||
MockOpBatch t1 = NewGrpcStream(kFlagAll); |
||||
t0.RequestRecv(receiver); |
||||
t1.Complete(receiver); |
||||
MockOpBatch t2 = NewGrpcStream(kFlagAll); |
||||
t2.RequestRecv(receiver); |
||||
t0.Complete(receiver); |
||||
t1.RequestRecv(receiver); |
||||
t2.Complete(receiver); |
||||
} |
||||
|
||||
TEST_F(TransportStreamReceiverTest, SingleStreamUnaryCall) { |
||||
TransportStreamReceiverImpl receiver(/*is_client=*/true); |
||||
MockOpBatch t0 = NewGrpcStream(kFlagPrefix); |
||||
MockOpBatch t1 = t0.NextBatch(kFlagMessageData); |
||||
MockOpBatch t2 = t1.NextBatch(kFlagSuffix); |
||||
t0.RequestRecv(receiver); |
||||
t1.RequestRecv(receiver); |
||||
t2.RequestRecv(receiver); |
||||
t0.Complete(receiver); |
||||
t1.Complete(receiver); |
||||
t2.Complete(receiver); |
||||
} |
||||
|
||||
TEST_F(TransportStreamReceiverTest, SingleStreamStreamingCall) { |
||||
TransportStreamReceiverImpl receiver(/*is_client=*/true); |
||||
MockOpBatch t0 = NewGrpcStream(kFlagPrefix); |
||||
t0.RequestRecv(receiver); |
||||
t0.Complete(receiver); |
||||
MockOpBatch t1 = t0.NextBatch(kFlagMessageData); |
||||
t1.Complete(receiver); |
||||
t1.RequestRecv(receiver); |
||||
MockOpBatch t2 = t1.NextBatch(kFlagMessageData); |
||||
t2.RequestRecv(receiver); |
||||
t2.Complete(receiver); |
||||
MockOpBatch t3 = t2.NextBatch(kFlagMessageData); |
||||
MockOpBatch t4 = t3.NextBatch(kFlagMessageData); |
||||
t3.Complete(receiver); |
||||
t4.Complete(receiver); |
||||
t3.RequestRecv(receiver); |
||||
t4.RequestRecv(receiver); |
||||
} |
||||
|
||||
TEST_F(TransportStreamReceiverTest, DISABLED_SingleStreamBufferedCallbacks) { |
||||
TransportStreamReceiverImpl receiver(/*is_client=*/true); |
||||
MockOpBatch t0 = NewGrpcStream(kFlagPrefix); |
||||
MockOpBatch t1 = t0.NextBatch(kFlagMessageData); |
||||
MockOpBatch t2 = t1.NextBatch(kFlagMessageData); |
||||
MockOpBatch t3 = t2.NextBatch(kFlagSuffix); |
||||
t0.RequestRecv(receiver); |
||||
// TODO(waynetu): Can gRPC issues recv_message before it actually receives the
|
||||
// previous one?
|
||||
t1.RequestRecv(receiver); |
||||
t2.RequestRecv(receiver); |
||||
t3.RequestRecv(receiver); |
||||
t0.Complete(receiver); |
||||
t1.Complete(receiver); |
||||
t2.Complete(receiver); |
||||
t3.Complete(receiver); |
||||
} |
||||
|
||||
// TODO(waynetu): Should we have some concurrent stress tests to make sure that
|
||||
// thread safety is well taken care of?
|
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
grpc::testing::TestEnvironment env(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
@ -1,376 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Unit tests for WireReaderImpl.
|
||||
//
|
||||
// WireReaderImpl is responsible for turning incoming transactions into
|
||||
// top-level metadata. The following tests verify that the interactions between
|
||||
// WireReaderImpl and both the output (readable) parcel and the transport stream
|
||||
// receiver are correct in all possible situations.
|
||||
|
||||
#include <grpc/grpc.h> |
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <memory> |
||||
#include <string> |
||||
#include <thread> |
||||
#include <utility> |
||||
|
||||
#include "absl/memory/memory.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_reader_impl.h" |
||||
#include "test/core/test_util/test_config.h" |
||||
#include "test/core/transport/binder/mock_objects.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
using ::testing::DoAll; |
||||
using ::testing::Return; |
||||
using ::testing::SetArgPointee; |
||||
using ::testing::StrictMock; |
||||
|
||||
namespace { |
||||
|
||||
class WireReaderTest : public ::testing::Test { |
||||
protected: |
||||
void SetUp() override { SetUp(true); } |
||||
void SetUp(bool is_client) { |
||||
transport_stream_receiver_ = |
||||
std::make_shared<StrictMock<MockTransportStreamReceiver>>(); |
||||
wire_reader_ = std::make_shared<WireReaderImpl>( |
||||
transport_stream_receiver_, is_client, |
||||
std::make_shared< |
||||
grpc::experimental::binder::UntrustedSecurityPolicy>()); |
||||
} |
||||
|
||||
void ExpectReadInt32(int result) { |
||||
EXPECT_CALL(mock_readable_parcel_, ReadInt32) |
||||
.WillOnce(DoAll(SetArgPointee<0>(result), Return(absl::OkStatus()))); |
||||
} |
||||
|
||||
void ExpectReadByteArray(const std::string& buffer) { |
||||
ExpectReadInt32(buffer.length()); |
||||
if (!buffer.empty()) { |
||||
EXPECT_CALL(mock_readable_parcel_, ReadByteArray) |
||||
.WillOnce([buffer](std::string* data) { |
||||
*data = buffer; |
||||
return absl::OkStatus(); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
void ExpectReadString(const std::string& str) { |
||||
EXPECT_CALL(mock_readable_parcel_, ReadString) |
||||
.WillOnce([str](std::string* out) { |
||||
*out = str; |
||||
return absl::OkStatus(); |
||||
}); |
||||
} |
||||
|
||||
void UnblockSetupTransport() { |
||||
// SETUP_TRANSPORT should finish before we can proceed with any other
|
||||
// requests and streaming calls. The MockBinder will construct a
|
||||
// MockTransactionReceiver, which will then sends SETUP_TRANSPORT request
|
||||
// back to us.
|
||||
wire_reader_->SetupTransport(std::make_unique<MockBinder>()); |
||||
} |
||||
|
||||
template <typename T> |
||||
absl::Status CallProcessTransaction(T tx_code) { |
||||
return wire_reader_->ProcessTransaction( |
||||
static_cast<transaction_code_t>(tx_code), &mock_readable_parcel_, |
||||
/*uid=*/0); |
||||
} |
||||
|
||||
std::shared_ptr<StrictMock<MockTransportStreamReceiver>> |
||||
transport_stream_receiver_; |
||||
std::shared_ptr<WireReaderImpl> wire_reader_; |
||||
MockReadableParcel mock_readable_parcel_; |
||||
}; |
||||
|
||||
MATCHER_P(StatusOrStrEq, target, "") { |
||||
if (!arg.ok()) return false; |
||||
return arg.value() == target; |
||||
} |
||||
|
||||
MATCHER_P(StatusOrContainerEq, target, "") { |
||||
if (!arg.ok()) return false; |
||||
return arg.value() == target; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(WireReaderTest, SetupTransport) { |
||||
auto mock_binder = std::make_unique<MockBinder>(); |
||||
MockBinder& mock_binder_ref = *mock_binder; |
||||
|
||||
::testing::InSequence sequence; |
||||
EXPECT_CALL(mock_binder_ref, Initialize); |
||||
EXPECT_CALL(mock_binder_ref, PrepareTransaction); |
||||
const MockReadableParcel mock_readable_parcel; |
||||
EXPECT_CALL(mock_binder_ref, GetWritableParcel); |
||||
|
||||
// Write version.
|
||||
EXPECT_CALL(mock_binder_ref.GetWriter(), WriteInt32(1)); |
||||
|
||||
wire_reader_->SetupTransport(std::move(mock_binder)); |
||||
} |
||||
|
||||
TEST_F(WireReaderTest, ProcessTransactionControlMessageSetupTransport) { |
||||
::testing::InSequence sequence; |
||||
UnblockSetupTransport(); |
||||
} |
||||
|
||||
TEST_F(WireReaderTest, ProcessTransactionControlMessagePingResponse) { |
||||
::testing::InSequence sequence; |
||||
UnblockSetupTransport(); |
||||
EXPECT_CALL(mock_readable_parcel_, ReadInt32); |
||||
EXPECT_TRUE( |
||||
CallProcessTransaction(BinderTransportTxCode::PING_RESPONSE).ok()); |
||||
} |
||||
|
||||
TEST_F(WireReaderTest, ProcessTransactionServerRpcDataEmptyFlagIgnored) { |
||||
::testing::InSequence sequence; |
||||
UnblockSetupTransport(); |
||||
|
||||
// first transaction: empty flag
|
||||
ExpectReadInt32(0); |
||||
// Won't further read sequence number.
|
||||
EXPECT_TRUE(CallProcessTransaction(kFirstCallId).ok()); |
||||
} |
||||
|
||||
TEST_F(WireReaderTest, |
||||
ProcessTransactionServerRpcDataFlagPrefixWithoutMetadata) { |
||||
::testing::InSequence sequence; |
||||
UnblockSetupTransport(); |
||||
|
||||
// flag
|
||||
ExpectReadInt32(kFlagPrefix); |
||||
// sequence number
|
||||
ExpectReadInt32(0); |
||||
|
||||
// count
|
||||
ExpectReadInt32(0); |
||||
EXPECT_CALL( |
||||
*transport_stream_receiver_, |
||||
NotifyRecvInitialMetadata(kFirstCallId, StatusOrContainerEq(Metadata{}))); |
||||
|
||||
EXPECT_TRUE(CallProcessTransaction(kFirstCallId).ok()); |
||||
} |
||||
|
||||
TEST_F(WireReaderTest, ProcessTransactionServerRpcDataFlagPrefixWithMetadata) { |
||||
::testing::InSequence sequence; |
||||
UnblockSetupTransport(); |
||||
|
||||
// flag
|
||||
ExpectReadInt32(kFlagPrefix); |
||||
// sequence number
|
||||
ExpectReadInt32(0); |
||||
|
||||
const std::vector<std::pair<std::string, std::string>> kMetadata = { |
||||
{"", ""}, |
||||
{"", "value"}, |
||||
{"key", ""}, |
||||
{"key", "value"}, |
||||
{"another-key", "another-value"}, |
||||
}; |
||||
|
||||
// count
|
||||
ExpectReadInt32(kMetadata.size()); |
||||
for (const auto& md : kMetadata) { |
||||
// metadata key
|
||||
ExpectReadByteArray(md.first); |
||||
// metadata val
|
||||
// TODO(waynetu): metadata value can also be "parcelable".
|
||||
ExpectReadByteArray(md.second); |
||||
} |
||||
EXPECT_CALL( |
||||
*transport_stream_receiver_, |
||||
NotifyRecvInitialMetadata(kFirstCallId, StatusOrContainerEq(kMetadata))); |
||||
|
||||
EXPECT_TRUE(CallProcessTransaction(kFirstCallId).ok()); |
||||
} |
||||
|
||||
TEST_F(WireReaderTest, ProcessTransactionServerRpcDataFlagMessageDataNonEmpty) { |
||||
::testing::InSequence sequence; |
||||
UnblockSetupTransport(); |
||||
|
||||
// flag
|
||||
ExpectReadInt32(kFlagMessageData); |
||||
// sequence number
|
||||
ExpectReadInt32(0); |
||||
|
||||
// message data
|
||||
// TODO(waynetu): message data can also be "parcelable".
|
||||
const std::string kMessageData = "message data"; |
||||
ExpectReadByteArray(kMessageData); |
||||
EXPECT_CALL(*transport_stream_receiver_, |
||||
NotifyRecvMessage(kFirstCallId, StatusOrStrEq(kMessageData))); |
||||
|
||||
EXPECT_TRUE(CallProcessTransaction(kFirstCallId).ok()); |
||||
} |
||||
|
||||
TEST_F(WireReaderTest, ProcessTransactionServerRpcDataFlagMessageDataEmpty) { |
||||
::testing::InSequence sequence; |
||||
UnblockSetupTransport(); |
||||
|
||||
// flag
|
||||
ExpectReadInt32(kFlagMessageData); |
||||
// sequence number
|
||||
ExpectReadInt32(0); |
||||
|
||||
// message data
|
||||
// TODO(waynetu): message data can also be "parcelable".
|
||||
const std::string kMessageData; |
||||
ExpectReadByteArray(kMessageData); |
||||
EXPECT_CALL(*transport_stream_receiver_, |
||||
NotifyRecvMessage(kFirstCallId, StatusOrStrEq(kMessageData))); |
||||
|
||||
EXPECT_TRUE(CallProcessTransaction(kFirstCallId).ok()); |
||||
} |
||||
|
||||
TEST_F(WireReaderTest, ProcessTransactionServerRpcDataFlagSuffixWithStatus) { |
||||
::testing::InSequence sequence; |
||||
UnblockSetupTransport(); |
||||
|
||||
constexpr int kStatus = 0x1234; |
||||
// flag
|
||||
ExpectReadInt32(kFlagSuffix | kFlagStatusDescription | (kStatus << 16)); |
||||
// sequence number
|
||||
ExpectReadInt32(0); |
||||
// status description
|
||||
EXPECT_CALL(mock_readable_parcel_, ReadString); |
||||
// metadata count
|
||||
ExpectReadInt32(0); |
||||
EXPECT_CALL(*transport_stream_receiver_, |
||||
NotifyRecvTrailingMetadata( |
||||
kFirstCallId, StatusOrContainerEq(Metadata{}), kStatus)); |
||||
|
||||
EXPECT_TRUE(CallProcessTransaction(kFirstCallId).ok()); |
||||
} |
||||
|
||||
TEST_F(WireReaderTest, ProcessTransactionServerRpcDataFlagSuffixWithoutStatus) { |
||||
::testing::InSequence sequence; |
||||
UnblockSetupTransport(); |
||||
|
||||
// flag
|
||||
ExpectReadInt32(kFlagSuffix); |
||||
// sequence number
|
||||
ExpectReadInt32(0); |
||||
// No status description
|
||||
// metadata count
|
||||
ExpectReadInt32(0); |
||||
EXPECT_CALL(*transport_stream_receiver_, |
||||
NotifyRecvTrailingMetadata(kFirstCallId, |
||||
StatusOrContainerEq(Metadata{}), 0)); |
||||
|
||||
EXPECT_TRUE(CallProcessTransaction(kFirstCallId).ok()); |
||||
} |
||||
|
||||
TEST_F(WireReaderTest, InBoundFlowControl) { |
||||
::testing::InSequence sequence; |
||||
UnblockSetupTransport(); |
||||
|
||||
// data size
|
||||
EXPECT_CALL(mock_readable_parcel_, GetDataSize).WillOnce(Return(1000)); |
||||
// flag
|
||||
ExpectReadInt32(kFlagMessageData | kFlagMessageDataIsPartial); |
||||
// sequence number
|
||||
ExpectReadInt32(0); |
||||
// message size
|
||||
ExpectReadInt32(1000); |
||||
EXPECT_CALL(mock_readable_parcel_, ReadByteArray) |
||||
.WillOnce(DoAll(SetArgPointee<0>(std::string(1000, 'a')), |
||||
Return(absl::OkStatus()))); |
||||
|
||||
// Data is not completed. No callback will be triggered.
|
||||
EXPECT_TRUE(CallProcessTransaction(kFirstCallId).ok()); |
||||
|
||||
EXPECT_CALL(mock_readable_parcel_, GetDataSize).WillOnce(Return(1000)); |
||||
// flag
|
||||
ExpectReadInt32(kFlagMessageData); |
||||
// sequence number
|
||||
ExpectReadInt32(1); |
||||
// message size
|
||||
ExpectReadInt32(1000); |
||||
EXPECT_CALL(mock_readable_parcel_, ReadByteArray) |
||||
.WillOnce(DoAll(SetArgPointee<0>(std::string(1000, 'b')), |
||||
Return(absl::OkStatus()))); |
||||
|
||||
EXPECT_CALL(*transport_stream_receiver_, |
||||
NotifyRecvMessage(kFirstCallId, |
||||
StatusOrContainerEq(std::string(1000, 'a') + |
||||
std::string(1000, 'b')))); |
||||
EXPECT_TRUE(CallProcessTransaction(kFirstCallId).ok()); |
||||
} |
||||
|
||||
TEST_F(WireReaderTest, ServerInitialMetadata) { |
||||
SetUp(/*is_client=*/false); |
||||
|
||||
::testing::InSequence sequence; |
||||
UnblockSetupTransport(); |
||||
|
||||
// flag
|
||||
ExpectReadInt32(kFlagPrefix); |
||||
// sequence number
|
||||
ExpectReadInt32(0); |
||||
|
||||
const std::vector<std::pair<std::string, std::string>> kMetadata = { |
||||
{"", ""}, |
||||
{"", "value"}, |
||||
{"key", ""}, |
||||
{"key", "value"}, |
||||
{"another-key", "another-value"}, |
||||
}; |
||||
|
||||
// method ref
|
||||
ExpectReadString("test.service/rpc.method"); |
||||
|
||||
// metadata
|
||||
{ |
||||
// count
|
||||
ExpectReadInt32(kMetadata.size()); |
||||
for (const auto& md : kMetadata) { |
||||
// metadata key
|
||||
ExpectReadByteArray(md.first); |
||||
// metadata val
|
||||
// TODO(waynetu): metadata value can also be "parcelable".
|
||||
ExpectReadByteArray(md.second); |
||||
} |
||||
} |
||||
|
||||
// Since path and authority is not encoded as metadata in wire format,
|
||||
// wire_reader implementation should insert them as metadata before passing
|
||||
// to transport layer.
|
||||
auto metadata_expectation = kMetadata; |
||||
metadata_expectation.push_back({":path", "/test.service/rpc.method"}); |
||||
metadata_expectation.push_back({":authority", "binder.authority"}); |
||||
|
||||
EXPECT_CALL(*transport_stream_receiver_, |
||||
NotifyRecvInitialMetadata( |
||||
kFirstCallId, StatusOrContainerEq(metadata_expectation))); |
||||
|
||||
EXPECT_TRUE(CallProcessTransaction(kFirstCallId).ok()); |
||||
} |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
grpc::testing::TestEnvironment env(&argc, argv); |
||||
grpc_init(); |
||||
auto results = RUN_ALL_TESTS(); |
||||
grpc_shutdown(); |
||||
return results; |
||||
} |
@ -1,265 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// 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 "src/core/ext/transport/binder/wire_format/wire_writer.h" |
||||
|
||||
#include <grpcpp/impl/grpc_library.h> |
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <string> |
||||
#include <utility> |
||||
|
||||
#include "absl/memory/memory.h" |
||||
#include "test/core/test_util/test_config.h" |
||||
#include "test/core/transport/binder/mock_objects.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
using ::testing::Return; |
||||
|
||||
MATCHER_P(StrEqInt8Ptr, target, "") { |
||||
return std::string(reinterpret_cast<const char*>(arg), target.size()) == |
||||
target; |
||||
} |
||||
|
||||
TEST(WireWriterTest, RpcCall) { |
||||
grpc::internal::GrpcLibrary init_lib; |
||||
// Required because wire writer uses combiner internally.
|
||||
grpc_core::ExecCtx exec_ctx; |
||||
auto mock_binder = std::make_unique<MockBinder>(); |
||||
MockBinder& mock_binder_ref = *mock_binder; |
||||
MockWritableParcel mock_writable_parcel; |
||||
ON_CALL(mock_binder_ref, GetWritableParcel) |
||||
.WillByDefault(Return(&mock_writable_parcel)); |
||||
WireWriterImpl wire_writer(std::move(mock_binder)); |
||||
|
||||
auto ExpectWriteByteArray = [&](const std::string& target) { |
||||
// length
|
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(target.size())); |
||||
if (!target.empty()) { |
||||
// content
|
||||
EXPECT_CALL(mock_writable_parcel, |
||||
WriteByteArray(StrEqInt8Ptr(target), target.size())); |
||||
} |
||||
}; |
||||
|
||||
::testing::InSequence sequence; |
||||
int sequence_number = 0; |
||||
|
||||
{ |
||||
// flag
|
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(0)); |
||||
// sequence number
|
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(sequence_number)); |
||||
|
||||
EXPECT_CALL(mock_binder_ref, Transact(BinderTransportTxCode(kFirstCallId))); |
||||
|
||||
auto tx = std::make_unique<Transaction>(kFirstCallId, /*is_client=*/true); |
||||
EXPECT_TRUE(wire_writer.RpcCall(std::move(tx)).ok()); |
||||
sequence_number++; |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
} |
||||
{ |
||||
// flag
|
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(kFlagPrefix)); |
||||
// sequence number. This is another stream so the sequence number starts
|
||||
// with 0.
|
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(0)); |
||||
|
||||
EXPECT_CALL(mock_writable_parcel, |
||||
WriteString(absl::string_view("/example/method/ref"))); |
||||
|
||||
const std::vector<std::pair<std::string, std::string>> kMetadata = { |
||||
{"", ""}, |
||||
{"", "value"}, |
||||
{"key", ""}, |
||||
{"key", "value"}, |
||||
{"another-key", "another-value"}, |
||||
}; |
||||
|
||||
// Number of metadata
|
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(kMetadata.size())); |
||||
|
||||
for (const auto& md : kMetadata) { |
||||
ExpectWriteByteArray(md.first); |
||||
ExpectWriteByteArray(md.second); |
||||
} |
||||
|
||||
EXPECT_CALL(mock_binder_ref, |
||||
Transact(BinderTransportTxCode(kFirstCallId + 1))); |
||||
|
||||
auto tx = |
||||
std::make_unique<Transaction>(kFirstCallId + 1, /*is_client=*/true); |
||||
tx->SetPrefix(kMetadata); |
||||
tx->SetMethodRef("/example/method/ref"); |
||||
EXPECT_TRUE(wire_writer.RpcCall(std::move(tx)).ok()); |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
} |
||||
{ |
||||
// flag
|
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(kFlagMessageData)); |
||||
// sequence number
|
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(sequence_number)); |
||||
|
||||
ExpectWriteByteArray("data"); |
||||
EXPECT_CALL(mock_binder_ref, Transact(BinderTransportTxCode(kFirstCallId))); |
||||
|
||||
auto tx = std::make_unique<Transaction>(kFirstCallId, /*is_client=*/true); |
||||
tx->SetData("data"); |
||||
EXPECT_TRUE(wire_writer.RpcCall(std::move(tx)).ok()); |
||||
sequence_number++; |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
} |
||||
{ |
||||
// flag
|
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(kFlagSuffix)); |
||||
// sequence number
|
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(sequence_number)); |
||||
|
||||
EXPECT_CALL(mock_binder_ref, Transact(BinderTransportTxCode(kFirstCallId))); |
||||
|
||||
auto tx = std::make_unique<Transaction>(kFirstCallId, /*is_client=*/true); |
||||
tx->SetSuffix({}); |
||||
EXPECT_TRUE(wire_writer.RpcCall(std::move(tx)).ok()); |
||||
sequence_number++; |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
} |
||||
{ |
||||
// flag
|
||||
EXPECT_CALL(mock_writable_parcel, |
||||
WriteInt32(kFlagPrefix | kFlagMessageData | kFlagSuffix)); |
||||
// sequence number
|
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(sequence_number)); |
||||
|
||||
EXPECT_CALL(mock_writable_parcel, |
||||
WriteString(absl::string_view("/example/method/ref"))); |
||||
|
||||
const std::vector<std::pair<std::string, std::string>> kMetadata = { |
||||
{"", ""}, |
||||
{"", "value"}, |
||||
{"key", ""}, |
||||
{"key", "value"}, |
||||
{"another-key", "another-value"}, |
||||
}; |
||||
|
||||
// Number of metadata
|
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(kMetadata.size())); |
||||
|
||||
for (const auto& md : kMetadata) { |
||||
ExpectWriteByteArray(md.first); |
||||
ExpectWriteByteArray(md.second); |
||||
} |
||||
|
||||
// Empty message data
|
||||
ExpectWriteByteArray(""); |
||||
|
||||
EXPECT_CALL(mock_binder_ref, Transact(BinderTransportTxCode(kFirstCallId))); |
||||
|
||||
auto tx = std::make_unique<Transaction>(kFirstCallId, /*is_client=*/true); |
||||
// TODO(waynetu): Implement a helper function that automatically creates
|
||||
// EXPECT_CALL based on the tx object.
|
||||
tx->SetPrefix(kMetadata); |
||||
tx->SetMethodRef("/example/method/ref"); |
||||
tx->SetData(""); |
||||
tx->SetSuffix({}); |
||||
EXPECT_TRUE(wire_writer.RpcCall(std::move(tx)).ok()); |
||||
sequence_number++; |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
} |
||||
|
||||
// Really large message
|
||||
{ |
||||
EXPECT_CALL(mock_writable_parcel, |
||||
WriteInt32(kFlagMessageData | kFlagMessageDataIsPartial)); |
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(0)); |
||||
ExpectWriteByteArray(std::string(WireWriterImpl::kBlockSize, 'a')); |
||||
EXPECT_CALL(mock_writable_parcel, GetDataSize) |
||||
.WillOnce(Return(WireWriterImpl::kBlockSize)); |
||||
EXPECT_CALL(mock_binder_ref, |
||||
Transact(BinderTransportTxCode(kFirstCallId + 2))); |
||||
|
||||
EXPECT_CALL(mock_writable_parcel, |
||||
WriteInt32(kFlagMessageData | kFlagMessageDataIsPartial)); |
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(1)); |
||||
ExpectWriteByteArray(std::string(WireWriterImpl::kBlockSize, 'a')); |
||||
EXPECT_CALL(mock_writable_parcel, GetDataSize) |
||||
.WillOnce(Return(WireWriterImpl::kBlockSize)); |
||||
EXPECT_CALL(mock_binder_ref, |
||||
Transact(BinderTransportTxCode(kFirstCallId + 2))); |
||||
|
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(kFlagMessageData)); |
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(2)); |
||||
ExpectWriteByteArray("a"); |
||||
EXPECT_CALL(mock_writable_parcel, GetDataSize).WillOnce(Return(1)); |
||||
EXPECT_CALL(mock_binder_ref, |
||||
Transact(BinderTransportTxCode(kFirstCallId + 2))); |
||||
|
||||
// Use a new stream.
|
||||
auto tx = |
||||
std::make_unique<Transaction>(kFirstCallId + 2, /*is_client=*/true); |
||||
tx->SetData(std::string(2 * WireWriterImpl::kBlockSize + 1, 'a')); |
||||
EXPECT_TRUE(wire_writer.RpcCall(std::move(tx)).ok()); |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
} |
||||
// Really large message with metadata
|
||||
{ |
||||
EXPECT_CALL( |
||||
mock_writable_parcel, |
||||
WriteInt32(kFlagPrefix | kFlagMessageData | kFlagMessageDataIsPartial)); |
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(0)); |
||||
EXPECT_CALL(mock_writable_parcel, WriteString(absl::string_view("123"))); |
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(0)); |
||||
ExpectWriteByteArray(std::string(WireWriterImpl::kBlockSize, 'a')); |
||||
EXPECT_CALL(mock_writable_parcel, GetDataSize) |
||||
.WillOnce(Return(WireWriterImpl::kBlockSize)); |
||||
EXPECT_CALL(mock_binder_ref, |
||||
Transact(BinderTransportTxCode(kFirstCallId + 3))); |
||||
|
||||
EXPECT_CALL(mock_writable_parcel, |
||||
WriteInt32(kFlagMessageData | kFlagMessageDataIsPartial)); |
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(1)); |
||||
ExpectWriteByteArray(std::string(WireWriterImpl::kBlockSize, 'a')); |
||||
EXPECT_CALL(mock_writable_parcel, GetDataSize) |
||||
.WillOnce(Return(WireWriterImpl::kBlockSize)); |
||||
EXPECT_CALL(mock_binder_ref, |
||||
Transact(BinderTransportTxCode(kFirstCallId + 3))); |
||||
|
||||
EXPECT_CALL(mock_writable_parcel, |
||||
WriteInt32(kFlagMessageData | kFlagSuffix)); |
||||
EXPECT_CALL(mock_writable_parcel, WriteInt32(2)); |
||||
ExpectWriteByteArray("a"); |
||||
EXPECT_CALL(mock_writable_parcel, GetDataSize).WillOnce(Return(1)); |
||||
EXPECT_CALL(mock_binder_ref, |
||||
Transact(BinderTransportTxCode(kFirstCallId + 3))); |
||||
|
||||
// Use a new stream.
|
||||
auto tx = |
||||
std::make_unique<Transaction>(kFirstCallId + 3, /*is_client=*/true); |
||||
tx->SetPrefix({}); |
||||
tx->SetMethodRef("123"); |
||||
tx->SetData(std::string(2 * WireWriterImpl::kBlockSize + 1, 'a')); |
||||
tx->SetSuffix({}); |
||||
EXPECT_TRUE(wire_writer.RpcCall(std::move(tx)).ok()); |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
} |
||||
grpc_core::ExecCtx::Get()->Flush(); |
||||
} |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
grpc::testing::TestEnvironment env(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
Loading…
Reference in new issue