Update artifacts branch

artifacts
GitHub Actions 5 months ago
parent 6386491d15
commit b6e418d03b
  1. 2
      .source-revision
  2. 126
      include/grpcpp/create_channel_binder.h
  3. 43
      include/grpcpp/security/binder_credentials.h
  4. 135
      src/core/ext/transport/binder/client/binder_connector.cc
  5. 42
      src/core/ext/transport/binder/client/binder_connector.h
  6. 232
      src/core/ext/transport/binder/client/channel_create.cc
  7. 94
      src/core/ext/transport/binder/client/channel_create_impl.cc
  8. 51
      src/core/ext/transport/binder/client/connection_id_generator.h
  9. 114
      src/core/ext/transport/binder/client/endpoint_binder_pool.cc
  10. 65
      src/core/ext/transport/binder/client/endpoint_binder_pool.h
  11. 138
      src/core/ext/transport/binder/client/jni_utils.cc
  12. 58
      src/core/ext/transport/binder/client/jni_utils.h
  13. 47
      src/core/ext/transport/binder/client/security_policy_setting.cc
  14. 50
      src/core/ext/transport/binder/client/security_policy_setting.h
  15. 107
      src/core/ext/transport/binder/security_policy/binder_security_policy.cc
  16. 250
      src/core/ext/transport/binder/server/binder_server.cc
  17. 66
      src/core/ext/transport/binder/server/binder_server.h
  18. 71
      src/core/ext/transport/binder/server/binder_server_credentials.cc
  19. 759
      src/core/ext/transport/binder/transport/binder_transport.cc
  20. 120
      src/core/ext/transport/binder/transport/binder_transport.h
  21. 219
      src/core/ext/transport/binder/utils/ndk_binder.cc
  22. 71
      src/core/ext/transport/binder/utils/transport_stream_receiver.h
  23. 258
      src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc
  24. 112
      src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h
  25. 105
      src/core/ext/transport/binder/wire_format/binder.h
  26. 309
      src/core/ext/transport/binder/wire_format/binder_android.cc
  27. 122
      src/core/ext/transport/binder/wire_format/binder_android.h
  28. 43
      src/core/ext/transport/binder/wire_format/binder_constants.h
  29. 107
      src/core/ext/transport/binder/wire_format/transaction.h
  30. 38
      src/core/ext/transport/binder/wire_format/wire_reader.h
  31. 451
      src/core/ext/transport/binder/wire_format/wire_reader_impl.cc
  32. 159
      src/core/ext/transport/binder/wire_format/wire_reader_impl.h
  33. 394
      src/core/ext/transport/binder/wire_format/wire_writer.cc
  34. 127
      src/core/ext/transport/binder/wire_format/wire_writer.h
  35. 155
      src/core/resolver/binder/binder_resolver.cc
  36. 218
      test/core/resolver/binder_resolver_test.cc
  37. 731
      test/core/transport/binder/binder_transport_test.cc
  38. 245
      test/core/transport/binder/end2end/binder_server_test.cc
  39. 568
      test/core/transport/binder/end2end/end2end_binder_transport_test.cc
  40. 277
      test/core/transport/binder/end2end/fake_binder.cc
  41. 308
      test/core/transport/binder/end2end/fake_binder.h
  42. 350
      test/core/transport/binder/end2end/fake_binder_test.cc
  43. 159
      test/core/transport/binder/end2end/fuzzers/client_fuzzer.cc
  44. 155
      test/core/transport/binder/end2end/fuzzers/fuzzer_utils.h
  45. 133
      test/core/transport/binder/end2end/fuzzers/server_fuzzer.cc
  46. 134
      test/core/transport/binder/end2end/testing_channel_create.cc
  47. 41
      test/core/transport/binder/end2end/testing_channel_create.h
  48. 75
      test/core/transport/binder/endpoint_binder_pool_test.cc
  49. 288
      test/core/transport/binder/transport_stream_receiver_test.cc
  50. 378
      test/core/transport/binder/wire_reader_test.cc
  51. 267
      test/core/transport/binder/wire_writer_test.cc

@ -1 +1 @@
3cdb1335251ac3f1a4ff4fe469ebc83c7112dd04
4ffcdd4ab7976cac397fcff25b6f952d0b5c8168

@ -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 GRPCPP_CREATE_CHANNEL_BINDER_H
#define GRPCPP_CREATE_CHANNEL_BINDER_H
#include <grpc/support/port_platform.h>
#ifdef GPR_ANDROID
#include <jni.h>
#include <memory>
#include "absl/strings/string_view.h"
#include <grpcpp/channel.h>
#include <grpcpp/security/binder_security_policy.h>
#include <grpcpp/support/channel_arguments.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 <memory>
#include <grpcpp/security/binder_security_policy.h>
#include <grpcpp/security/server_credentials.h>
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,135 +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 <functional>
#include <map>
#include "absl/log/check.h"
#include "absl/log/log.h"
#include <grpcpp/security/binder_security_policy.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,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_BINDER_CONNECTOR_H
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_BINDER_CONNECTOR_H
#include <memory>
#include <utility>
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include <grpc/impl/grpc_types.h>
#include <grpc/support/port_platform.h>
#include <grpcpp/channel.h>
#include <grpcpp/support/channel_arguments.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,232 +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/lib/gprpp/crash.h"
#ifdef GPR_SUPPORT_BINDER_TRANSPORT
#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 <grpc/support/port_platform.h>
#include <grpcpp/impl/grpc_library.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,94 +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,51 +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 <map>
#include "absl/strings/string_view.h"
#include <grpc/support/port_platform.h>
#include "src/core/lib/gprpp/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 "absl/log/check.h"
#include "absl/log/log.h"
#include <grpc/support/port_platform.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,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_CLIENT_ENDPOINT_BINDER_POOL_H
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_ENDPOINT_BINDER_POOL_H
#include <functional>
#include <string>
#include "absl/container/flat_hash_map.h"
#include <grpc/support/port_platform.h>
#include "src/core/ext/transport/binder/wire_format/binder.h"
#include "src/core/lib/gprpp/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 "absl/log/check.h"
#include "absl/log/log.h"
#include <grpc/support/port_platform.h>
#ifndef GRPC_NO_BINDER
#include "src/core/lib/gprpp/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,58 +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 <jni.h>
#include <functional>
#include <string>
#include "absl/strings/string_view.h"
#include <grpc/support/port_platform.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 "absl/log/check.h"
#include <grpc/support/port_platform.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,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_SECURITY_POLICY_SETTING_H
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_SECURITY_POLICY_SETTING_H
#include "absl/container/flat_hash_map.h"
#include "absl/strings/string_view.h"
#include <grpc/support/port_platform.h>
#include <grpcpp/security/binder_security_policy.h>
#include "src/core/lib/gprpp/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,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.
#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/lib/gprpp/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,250 +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 <memory>
#include <string>
#include <utility>
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "absl/memory/memory.h"
#include <grpc/grpc.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,66 +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 <string>
#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
#include <grpc/support/port_platform.h>
#include <grpcpp/security/binder_security_policy.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 "absl/log/check.h"
#include <grpc/support/port_platform.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,759 +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/gprpp/crash.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"
#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,120 +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 <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 <grpc/support/port_platform.h>
#include <grpcpp/security/binder_security_policy.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/gprpp/crash.h"
#include "src/core/lib/iomgr/combiner.h"
#include "src/core/lib/transport/transport.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,219 +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/lib/gprpp/crash.h"
#include "src/core/lib/gprpp/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,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.
#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 <functional>
#include <string>
#include <vector>
#include "absl/status/statusor.h"
#include <grpc/support/port_platform.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,258 +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/lib/gprpp/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 <functional>
#include <map>
#include <queue>
#include <set>
#include <string>
#include <vector>
#include <grpc/support/port_platform.h>
#include "src/core/ext/transport/binder/utils/transport_stream_receiver.h"
#include "src/core/lib/gprpp/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,105 +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 <cstdint>
#include <functional>
#include <memory>
#include <string>
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include <grpc/support/port_platform.h>
#include "src/core/ext/transport/binder/wire_format/binder_constants.h"
#include "src/core/lib/gprpp/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,309 +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/lib/gprpp/crash.h"
#include "src/core/lib/gprpp/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,122 +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,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 <cstdint>
#include "absl/base/attributes.h"
#include <grpc/support/port_platform.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,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_WIRE_FORMAT_TRANSACTION_H
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_TRANSACTION_H
#include <string>
#include <vector>
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "absl/strings/string_view.h"
#include <grpc/support/port_platform.h>
#include "src/core/lib/gprpp/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 <memory>
#include <utility>
#include <grpc/support/port_platform.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/lib/gprpp/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,451 +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/lib/gprpp/crash.h"
#include "src/core/lib/gprpp/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,159 +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 <memory>
#include <queue>
#include <utility>
#include "absl/container/flat_hash_map.h"
#include "absl/functional/any_invocable.h"
#include <grpc/support/port_platform.h>
#include <grpcpp/security/binder_security_policy.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/gprpp/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,394 +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/lib/gprpp/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 `GPR_DEBUG` log level.
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,127 +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 <queue>
#include <string>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include <grpc/support/port_platform.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/gprpp/sync.h"
#include "src/core/lib/iomgr/combiner.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,155 +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 <algorithm>
#include "absl/status/status.h"
#include <grpc/support/port_platform.h>
#include "src/core/lib/gprpp/status_helper.h"
#include "src/core/lib/iomgr/port.h" // IWYU pragma: keep
#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 <grpc/support/log.h>
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/gprpp/orphanable.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/resolved_address.h"
#include "src/core/lib/uri/uri_parser.h"
#include "src/core/resolver/endpoint_addresses.h"
#include "src/core/resolver/resolver.h"
#include "src/core/resolver/resolver_factory.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,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 <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/gprpp/orphanable.h"
#include "src/core/lib/iomgr/port.h"
#include "src/core/lib/iomgr/resolved_address.h"
#include "src/core/lib/uri/uri_parser.h"
#include "src/core/resolver/endpoint_addresses.h"
#include "src/core/resolver/resolver.h"
#include "src/core/resolver/resolver_factory.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 "absl/log/log.h"
#include <grpc/grpc.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,731 +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 <memory>
#include <string>
#include <vector>
#include <gtest/gtest.h>
#include "absl/memory/memory.h"
#include "absl/strings/match.h"
#include "absl/strings/str_join.h"
#include <grpc/grpc.h>
#include <grpcpp/security/binder_security_policy.h>
#include "src/core/ext/transport/binder/transport/binder_stream.h"
#include "src/core/lib/gprpp/notification.h"
#include "src/core/lib/resource_quota/resource_quota.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(&notification) {
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(&notification) {
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(&notification) {
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,245 +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 <memory>
#include <thread>
#include <vector>
#include <gtest/gtest.h>
#include "absl/memory/memory.h"
#include <grpcpp/grpcpp.h>
#include <grpcpp/security/binder_credentials.h>
#include <grpcpp/security/binder_security_policy.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,568 +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 <string>
#include <thread>
#include <utility>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/memory/memory.h"
#include "absl/time/time.h"
#include <grpcpp/grpcpp.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,277 +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/lib/gprpp/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,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.
// 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/lib/gprpp/sync.h"
#include "src/core/lib/gprpp/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,350 +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 <algorithm>
#include <random>
#include <string>
#include <utility>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#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 = &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,159 +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 <thread>
#include <utility>
#include "absl/log/check.h"
#include "absl/memory/memory.h"
#include <grpc/grpc.h>
#include <grpcpp/security/binder_security_policy.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,155 +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 <grpc/support/log.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/lib/gprpp/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,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 "absl/log/check.h"
#include <grpc/grpc.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,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 "test/core/transport/binder/end2end/testing_channel_create.h"
#include <utility>
#include "absl/log/check.h"
#include <grpcpp/security/binder_security_policy.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 <utility>
#include <grpcpp/grpcpp.h>
#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,75 +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 <cassert>
#include <string>
#include <utility>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#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,288 +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 <cassert>
#include <string>
#include <utility>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#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,378 +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 <memory>
#include <string>
#include <thread>
#include <utility>
#include <gtest/gtest.h>
#include "absl/memory/memory.h"
#include <grpc/grpc.h>
#include <grpcpp/security/binder_security_policy.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,267 +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 <string>
#include <utility>
#include <gtest/gtest.h>
#include "absl/memory/memory.h"
#include <grpcpp/impl/grpc_library.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…
Cancel
Save