mirror of https://github.com/grpc/grpc.git
commit
9c7e8df653
321 changed files with 5862 additions and 15494 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,19 @@ |
||||
# This patch works around a problem with Windows RBE described in
|
||||
# https://github.com/bazelbuild/bazel/issues/11636. It can be removed
|
||||
# once that issue is resolved.
|
||||
diff --git a/go/private/rules/binary.bzl b/go/private/rules/binary.bzl
|
||||
index 40a17f4d..2741ad71 100644
|
||||
--- a/go/private/rules/binary.bzl
|
||||
+++ b/go/private/rules/binary.bzl
|
||||
@@ -462,8 +462,9 @@ exit /b %GO_EXIT_CODE%
|
||||
content = cmd,
|
||||
)
|
||||
ctx.actions.run(
|
||||
- executable = bat,
|
||||
- inputs = sdk.headers + sdk.tools + sdk.srcs + ctx.files.srcs + [sdk.go],
|
||||
+ executable = "cmd.exe",
|
||||
+ arguments = ["/S", "/C", bat.path.replace("/", "\\")],
|
||||
+ inputs = sdk.headers + sdk.tools + sdk.srcs + ctx.files.srcs + [sdk.go, bat],
|
||||
outputs = [out, gotmp],
|
||||
mnemonic = "GoToolchainBinaryBuild",
|
||||
)
|
File diff suppressed because it is too large
Load Diff
@ -1,125 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPCPP_CREATE_CHANNEL_BINDER_H |
||||
#define GRPCPP_CREATE_CHANNEL_BINDER_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_ANDROID |
||||
|
||||
#include <grpcpp/channel.h> |
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
#include <grpcpp/support/channel_arguments.h> |
||||
#include <jni.h> |
||||
|
||||
#include <memory> |
||||
|
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
|
||||
/// EXPERIMENTAL Create a new \a Channel based on binder transport. The package
|
||||
/// name and class name will be used identify the specific application component
|
||||
/// to connect to.
|
||||
///
|
||||
/// \param jni_env_void Pointer to a JNIEnv structure
|
||||
/// \param context The context that we will use to invoke \a bindService See
|
||||
/// https://developer.android.com/reference/android/content/Context#bindService(android.content.Intent,%20android.content.ServiceConnection,%20int)
|
||||
/// for detail.
|
||||
/// \param package_name Package name of the component to be connected to
|
||||
/// \param class_name Class name of the component to be connected to
|
||||
/// \param security_policy Used for checking if remote component is allowed to
|
||||
/// connect
|
||||
std::shared_ptr<grpc::Channel> CreateBinderChannel( |
||||
void* jni_env_void, jobject context, absl::string_view package_name, |
||||
absl::string_view class_name, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
|
||||
/// EXPERIMENTAL Create a new \a Channel based on binder transport. The package
|
||||
/// name and class name will be used identify the specific application component
|
||||
/// to connect to.
|
||||
///
|
||||
/// \param jni_env_void Pointer to a JNIEnv structure
|
||||
/// \param context The context that we will use to invoke \a bindService See
|
||||
/// https://developer.android.com/reference/android/content/Context#bindService(android.content.Intent,%20android.content.ServiceConnection,%20int)
|
||||
/// for detail.
|
||||
/// \param package_name Package name of the component to be connected to
|
||||
/// \param class_name Class name of the component to be connected to
|
||||
/// \param security_policy Used for checking if remote component is allowed to
|
||||
/// connect
|
||||
/// \param args Options for channel creation.
|
||||
std::shared_ptr<grpc::Channel> CreateCustomBinderChannel( |
||||
void* jni_env_void, jobject context, absl::string_view package_name, |
||||
absl::string_view class_name, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy, |
||||
const ChannelArguments& args); |
||||
|
||||
/// EXPERIMENTAL Create a new \a Channel based on binder transport.
|
||||
///
|
||||
/// \param jni_env_void Pointer to a JNIEnv structure
|
||||
/// \param context The context that we will use to invoke \a bindService See
|
||||
/// https://developer.android.com/reference/android/content/Context#bindService(android.content.Intent,%20android.content.ServiceConnection,%20int)
|
||||
/// for detail.
|
||||
/// \param uri An URI that can be parsed as an `Intent` with
|
||||
/// https://developer.android.com/reference/android/content/Intent#parseUri(java.lang.String,%20int)
|
||||
/// \param security_policy Used for checking if remote component is allowed to
|
||||
/// connect
|
||||
std::shared_ptr<grpc::Channel> CreateBinderChannel( |
||||
void* jni_env_void, jobject context, absl::string_view uri, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
|
||||
/// EXPERIMENTAL Create a new \a Channel based on binder transport.
|
||||
///
|
||||
/// \param jni_env_void Pointer to a JNIEnv structure
|
||||
/// \param context The context that we will use to invoke \a bindService See
|
||||
/// https://developer.android.com/reference/android/content/Context#bindService(android.content.Intent,%20android.content.ServiceConnection,%20int)
|
||||
/// for detail.
|
||||
/// \param uri An URI that can be parsed as an `Intent` with
|
||||
/// https://developer.android.com/reference/android/content/Intent#parseUri(java.lang.String,%20int)
|
||||
/// \param security_policy Used for checking if remote component is allowed to
|
||||
/// connect
|
||||
/// \param args Options for channel creation.
|
||||
std::shared_ptr<grpc::Channel> CreateCustomBinderChannel( |
||||
void* jni_env_void, jobject context, absl::string_view uri, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy, |
||||
const ChannelArguments& args); |
||||
|
||||
/// EXPERIMENTAL Finds internal binder transport Java code. To create channels
|
||||
/// in threads created in native code, it is required to call this function
|
||||
/// once beforehand in a thread that is not created in native code.
|
||||
/// See
|
||||
/// https://developer.android.com/training/articles/perf-jni#faq:-why-didnt-findclass-find-my-class
|
||||
/// for details of this limitation.
|
||||
/// Returns true when the initialization is successful.
|
||||
bool InitializeBinderChannelJavaClass(void* jni_env_void); |
||||
|
||||
/// EXPERIMENTAL Alternative version of `InitializeBinderChannelJavaClass(void*
|
||||
/// jni_env_void)`. This version used a user-specified function to find the
|
||||
/// required internal Java class. When a class is found, the `class_finder`
|
||||
/// function should return a local reference to the class (jclass type). The
|
||||
/// returned jclass will then be used to create global reference for gRPC to use
|
||||
/// it later. After that, gRPC will DeleteLocalRef the returned local reference.
|
||||
bool InitializeBinderChannelJavaClass( |
||||
void* jni_env_void, std::function<void*(std::string)> class_finder); |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
|
||||
#endif |
||||
|
||||
#endif // GRPCPP_CREATE_CHANNEL_BINDER_H
|
@ -1,43 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPCPP_SECURITY_BINDER_CREDENTIALS_H |
||||
#define GRPCPP_SECURITY_BINDER_CREDENTIALS_H |
||||
|
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
#include <grpcpp/security/server_credentials.h> |
||||
|
||||
#include <memory> |
||||
|
||||
namespace grpc { |
||||
|
||||
class ChannelCredentials; |
||||
|
||||
namespace experimental { |
||||
|
||||
/// EXPERIMENTAL Builds Binder ServerCredentials.
|
||||
///
|
||||
/// This should be used along with `binder:` URI scheme. The path in the URI can
|
||||
/// later be used to access the server's endpoint binder.
|
||||
/// Note that calling \a ServerBuilder::AddListeningPort() with Binder
|
||||
/// ServerCredentials in a non-supported environment will make the subsequent
|
||||
/// call to \a ServerBuilder::BuildAndStart() return a null pointer.
|
||||
std::shared_ptr<grpc::ServerCredentials> BinderServerCredentials( |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
|
||||
#endif // GRPCPP_SECURITY_BINDER_CREDENTIALS_H
|
@ -1,82 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPCPP_SECURITY_BINDER_SECURITY_POLICY_H |
||||
#define GRPCPP_SECURITY_BINDER_SECURITY_POLICY_H |
||||
|
||||
#include <memory> |
||||
|
||||
#ifdef GPR_ANDROID |
||||
|
||||
#include <jni.h> |
||||
|
||||
#endif |
||||
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
namespace binder { |
||||
|
||||
// EXPERIMENTAL Determinines if a connection is allowed to be
|
||||
// established on Android. See https://source.android.com/security/app-sandbox
|
||||
// for more info about UID.
|
||||
class SecurityPolicy { |
||||
public: |
||||
virtual ~SecurityPolicy() = default; |
||||
// Returns true if the UID is authorized to connect.
|
||||
// Must return the same value for the same inputs so callers can safely cache
|
||||
// the result.
|
||||
virtual bool IsAuthorized(int uid) = 0; |
||||
}; |
||||
|
||||
// EXPERIMENTAL Allows all connection. Anything on the Android device will be
|
||||
// able to connect, use with caution!
|
||||
class UntrustedSecurityPolicy : public SecurityPolicy { |
||||
public: |
||||
UntrustedSecurityPolicy(); |
||||
~UntrustedSecurityPolicy() override; |
||||
bool IsAuthorized(int uid) override; |
||||
}; |
||||
|
||||
// EXPERIMENTAL Only allows the connections from processes with the same UID. In
|
||||
// most cases this means "from the same APK".
|
||||
class InternalOnlySecurityPolicy : public SecurityPolicy { |
||||
public: |
||||
InternalOnlySecurityPolicy(); |
||||
~InternalOnlySecurityPolicy() override; |
||||
bool IsAuthorized(int uid) override; |
||||
}; |
||||
|
||||
#ifdef GPR_ANDROID |
||||
|
||||
// EXPERIMENTAL Only allows the connections from the APK that have the same
|
||||
// signature.
|
||||
class SameSignatureSecurityPolicy : public SecurityPolicy { |
||||
public: |
||||
// `context` is required for getting PackageManager Java class
|
||||
SameSignatureSecurityPolicy(JavaVM* jvm, jobject context); |
||||
~SameSignatureSecurityPolicy() override; |
||||
bool IsAuthorized(int uid) override; |
||||
|
||||
private: |
||||
JavaVM* jvm_; |
||||
jobject context_; |
||||
}; |
||||
|
||||
#endif |
||||
|
||||
} // namespace binder
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
|
||||
#endif // GRPCPP_SECURITY_BINDER_SECURITY_POLICY_H
|
@ -1,10 +0,0 @@ |
||||
# Binder transport for cross process IPC on Android |
||||
|
||||
EXPERIMENTAL. API stability not guaranteed. |
||||
|
||||
This transport implements |
||||
[BinderChannel for native cross-process communication on Android](https://github.com/grpc/proposal/blob/master/L73-java-binderchannel.md) and enables C++/Java cross-process communication on Android with gRPC. |
||||
|
||||
Tests: https://github.com/grpc/grpc/tree/master/test/core/transport/binder/ |
||||
|
||||
Example apps: https://github.com/grpc/grpc/tree/master/examples/android/binder/java/io/grpc/binder/cpp |
@ -1,134 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include "src/core/ext/transport/binder/client/binder_connector.h" |
||||
#include "src/core/lib/iomgr/port.h" |
||||
|
||||
#ifdef GRPC_HAVE_UNIX_SOCKET |
||||
#ifdef GPR_WINDOWS |
||||
// clang-format off
|
||||
#include <ws2def.h> |
||||
#include <afunix.h> |
||||
// clang-format on
|
||||
#else |
||||
#include <sys/un.h> |
||||
#endif // GPR_WINDOWS
|
||||
#endif |
||||
|
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
|
||||
#include <functional> |
||||
#include <map> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "src/core/client_channel/connector.h" |
||||
#include "src/core/client_channel/subchannel.h" |
||||
#include "src/core/ext/transport/binder/client/endpoint_binder_pool.h" |
||||
#include "src/core/ext/transport/binder/client/security_policy_setting.h" |
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
|
||||
namespace { |
||||
|
||||
// TODO(mingcl): Currently this does no error handling and assumes the
|
||||
// connection always succeeds in reasonable amount of time.
|
||||
class BinderConnector : public grpc_core::SubchannelConnector { |
||||
public: |
||||
BinderConnector() {} |
||||
~BinderConnector() override {} |
||||
void Connect(const Args& args, Result* result, |
||||
grpc_closure* notify) override { |
||||
#ifdef GRPC_HAVE_UNIX_SOCKET |
||||
{ |
||||
struct sockaddr_un* un = |
||||
reinterpret_cast<struct sockaddr_un*>(args.address->addr); |
||||
// length of identifier, including null terminator
|
||||
size_t id_length = args.address->len - sizeof(un->sun_family); |
||||
// The c-style string at least will have a null terminator, and the
|
||||
// connection id itself should not be empty
|
||||
CHECK_GE(id_length, 2u); |
||||
// Make sure there is null terminator at the expected location before
|
||||
// reading from it
|
||||
CHECK_EQ(un->sun_path[id_length - 1], '\0'); |
||||
conn_id_ = un->sun_path; |
||||
} |
||||
#else |
||||
CHECK(0); |
||||
#endif |
||||
LOG(INFO) << "BinderConnector " << this << " conn_id_ = " << conn_id_; |
||||
|
||||
args_ = args; |
||||
CHECK_EQ(notify_, nullptr); |
||||
CHECK_NE(notify, nullptr); |
||||
notify_ = notify; |
||||
result_ = result; |
||||
|
||||
Ref().release(); // Ref held by the following callback
|
||||
|
||||
grpc_binder::GetEndpointBinderPool()->GetEndpointBinder( |
||||
conn_id_, |
||||
std::bind(&BinderConnector::OnConnected, this, std::placeholders::_1)); |
||||
} |
||||
|
||||
void OnConnected(std::unique_ptr<grpc_binder::Binder> endpoint_binder) { |
||||
CHECK(endpoint_binder != nullptr); |
||||
grpc_core::Transport* transport = grpc_create_binder_transport_client( |
||||
std::move(endpoint_binder), |
||||
grpc_binder::GetSecurityPolicySetting()->Get(conn_id_)); |
||||
CHECK_NE(transport, nullptr); |
||||
result_->channel_args = args_.channel_args; |
||||
result_->transport = transport; |
||||
|
||||
CHECK_NE(notify_, nullptr); |
||||
// ExecCtx is required here for running grpc_closure because this callback
|
||||
// might be invoked from non-gRPC code
|
||||
if (grpc_core::ExecCtx::Get() == nullptr) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, notify_, absl::OkStatus()); |
||||
} else { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, notify_, absl::OkStatus()); |
||||
} |
||||
|
||||
Unref(); // Was referenced in BinderConnector::Connect
|
||||
} |
||||
void Shutdown(grpc_error_handle /*error*/) override {} |
||||
|
||||
private: |
||||
Args args_; |
||||
grpc_closure* notify_ = nullptr; |
||||
Result* result_ = nullptr; |
||||
|
||||
std::string conn_id_; |
||||
}; |
||||
|
||||
} // namespace
|
||||
|
||||
namespace grpc_core { |
||||
|
||||
RefCountedPtr<Subchannel> BinderClientChannelFactory::CreateSubchannel( |
||||
const grpc_resolved_address& address, const ChannelArgs& args) { |
||||
LOG(INFO) << "BinderClientChannelFactory creating subchannel " << this; |
||||
return Subchannel::Create( |
||||
MakeOrphanable<BinderConnector>(), address, |
||||
args.Set(GRPC_ARG_DEFAULT_AUTHORITY, "binder.authority")); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_NO_BINDER
|
@ -1,41 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_BINDER_CONNECTOR_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_BINDER_CONNECTOR_H |
||||
|
||||
#include <grpc/impl/grpc_types.h> |
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpcpp/channel.h> |
||||
#include <grpcpp/support/channel_arguments.h> |
||||
|
||||
#include <memory> |
||||
#include <utility> |
||||
|
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/strings/strip.h" |
||||
#include "src/core/client_channel/client_channel_factory.h" |
||||
#include "src/core/client_channel/client_channel_filter.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
class BinderClientChannelFactory : public ClientChannelFactory { |
||||
public: |
||||
RefCountedPtr<Subchannel> CreateSubchannel( |
||||
const grpc_resolved_address& address, const ChannelArgs& args) override; |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_BINDER_CONNECTOR_H
|
@ -1,231 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <grpcpp/create_channel_binder.h> |
||||
|
||||
// The interface is only defined if GPR_ANDROID is defined, because some
|
||||
// arguments requires JNI.
|
||||
// Furthermore, the interface is non-phony only when
|
||||
// GPR_SUPPORT_BINDER_TRANSPORT is true because actual implementation of binder
|
||||
// transport requires newer version of NDK API
|
||||
|
||||
#ifdef GPR_ANDROID |
||||
|
||||
#include <grpc/grpc.h> |
||||
#include <grpc/grpc_posix.h> |
||||
|
||||
#include "src/core/util/crash.h" |
||||
|
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpcpp/impl/grpc_library.h> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/memory/memory.h" |
||||
#include "absl/strings/substitute.h" |
||||
#include "absl/time/clock.h" |
||||
#include "absl/time/time.h" |
||||
#include "src/core/client_channel/client_channel_filter.h" |
||||
#include "src/core/ext/transport/binder/client/channel_create_impl.h" |
||||
#include "src/core/ext/transport/binder/client/connection_id_generator.h" |
||||
#include "src/core/ext/transport/binder/client/endpoint_binder_pool.h" |
||||
#include "src/core/ext/transport/binder/client/jni_utils.h" |
||||
#include "src/core/ext/transport/binder/client/security_policy_setting.h" |
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder_android.h" |
||||
#include "src/core/lib/channel/channel_args.h" |
||||
#include "src/core/lib/config/core_configuration.h" |
||||
#include "src/core/lib/surface/channel.h" |
||||
#include "src/core/lib/transport/transport.h" |
||||
#include "src/cpp/client/create_channel_internal.h" |
||||
|
||||
namespace { |
||||
// grpc.io.action.BIND is the standard action name for binding to binder
|
||||
// transport server.
|
||||
const char* kStandardActionName = "grpc.io.action.BIND"; |
||||
} // namespace
|
||||
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
|
||||
std::shared_ptr<grpc::Channel> CreateBinderChannel( |
||||
void* jni_env_void, jobject context, absl::string_view package_name, |
||||
absl::string_view class_name, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) { |
||||
return CreateCustomBinderChannel(jni_env_void, context, package_name, |
||||
class_name, security_policy, |
||||
ChannelArguments()); |
||||
} |
||||
|
||||
std::shared_ptr<grpc::Channel> CreateCustomBinderChannel( |
||||
void* jni_env_void, jobject context, absl::string_view package_name, |
||||
absl::string_view class_name, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy, |
||||
const ChannelArguments& args) { |
||||
return CreateCustomBinderChannel( |
||||
jni_env_void, context, |
||||
absl::Substitute("android-app://$0#Intent;action=$1;component=$0/$2;end", |
||||
package_name, kStandardActionName, class_name), |
||||
security_policy, args); |
||||
} |
||||
|
||||
std::shared_ptr<grpc::Channel> CreateBinderChannel( |
||||
void* jni_env_void, jobject context, absl::string_view uri, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) { |
||||
return CreateCustomBinderChannel(jni_env_void, context, uri, security_policy, |
||||
ChannelArguments()); |
||||
} |
||||
|
||||
std::shared_ptr<grpc::Channel> CreateCustomBinderChannel( |
||||
void* jni_env_void, jobject context, absl::string_view uri, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy, |
||||
const ChannelArguments& args) { |
||||
grpc_init(); |
||||
|
||||
CHECK_NE(jni_env_void, nullptr); |
||||
CHECK_NE(security_policy, nullptr); |
||||
|
||||
// Generate an unique connection ID that identifies this connection (Useful
|
||||
// for mapping connection between Java and C++ code).
|
||||
std::string connection_id = |
||||
grpc_binder::GetConnectionIdGenerator()->Generate(uri); |
||||
|
||||
LOG(ERROR) << "connection id is " << connection_id; |
||||
|
||||
// After invoking this Java method, Java code will put endpoint binder into
|
||||
// `EndpointBinderPool` after the connection succeeds
|
||||
// TODO(mingcl): Consider if we want to delay the connection establishment
|
||||
// until SubchannelConnector start establishing connection. For now we don't
|
||||
// see any benifits doing that.
|
||||
grpc_binder::TryEstablishConnectionWithUri(static_cast<JNIEnv*>(jni_env_void), |
||||
context, uri, connection_id); |
||||
|
||||
grpc_binder::GetSecurityPolicySetting()->Set(connection_id, security_policy); |
||||
|
||||
// Set server URI to a URI that contains connection id. The URI will be used
|
||||
// by subchannel connector to obtain correct endpoint binder from
|
||||
// `EndpointBinderPool`.
|
||||
string server_uri = "binder:" + connection_id; |
||||
|
||||
grpc_channel_args channel_args; |
||||
args.SetChannelArgs(&channel_args); |
||||
|
||||
return CreateChannelInternal( |
||||
"", |
||||
grpc::internal::CreateClientBinderChannelImpl(server_uri, &channel_args), |
||||
std::vector< |
||||
std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>()); |
||||
} |
||||
|
||||
bool InitializeBinderChannelJavaClass(void* jni_env_void) { |
||||
return grpc_binder::FindNativeConnectionHelper( |
||||
static_cast<JNIEnv*>(jni_env_void)) != nullptr; |
||||
} |
||||
|
||||
bool InitializeBinderChannelJavaClass( |
||||
void* jni_env_void, std::function<void*(std::string)> class_finder) { |
||||
return grpc_binder::FindNativeConnectionHelper( |
||||
static_cast<JNIEnv*>(jni_env_void), class_finder) != nullptr; |
||||
} |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
|
||||
#else // !GPR_SUPPORT_BINDER_TRANSPORT
|
||||
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
|
||||
std::shared_ptr<grpc::Channel> CreateBinderChannel( |
||||
void*, jobject, absl::string_view, absl::string_view, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy>) { |
||||
LOG(ERROR) << "This APK is compiled with Android API level = " |
||||
<< __ANDROID_API__ |
||||
<< ", which is not supported. See port_platform.h for supported " |
||||
"versions."; |
||||
CHECK(0); |
||||
return {}; |
||||
} |
||||
|
||||
std::shared_ptr<grpc::Channel> CreateCustomBinderChannel( |
||||
void*, jobject, absl::string_view, absl::string_view, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy>, |
||||
const ChannelArguments&) { |
||||
LOG(ERROR) << "This APK is compiled with Android API level = " |
||||
<< __ANDROID_API__ |
||||
<< ", which is not supported. See port_platform.h for supported " |
||||
"versions."; |
||||
CHECK(0); |
||||
return {}; |
||||
} |
||||
|
||||
std::shared_ptr<grpc::Channel> CreateBinderChannel( |
||||
void*, jobject, absl::string_view, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy>) { |
||||
LOG(ERROR) << "This APK is compiled with Android API level = " |
||||
<< __ANDROID_API__ |
||||
<< ", which is not supported. See port_platform.h for supported " |
||||
"versions."; |
||||
CHECK(0); |
||||
return {}; |
||||
} |
||||
|
||||
std::shared_ptr<grpc::Channel> CreateCustomBinderChannel( |
||||
void*, jobject, absl::string_view, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy>, |
||||
const ChannelArguments&) { |
||||
LOG(ERROR) << "This APK is compiled with Android API level = " |
||||
<< __ANDROID_API__ |
||||
<< ", which is not supported. See port_platform.h for supported " |
||||
"versions."; |
||||
CHECK(0); |
||||
return {}; |
||||
} |
||||
|
||||
bool InitializeBinderChannelJavaClass(void* jni_env_void) { |
||||
LOG(ERROR) << "This APK is compiled with Android API level = " |
||||
<< __ANDROID_API__ |
||||
<< ", which is not supported. See port_platform.h for supported " |
||||
"versions."; |
||||
CHECK(0); |
||||
return {}; |
||||
} |
||||
|
||||
bool InitializeBinderChannelJavaClass( |
||||
void* jni_env_void, std::function<void*(std::string)> class_finder) { |
||||
LOG(ERROR) << "This APK is compiled with Android API level = " |
||||
<< __ANDROID_API__ |
||||
<< ", which is not supported. See port_platform.h for supported " |
||||
"versions."; |
||||
CHECK(0); |
||||
return {}; |
||||
} |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
|
||||
#endif // GPR_SUPPORT_BINDER_TRANSPORT
|
||||
|
||||
#endif // GPR_ANDROID
|
||||
|
||||
#endif // GRPC_NO_BINDER
|
@ -1,93 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/core/ext/transport/binder/client/channel_create_impl.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <memory> |
||||
#include <utility> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "src/core/ext/transport/binder/client/binder_connector.h" |
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/lib/channel/channel_args.h" |
||||
#include "src/core/lib/config/core_configuration.h" |
||||
#include "src/core/lib/surface/channel.h" |
||||
#include "src/core/lib/surface/channel_create.h" |
||||
|
||||
namespace { |
||||
|
||||
grpc_core::BinderClientChannelFactory* g_factory; |
||||
gpr_once g_factory_once = GPR_ONCE_INIT; |
||||
|
||||
void FactoryInit() { g_factory = new grpc_core::BinderClientChannelFactory(); } |
||||
} // namespace
|
||||
|
||||
namespace grpc { |
||||
namespace internal { |
||||
|
||||
grpc_channel* CreateDirectBinderChannelImplForTesting( |
||||
std::unique_ptr<grpc_binder::Binder> endpoint_binder, |
||||
const grpc_channel_args* args, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
|
||||
grpc_core::Transport* transport = grpc_create_binder_transport_client( |
||||
std::move(endpoint_binder), security_policy); |
||||
CHECK_NE(transport, nullptr); |
||||
|
||||
auto channel_args = grpc_core::CoreConfiguration::Get() |
||||
.channel_args_preconditioning() |
||||
.PreconditionChannelArgs(args) |
||||
.Set(GRPC_ARG_DEFAULT_AUTHORITY, "binder.authority"); |
||||
auto channel = |
||||
grpc_core::ChannelCreate("binder_target_placeholder", channel_args, |
||||
GRPC_CLIENT_DIRECT_CHANNEL, transport); |
||||
// TODO(mingcl): Handle error properly
|
||||
CHECK(channel.ok()); |
||||
grpc_channel_args_destroy(args); |
||||
return channel->release()->c_ptr(); |
||||
} |
||||
|
||||
grpc_channel* CreateClientBinderChannelImpl(std::string target, |
||||
const grpc_channel_args* args) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
|
||||
gpr_once_init(&g_factory_once, FactoryInit); |
||||
|
||||
auto channel_args = grpc_core::CoreConfiguration::Get() |
||||
.channel_args_preconditioning() |
||||
.PreconditionChannelArgs(args) |
||||
.SetObject(g_factory); |
||||
|
||||
auto channel = grpc_core::ChannelCreate(target, channel_args, |
||||
GRPC_CLIENT_CHANNEL, nullptr); |
||||
|
||||
if (!channel.ok()) { |
||||
return grpc_lame_client_channel_create( |
||||
target.c_str(), static_cast<grpc_status_code>(channel.status().code()), |
||||
"Failed to create binder channel"); |
||||
} |
||||
|
||||
return channel->release()->c_ptr(); |
||||
} |
||||
|
||||
} // namespace internal
|
||||
} // namespace grpc
|
||||
#endif |
@ -1,42 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_CHANNEL_CREATE_IMPL_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_CHANNEL_CREATE_IMPL_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
|
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/lib/channel/channel_args.h" |
||||
|
||||
namespace grpc { |
||||
namespace internal { |
||||
|
||||
// Creates a GRPC_CLIENT_DIRECT_CHANNEL channel from endpoint binder
|
||||
// At this moment this is only used for testing.
|
||||
grpc_channel* CreateDirectBinderChannelImplForTesting( |
||||
std::unique_ptr<grpc_binder::Binder> endpoint_binder, |
||||
const grpc_channel_args* args, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
|
||||
// Creates a GRPC_CLIENT_CHANNEL channel
|
||||
grpc_channel* CreateClientBinderChannelImpl(std::string target, |
||||
const grpc_channel_args* args); |
||||
|
||||
} // namespace internal
|
||||
} // namespace grpc
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_CHANNEL_CREATE_IMPL_H
|
@ -1,69 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/core/ext/transport/binder/client/connection_id_generator.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/strings/str_cat.h" |
||||
|
||||
namespace { |
||||
// Make sure `s` does not contain characters other than numbers, alphabets,
|
||||
// period and underscore
|
||||
std::string Normalize(absl::string_view str_view) { |
||||
std::string s = std::string(str_view); |
||||
for (size_t i = 0; i < s.length(); i++) { |
||||
if (!isalnum(s[i]) && s[i] != '.') { |
||||
s[i] = '_'; |
||||
} |
||||
} |
||||
return s; |
||||
} |
||||
|
||||
// Remove prefix of the string if the string is longer than len
|
||||
std::string StripToLength(const std::string& s, size_t len) { |
||||
if (s.length() > len) { |
||||
return s.substr(s.length() - len, len); |
||||
} |
||||
return s; |
||||
} |
||||
} // namespace
|
||||
|
||||
namespace grpc_binder { |
||||
|
||||
std::string ConnectionIdGenerator::Generate(absl::string_view uri) { |
||||
// reserve some room for serial number
|
||||
const size_t kReserveForNumbers = 15; |
||||
std::string s = |
||||
StripToLength(Normalize(uri), kPathLengthLimit - kReserveForNumbers); |
||||
std::string ret; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
// Insert a hyphen before serial number
|
||||
ret = absl::StrCat(s, "-", ++count_); |
||||
} |
||||
CHECK_LT(ret.length(), kPathLengthLimit); |
||||
return ret; |
||||
} |
||||
|
||||
ConnectionIdGenerator* GetConnectionIdGenerator() { |
||||
static ConnectionIdGenerator* cig = new ConnectionIdGenerator(); |
||||
return cig; |
||||
} |
||||
|
||||
} // namespace grpc_binder
|
||||
#endif |
@ -1,50 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_CONNECTION_ID_GENERATOR_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_CONNECTION_ID_GENERATOR_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <map> |
||||
|
||||
#include "absl/strings/string_view.h" |
||||
#include "src/core/util/sync.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
// Generates somewhat human-readable unique identifiers from package name and
|
||||
// class name. We will generate a Id that only contains unreserved URI
|
||||
// characters (uppercase and lowercase letters, decimal digits, hyphen, period,
|
||||
// underscore, and tilde).
|
||||
class ConnectionIdGenerator { |
||||
public: |
||||
std::string Generate(absl::string_view uri); |
||||
|
||||
private: |
||||
// Our generated Id need to fit in unix socket path length limit. We use 100
|
||||
// here to be safe.
|
||||
const size_t kPathLengthLimit = 100; |
||||
|
||||
grpc_core::Mutex m_; |
||||
// Every generated identifier will followed by the value of this counter to
|
||||
// make sure every generated id is unique.
|
||||
int count_ ABSL_GUARDED_BY(m_); |
||||
}; |
||||
|
||||
ConnectionIdGenerator* GetConnectionIdGenerator(); |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_CONNECTION_ID_GENERATOR_H
|
@ -1,114 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/core/ext/transport/binder/client/endpoint_binder_pool.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include "src/core/ext/transport/binder/client/jni_utils.h" |
||||
|
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
|
||||
#include <jni.h> |
||||
|
||||
#include "src/core/ext/transport/binder/wire_format/binder_android.h" |
||||
|
||||
extern "C" { |
||||
// Adds endpoint binder to binder pool when Java notify us that the endpoint
|
||||
// binder is ready. This is called from GrpcBinderConnection.java
|
||||
JNIEXPORT void JNICALL |
||||
Java_io_grpc_binder_cpp_GrpcBinderConnection_notifyConnected__Ljava_lang_String_2Landroid_os_IBinder_2( |
||||
JNIEnv* jni_env, jobject, jstring conn_id_jstring, jobject ibinder) { |
||||
jboolean isCopy; |
||||
const char* conn_id = jni_env->GetStringUTFChars(conn_id_jstring, &isCopy); |
||||
LOG(INFO) << __func__ << " invoked with conn_id = " << conn_id; |
||||
CHECK_NE(ibinder, nullptr); |
||||
grpc_binder::ndk_util::SpAIBinder aibinder = |
||||
grpc_binder::FromJavaBinder(jni_env, ibinder); |
||||
LOG(INFO) << __func__ << " got aibinder = " << aibinder.get(); |
||||
auto b = std::make_unique<grpc_binder::BinderAndroid>(aibinder); |
||||
CHECK(b != nullptr); |
||||
grpc_binder::GetEndpointBinderPool()->AddEndpointBinder(conn_id, |
||||
std::move(b)); |
||||
if (isCopy == JNI_TRUE) { |
||||
jni_env->ReleaseStringUTFChars(conn_id_jstring, conn_id); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif // GPR_SUPPORT_BINDER_TRANSPORT
|
||||
|
||||
namespace grpc_binder { |
||||
|
||||
void EndpointBinderPool::GetEndpointBinder( |
||||
std::string conn_id, |
||||
std::function<void(std::unique_ptr<grpc_binder::Binder>)> cb) { |
||||
LOG(INFO) << "EndpointBinder requested. conn_id = " << conn_id; |
||||
std::unique_ptr<grpc_binder::Binder> b; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
if (binder_map_.count(conn_id)) { |
||||
b = std::move(binder_map_[conn_id]); |
||||
binder_map_.erase(conn_id); |
||||
CHECK(b != nullptr); |
||||
} else { |
||||
if (pending_requests_.count(conn_id) != 0) { |
||||
LOG(ERROR) << "Duplicate GetEndpointBinder requested. conn_id = " |
||||
<< conn_id; |
||||
return; |
||||
} |
||||
pending_requests_[conn_id] = std::move(cb); |
||||
return; |
||||
} |
||||
} |
||||
CHECK(b != nullptr); |
||||
cb(std::move(b)); |
||||
} |
||||
|
||||
void EndpointBinderPool::AddEndpointBinder( |
||||
std::string conn_id, std::unique_ptr<grpc_binder::Binder> b) { |
||||
LOG(INFO) << "EndpointBinder added. conn_id = " << conn_id; |
||||
CHECK(b != nullptr); |
||||
// cb will be set in the following block if there is a pending callback
|
||||
std::function<void(std::unique_ptr<grpc_binder::Binder>)> cb = nullptr; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
if (binder_map_.count(conn_id) != 0) { |
||||
LOG(ERROR) << "EndpointBinder already in the pool. conn_id = " << conn_id; |
||||
return; |
||||
} |
||||
if (pending_requests_.count(conn_id)) { |
||||
cb = std::move(pending_requests_[conn_id]); |
||||
pending_requests_.erase(conn_id); |
||||
} else { |
||||
binder_map_[conn_id] = std::move(b); |
||||
b = nullptr; |
||||
} |
||||
} |
||||
if (cb != nullptr) { |
||||
cb(std::move(b)); |
||||
} |
||||
} |
||||
|
||||
EndpointBinderPool* GetEndpointBinderPool() { |
||||
static EndpointBinderPool* p = new EndpointBinderPool(); |
||||
return p; |
||||
} |
||||
} // namespace grpc_binder
|
||||
#endif |
@ -1,64 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_ENDPOINT_BINDER_POOL_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_ENDPOINT_BINDER_POOL_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <functional> |
||||
#include <string> |
||||
|
||||
#include "absl/container/flat_hash_map.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/util/sync.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
// This class serves as a buffer of endpoint binders between C++ and
|
||||
// Java. `AddEndpointBinder` will be indirectly invoked by Java code, and
|
||||
// `GetEndpointBinder` is for C++ code to register callback to get endpoint
|
||||
// binder when become available. This simplifies JNI related threading issues
|
||||
// since both side only need to interact with this buffer in non-blocking
|
||||
// manner and avoids cross-language callbacks.
|
||||
class EndpointBinderPool { |
||||
public: |
||||
// Invokes the callback when the binder corresponding to the conn_id become
|
||||
// available. If the binder is already available, invokes the callback
|
||||
// immediately.
|
||||
// Ownership of the endpoint binder will be transferred to the callback
|
||||
// function and it will be removed from the pool
|
||||
void GetEndpointBinder( |
||||
std::string conn_id, |
||||
std::function<void(std::unique_ptr<grpc_binder::Binder>)> cb); |
||||
|
||||
// Add an endpoint binder to the pool
|
||||
void AddEndpointBinder(std::string conn_id, |
||||
std::unique_ptr<grpc_binder::Binder> b); |
||||
|
||||
private: |
||||
grpc_core::Mutex m_; |
||||
absl::flat_hash_map<std::string, std::unique_ptr<grpc_binder::Binder>> |
||||
binder_map_ ABSL_GUARDED_BY(m_); |
||||
absl::flat_hash_map<std::string, |
||||
std::function<void(std::unique_ptr<grpc_binder::Binder>)>> |
||||
pending_requests_ ABSL_GUARDED_BY(m_); |
||||
}; |
||||
|
||||
// Returns the singleton
|
||||
EndpointBinderPool* GetEndpointBinderPool(); |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_ENDPOINT_BINDER_POOL_H
|
@ -1,138 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/core/ext/transport/binder/client/jni_utils.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include "src/core/util/crash.h" |
||||
|
||||
#if defined(ANDROID) || defined(__ANDROID__) |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
jclass FindNativeConnectionHelper(JNIEnv* env) { |
||||
return FindNativeConnectionHelper( |
||||
env, [env](std::string cl) { return env->FindClass(cl.c_str()); }); |
||||
} |
||||
|
||||
jclass FindNativeConnectionHelper( |
||||
JNIEnv* env, std::function<void*(std::string)> class_finder) { |
||||
auto do_find = [env, class_finder]() { |
||||
jclass cl = static_cast<jclass>( |
||||
class_finder("io/grpc/binder/cpp/NativeConnectionHelper")); |
||||
if (cl == nullptr) { |
||||
return cl; |
||||
} |
||||
jclass global_cl = static_cast<jclass>(env->NewGlobalRef(cl)); |
||||
env->DeleteLocalRef(cl); |
||||
CHECK_NE(global_cl, nullptr); |
||||
return global_cl; |
||||
}; |
||||
static jclass connection_helper_class = do_find(); |
||||
if (connection_helper_class != nullptr) { |
||||
return connection_helper_class; |
||||
} |
||||
// Some possible reasons:
|
||||
// * There is no Java class in the call stack and this is not invoked
|
||||
// from JNI_OnLoad
|
||||
// * The APK does not correctly depends on the helper class, or the
|
||||
// class get shrinked
|
||||
LOG(ERROR) |
||||
<< "Cannot find binder transport Java helper class. Did you invoke " |
||||
"grpc::experimental::InitializeBinderChannelJavaClass correctly " |
||||
"beforehand? Did the APK correctly include the connection helper " |
||||
"class (i.e depends on build target " |
||||
"src/core/ext/transport/binder/java/io/grpc/binder/" |
||||
"cpp:connection_helper) ?"; |
||||
// TODO(mingcl): Maybe it is worth to try again so the failure can be fixed
|
||||
// by invoking this function again at a different thread.
|
||||
return nullptr; |
||||
} |
||||
|
||||
void TryEstablishConnection(JNIEnv* env, jobject application, |
||||
absl::string_view pkg, absl::string_view cls, |
||||
absl::string_view action_name, |
||||
absl::string_view conn_id) { |
||||
std::string method = "tryEstablishConnection"; |
||||
std::string type = |
||||
"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/" |
||||
"lang/String;Ljava/lang/String;)V"; |
||||
|
||||
jclass cl = FindNativeConnectionHelper(env); |
||||
if (cl == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
jmethodID mid = env->GetStaticMethodID(cl, method.c_str(), type.c_str()); |
||||
if (mid == nullptr) { |
||||
LOG(ERROR) << "No method id " << method; |
||||
} |
||||
|
||||
env->CallStaticVoidMethod(cl, mid, application, |
||||
env->NewStringUTF(std::string(pkg).c_str()), |
||||
env->NewStringUTF(std::string(cls).c_str()), |
||||
env->NewStringUTF(std::string(action_name).c_str()), |
||||
env->NewStringUTF(std::string(conn_id).c_str())); |
||||
} |
||||
|
||||
void TryEstablishConnectionWithUri(JNIEnv* env, jobject application, |
||||
absl::string_view uri, |
||||
absl::string_view conn_id) { |
||||
std::string method = "tryEstablishConnectionWithUri"; |
||||
std::string type = |
||||
"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V"; |
||||
|
||||
jclass cl = FindNativeConnectionHelper(env); |
||||
if (cl == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
jmethodID mid = env->GetStaticMethodID(cl, method.c_str(), type.c_str()); |
||||
if (mid == nullptr) { |
||||
LOG(ERROR) << "No method id " << method; |
||||
} |
||||
|
||||
env->CallStaticVoidMethod(cl, mid, application, |
||||
env->NewStringUTF(std::string(uri).c_str()), |
||||
env->NewStringUTF(std::string(conn_id).c_str())); |
||||
} |
||||
|
||||
bool IsSignatureMatch(JNIEnv* env, jobject context, int uid1, int uid2) { |
||||
const std::string method = "isSignatureMatch"; |
||||
const std::string type = "(Landroid/content/Context;II)Z"; |
||||
|
||||
jclass cl = FindNativeConnectionHelper(env); |
||||
if (cl == nullptr) { |
||||
return false; |
||||
} |
||||
|
||||
jmethodID mid = env->GetStaticMethodID(cl, method.c_str(), type.c_str()); |
||||
if (mid == nullptr) { |
||||
LOG(ERROR) << "No method id " << method; |
||||
} |
||||
|
||||
jboolean result = env->CallStaticBooleanMethod(cl, mid, context, uid1, uid2); |
||||
return result == JNI_TRUE; |
||||
} |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif |
||||
#endif |
@ -1,57 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_JNI_UTILS_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_JNI_UTILS_H |
||||
|
||||
#if defined(ANDROID) || defined(__ANDROID__) |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
#include <jni.h> |
||||
|
||||
#include <functional> |
||||
#include <string> |
||||
|
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
// Finds NativeConnectionHelper Java class and caches it. This is useful because
|
||||
// FindClass only works when there is a Java class in the call stack. Typically
|
||||
// user might want to call this once in a place that is called from Java (ex.
|
||||
// JNI_OnLoad) so subsequent BinderTransport code can find Java class
|
||||
jclass FindNativeConnectionHelper(JNIEnv* env); |
||||
|
||||
jclass FindNativeConnectionHelper( |
||||
JNIEnv* env, std::function<void*(std::string)> class_finder); |
||||
|
||||
// Calls Java method NativeConnectionHelper.tryEstablishConnection
|
||||
void TryEstablishConnection(JNIEnv* env, jobject application, |
||||
absl::string_view pkg, absl::string_view cls, |
||||
absl::string_view action_name, |
||||
absl::string_view conn_id); |
||||
|
||||
void TryEstablishConnectionWithUri(JNIEnv* env, jobject application, |
||||
absl::string_view uri, |
||||
absl::string_view conn_id); |
||||
|
||||
// Calls Java method NativeConnectionHelper.isSignatureMatch.
|
||||
// Will also return false if failed to invoke Java.
|
||||
bool IsSignatureMatch(JNIEnv* env, jobject context, int uid1, int uid2); |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif |
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_JNI_UTILS_H
|
@ -1,47 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "absl/log/check.h" |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include "src/core/ext/transport/binder/client/security_policy_setting.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
void SecurityPolicySetting::Set( |
||||
absl::string_view connection_id, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) { |
||||
grpc_core::MutexLock l(&m_); |
||||
CHECK_EQ(security_policy_map_.count(std::string(connection_id)), 0u); |
||||
security_policy_map_[std::string(connection_id)] = security_policy; |
||||
} |
||||
|
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
SecurityPolicySetting::Get(absl::string_view connection_id) { |
||||
grpc_core::MutexLock l(&m_); |
||||
CHECK_NE(security_policy_map_.count(std::string(connection_id)), 0u); |
||||
return security_policy_map_[std::string(connection_id)]; |
||||
} |
||||
|
||||
SecurityPolicySetting* GetSecurityPolicySetting() { |
||||
static SecurityPolicySetting* s = new SecurityPolicySetting(); |
||||
return s; |
||||
} |
||||
|
||||
} // namespace grpc_binder
|
||||
#endif |
@ -1,49 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_SECURITY_POLICY_SETTING_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_SECURITY_POLICY_SETTING_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
|
||||
#include "absl/container/flat_hash_map.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "src/core/util/sync.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
// A singleton class for setting security setting for each connection. This is
|
||||
// required because we cannot pass security policy shared pointers around using
|
||||
// gRPC arguments, we can only pass connection_id around as part of URI
|
||||
class SecurityPolicySetting { |
||||
public: |
||||
void Set(absl::string_view connection_id, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> Get( |
||||
absl::string_view connection_id); |
||||
|
||||
private: |
||||
grpc_core::Mutex m_; |
||||
absl::flat_hash_map< |
||||
std::string, std::shared_ptr<grpc::experimental::binder::SecurityPolicy>> |
||||
security_policy_map_ ABSL_GUARDED_BY(m_); |
||||
}; |
||||
|
||||
SecurityPolicySetting* GetSecurityPolicySetting(); |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_CLIENT_SECURITY_POLICY_SETTING_H
|
@ -1,30 +0,0 @@ |
||||
# Copyright 2021 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
# copybara: Import internal android_library rule here |
||||
|
||||
licenses(["notice"]) |
||||
|
||||
android_library( |
||||
name = "connection_helper", |
||||
srcs = [ |
||||
"GrpcBinderConnection.java", |
||||
"GrpcCppServerBuilder.java", |
||||
"NativeConnectionHelper.java", |
||||
], |
||||
visibility = ["//visibility:public"], |
||||
deps = [ |
||||
# copybara: Add proguard dependency here |
||||
], |
||||
) |
@ -1,104 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package io.grpc.binder.cpp; |
||||
|
||||
import static android.content.Intent.URI_ANDROID_APP_SCHEME; |
||||
import static android.content.Intent.URI_INTENT_SCHEME; |
||||
|
||||
import android.annotation.TargetApi; |
||||
import android.content.ComponentName; |
||||
import android.content.Context; |
||||
import android.content.Intent; |
||||
import android.content.ServiceConnection; |
||||
import android.os.IBinder; |
||||
import android.util.Log; |
||||
import java.net.URISyntaxException; |
||||
|
||||
/* Handles the binder connection state with OnDeviceServer server */ |
||||
public class GrpcBinderConnection implements ServiceConnection { |
||||
private static final String logTag = "GrpcBinderConnection"; |
||||
|
||||
private Context mContext; |
||||
private IBinder mService; |
||||
|
||||
// A string that identifies this service connection
|
||||
private final String mConnId; |
||||
|
||||
public GrpcBinderConnection(Context context, String connId) { |
||||
mContext = context; |
||||
mConnId = connId; |
||||
} |
||||
|
||||
@Override |
||||
public void onNullBinding(ComponentName className) { |
||||
// TODO(mingcl): Notify C++ that the connection is never going to happen
|
||||
Log.e(logTag, "Service returned null IBinder. mConnId = " + mConnId); |
||||
} |
||||
|
||||
@Override |
||||
public void onServiceConnected(ComponentName className, IBinder service) { |
||||
Log.e(logTag, "Service has connected. mConnId = " + mConnId); |
||||
if (service == null) { |
||||
// This should not happen since onNullBinding should be invoked instead
|
||||
throw new IllegalArgumentException("service was null"); |
||||
} |
||||
synchronized (this) { |
||||
mService = service; |
||||
} |
||||
notifyConnected(mConnId, mService); |
||||
} |
||||
|
||||
@Override |
||||
public void onServiceDisconnected(ComponentName className) { |
||||
Log.e(logTag, "Service has disconnected. mConnId = " + mConnId); |
||||
} |
||||
|
||||
public void tryConnect(String pkg, String cls, String action_name) { |
||||
Intent intent = new Intent(action_name); |
||||
ComponentName compName = new ComponentName(pkg, cls); |
||||
intent.setComponent(compName); |
||||
tryConnect(intent); |
||||
} |
||||
|
||||
@TargetApi(22) |
||||
public void tryConnect(String uri) { |
||||
// Try connect with an URI that can be parsed as intent.
|
||||
try { |
||||
tryConnect(Intent.parseUri(uri, URI_ANDROID_APP_SCHEME | URI_INTENT_SCHEME)); |
||||
} catch (URISyntaxException e) { |
||||
Log.e(logTag, "Unable to parse the Uri: " + uri); |
||||
} |
||||
} |
||||
|
||||
private void tryConnect(Intent intent) { |
||||
synchronized (this) { |
||||
// Will return true if the system is in the process of bringing up a service that your client
|
||||
// has permission to bind to; false if the system couldn't find the service or if your client
|
||||
// doesn't have permission to bind to it
|
||||
boolean result = mContext.bindService(intent, this, Context.BIND_AUTO_CREATE); |
||||
if (result) { |
||||
Log.e(logTag, "bindService returns ok"); |
||||
} else { |
||||
Log.e( |
||||
logTag, |
||||
"bindService failed. Maybe the system couldn't find the service or the" |
||||
+ " client doesn't have permission to bind to it."); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Calls a function defined in endpoint_binder_pool.cc
|
||||
private static native void notifyConnected(String connId, IBinder service); |
||||
} |
@ -1,40 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package io.grpc.binder.cpp; |
||||
|
||||
import android.os.IBinder; |
||||
import android.util.Log; |
||||
|
||||
/* EXPERIMENTAL. Provides a interface to get endpoint binder from C++ */ |
||||
public class GrpcCppServerBuilder { |
||||
private static final String logTag = "GrpcCppServerBuilder"; |
||||
|
||||
public static IBinder GetEndpointBinder(String uri) { |
||||
String scheme = "binder:"; |
||||
if (uri.startsWith(scheme)) { |
||||
String path = uri.substring(scheme.length()); |
||||
// TODO(mingcl): Consider if we would like to make sure the path only contain valid
|
||||
// characters here
|
||||
IBinder ibinder = GetEndpointBinderInternal(path); |
||||
Log.e(logTag, "Returning binder=" + ibinder + " for URI=" + uri); |
||||
return ibinder; |
||||
} else { |
||||
Log.e(logTag, "URI " + uri + " does not start with 'binder:'"); |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
private static native IBinder GetEndpointBinderInternal(String conn_id); |
||||
} |
@ -1,71 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package io.grpc.binder.cpp; |
||||
|
||||
import android.content.Context; |
||||
import android.content.pm.PackageManager; |
||||
import android.os.Parcel; |
||||
import android.util.Log; |
||||
// copybara: Import proguard UsedByNative annotation here
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* This class will be invoked by gRPC binder transport internal implementation (from |
||||
* src/core/ext/transport/binder/client/jni_utils.cc) to perform operations that are only possible |
||||
* in Java |
||||
*/ |
||||
// copybara: Add @UsedByNative("jni_utils.cc")
|
||||
final class NativeConnectionHelper { |
||||
// Maps connection id to GrpcBinderConnection instances
|
||||
static Map<String, GrpcBinderConnection> connectionIdToGrpcBinderConnectionMap = new HashMap<>(); |
||||
|
||||
// copybara: Add @UsedByNative("jni_utils.cc")
|
||||
static void tryEstablishConnection( |
||||
Context context, String pkg, String cls, String actionName, String connId) { |
||||
// TODO(mingcl): Assert that connId is unique
|
||||
connectionIdToGrpcBinderConnectionMap.put(connId, new GrpcBinderConnection(context, connId)); |
||||
connectionIdToGrpcBinderConnectionMap.get(connId).tryConnect(pkg, cls, actionName); |
||||
} |
||||
|
||||
// copybara: Add @UsedByNative("jni_utils.cc")
|
||||
static void tryEstablishConnectionWithUri(Context context, String uri, String connId) { |
||||
// TODO(mingcl): Assert that connId is unique
|
||||
connectionIdToGrpcBinderConnectionMap.put(connId, new GrpcBinderConnection(context, connId)); |
||||
connectionIdToGrpcBinderConnectionMap.get(connId).tryConnect(uri); |
||||
} |
||||
|
||||
// Returns true if the packages signature of the 2 UIDs match.
|
||||
// `context` is used to get PackageManager.
|
||||
// Suppress unnecessary internal warnings related to checkSignatures compatibility issue.
|
||||
// BinderTransport code is only used on newer Android platform versions so this is fine.
|
||||
@SuppressWarnings("CheckSignatures") |
||||
// copybara: Add @UsedByNative("jni_utils.cc")
|
||||
static boolean isSignatureMatch(Context context, int uid1, int uid2) { |
||||
int result = context.getPackageManager().checkSignatures(uid1, uid2); |
||||
if (result == PackageManager.SIGNATURE_MATCH) { |
||||
return true; |
||||
} |
||||
Log.e( |
||||
"NativeConnectionHelper", |
||||
"Signatures does not match. checkSignature return value = " + result); |
||||
return false; |
||||
} |
||||
|
||||
// copybara: Add @UsedByNative("jni_utils.cc")
|
||||
static Parcel getEmptyParcel() { |
||||
return Parcel.obtain(); |
||||
} |
||||
} |
@ -1,106 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
|
||||
#ifdef GPR_ANDROID |
||||
|
||||
#include <jni.h> |
||||
#include <unistd.h> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "src/core/ext/transport/binder/client/jni_utils.h" |
||||
#include "src/core/util/crash.h" |
||||
|
||||
#endif |
||||
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
namespace binder { |
||||
|
||||
UntrustedSecurityPolicy::UntrustedSecurityPolicy() = default; |
||||
|
||||
UntrustedSecurityPolicy::~UntrustedSecurityPolicy() = default; |
||||
|
||||
bool UntrustedSecurityPolicy::IsAuthorized(int) { return true; }; |
||||
|
||||
InternalOnlySecurityPolicy::InternalOnlySecurityPolicy() = default; |
||||
|
||||
InternalOnlySecurityPolicy::~InternalOnlySecurityPolicy() = default; |
||||
|
||||
#ifdef GPR_ANDROID |
||||
bool InternalOnlySecurityPolicy::IsAuthorized(int uid) { |
||||
return static_cast<uid_t>(uid) == getuid(); |
||||
} |
||||
#else |
||||
bool InternalOnlySecurityPolicy::IsAuthorized(int) { return false; } |
||||
#endif |
||||
|
||||
#ifdef GPR_ANDROID |
||||
|
||||
namespace { |
||||
JNIEnv* GetEnv(JavaVM* vm) { |
||||
if (vm == nullptr) return nullptr; |
||||
|
||||
JNIEnv* result = nullptr; |
||||
jint attach = vm->AttachCurrentThread(&result, nullptr); |
||||
|
||||
CHECK(JNI_OK == attach); |
||||
CHECK_NE(result, nullptr); |
||||
return result; |
||||
} |
||||
} // namespace
|
||||
|
||||
SameSignatureSecurityPolicy::SameSignatureSecurityPolicy(JavaVM* jvm, |
||||
jobject context) |
||||
: jvm_(jvm) { |
||||
CHECK_NE(jvm, nullptr); |
||||
CHECK_NE(context, nullptr); |
||||
|
||||
JNIEnv* env = GetEnv(jvm_); |
||||
|
||||
// Make sure the context is still valid when IsAuthorized() is called
|
||||
context_ = env->NewGlobalRef(context); |
||||
CHECK_NE(context_, nullptr); |
||||
} |
||||
|
||||
SameSignatureSecurityPolicy::~SameSignatureSecurityPolicy() { |
||||
JNIEnv* env = GetEnv(jvm_); |
||||
env->DeleteLocalRef(context_); |
||||
} |
||||
|
||||
bool SameSignatureSecurityPolicy::IsAuthorized(int uid) { |
||||
JNIEnv* env = GetEnv(jvm_); |
||||
bool result = grpc_binder::IsSignatureMatch(env, context_, getuid(), uid); |
||||
if (result) { |
||||
LOG(INFO) << "uid " << getuid() << " and uid " << uid |
||||
<< " passed SameSignature check"; |
||||
} else { |
||||
LOG(ERROR) << "uid " << getuid() << " and uid " << uid |
||||
<< " failed SameSignature check"; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
#endif |
||||
|
||||
} // namespace binder
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
#endif |
@ -1,40 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_SECURITY_POLICY_SECURITY_POLICY_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_SECURITY_POLICY_SECURITY_POLICY_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
namespace binder { |
||||
|
||||
// This interface is for determining if a connection is allowed to be
|
||||
// established on Android. See https://source.android.com/security/app-sandbox
|
||||
// for more info about UID.
|
||||
class SecurityPolicy { |
||||
public: |
||||
virtual ~SecurityPolicy() = default; |
||||
// Returns true if the UID is authorized to connect.
|
||||
// Must return the same value for the same inputs so callers can safely cache
|
||||
// the result.
|
||||
virtual bool IsAuthorized(int uid) = 0; |
||||
}; |
||||
|
||||
} // namespace binder
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_SECURITY_POLICY_SECURITY_POLICY_H
|
@ -1,249 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/core/ext/transport/binder/server/binder_server.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <grpc/grpc.h> |
||||
|
||||
#include <memory> |
||||
#include <string> |
||||
#include <utility> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/memory/memory.h" |
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
#include "src/core/ext/transport/binder/utils/ndk_binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder_android.h" |
||||
#include "src/core/lib/iomgr/exec_ctx.h" |
||||
#include "src/core/lib/transport/error_utils.h" |
||||
#include "src/core/server/server.h" |
||||
|
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
|
||||
#include <jni.h> |
||||
|
||||
extern "C" { |
||||
|
||||
// This will be invoked from
|
||||
// src/core/ext/transport/binder/java/io/grpc/binder/cpp/GrpcCppServerBuilder.java
|
||||
JNIEXPORT jobject JNICALL |
||||
Java_io_grpc_binder_cpp_GrpcCppServerBuilder_GetEndpointBinderInternal__Ljava_lang_String_2( |
||||
JNIEnv* jni_env, jobject, jstring conn_id_jstring) { |
||||
grpc_binder::ndk_util::AIBinder* ai_binder = nullptr; |
||||
|
||||
{ |
||||
// This block is the scope of conn_id c-string
|
||||
jboolean isCopy; |
||||
const char* conn_id = jni_env->GetStringUTFChars(conn_id_jstring, &isCopy); |
||||
ai_binder = static_cast<grpc_binder::ndk_util::AIBinder*>( |
||||
grpc_get_endpoint_binder(std::string(conn_id))); |
||||
if (ai_binder == nullptr) { |
||||
LOG(ERROR) << "Cannot find endpoint binder with connection id = " |
||||
<< conn_id; |
||||
} |
||||
if (isCopy == JNI_TRUE) { |
||||
jni_env->ReleaseStringUTFChars(conn_id_jstring, conn_id); |
||||
} |
||||
} |
||||
|
||||
if (ai_binder == nullptr) { |
||||
return nullptr; |
||||
} |
||||
|
||||
return grpc_binder::ndk_util::AIBinder_toJavaBinder(jni_env, ai_binder); |
||||
} |
||||
} |
||||
|
||||
#endif |
||||
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
namespace binder { |
||||
|
||||
void* GetEndpointBinder(const std::string& service) { |
||||
return grpc_get_endpoint_binder(service); |
||||
} |
||||
|
||||
void AddEndpointBinder(const std::string& service, void* endpoint_binder) { |
||||
grpc_add_endpoint_binder(service, endpoint_binder); |
||||
} |
||||
|
||||
void RemoveEndpointBinder(const std::string& service) { |
||||
grpc_remove_endpoint_binder(service); |
||||
} |
||||
|
||||
} // namespace binder
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
|
||||
static absl::flat_hash_map<std::string, void*>* g_endpoint_binder_pool = |
||||
nullptr; |
||||
|
||||
namespace { |
||||
|
||||
grpc_core::Mutex* GetBinderPoolMutex() { |
||||
static grpc_core::Mutex* mu = new grpc_core::Mutex(); |
||||
return mu; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
void grpc_add_endpoint_binder(const std::string& service, |
||||
void* endpoint_binder) { |
||||
grpc_core::MutexLock lock(GetBinderPoolMutex()); |
||||
if (g_endpoint_binder_pool == nullptr) { |
||||
g_endpoint_binder_pool = new absl::flat_hash_map<std::string, void*>(); |
||||
} |
||||
(*g_endpoint_binder_pool)[service] = endpoint_binder; |
||||
} |
||||
|
||||
void grpc_remove_endpoint_binder(const std::string& service) { |
||||
grpc_core::MutexLock lock(GetBinderPoolMutex()); |
||||
if (g_endpoint_binder_pool == nullptr) { |
||||
return; |
||||
} |
||||
g_endpoint_binder_pool->erase(service); |
||||
} |
||||
|
||||
void* grpc_get_endpoint_binder(const std::string& service) { |
||||
grpc_core::MutexLock lock(GetBinderPoolMutex()); |
||||
if (g_endpoint_binder_pool == nullptr) { |
||||
return nullptr; |
||||
} |
||||
auto iter = g_endpoint_binder_pool->find(service); |
||||
return iter == g_endpoint_binder_pool->end() ? nullptr : iter->second; |
||||
} |
||||
|
||||
namespace grpc_core { |
||||
|
||||
class BinderServerListener : public Server::ListenerInterface { |
||||
public: |
||||
BinderServerListener( |
||||
Server* server, std::string addr, BinderTxReceiverFactory factory, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) |
||||
: server_(server), |
||||
addr_(std::move(addr)), |
||||
factory_(std::move(factory)), |
||||
security_policy_(security_policy) {} |
||||
|
||||
void Start(Server* /*server*/, |
||||
const std::vector<grpc_pollset*>* /*pollsets*/) override { |
||||
tx_receiver_ = factory_( |
||||
[this](transaction_code_t code, grpc_binder::ReadableParcel* parcel, |
||||
int uid) { return OnSetupTransport(code, parcel, uid); }); |
||||
endpoint_binder_ = tx_receiver_->GetRawBinder(); |
||||
grpc_add_endpoint_binder(addr_, endpoint_binder_); |
||||
} |
||||
|
||||
channelz::ListenSocketNode* channelz_listen_socket_node() const override { |
||||
return nullptr; |
||||
} |
||||
|
||||
void SetOnDestroyDone(grpc_closure* on_destroy_done) override { |
||||
on_destroy_done_ = on_destroy_done; |
||||
} |
||||
|
||||
void Orphan() override { Unref(); } |
||||
|
||||
~BinderServerListener() override { |
||||
ExecCtx::Get()->Flush(); |
||||
if (on_destroy_done_) { |
||||
ExecCtx::Run(DEBUG_LOCATION, on_destroy_done_, absl::OkStatus()); |
||||
ExecCtx::Get()->Flush(); |
||||
} |
||||
grpc_remove_endpoint_binder(addr_); |
||||
} |
||||
|
||||
private: |
||||
absl::Status OnSetupTransport(transaction_code_t code, |
||||
grpc_binder::ReadableParcel* parcel, int uid) { |
||||
ExecCtx exec_ctx; |
||||
if (static_cast<grpc_binder::BinderTransportTxCode>(code) != |
||||
grpc_binder::BinderTransportTxCode::SETUP_TRANSPORT) { |
||||
return absl::InvalidArgumentError("Not a SETUP_TRANSPORT request"); |
||||
} |
||||
|
||||
LOG(INFO) << "BinderServerListener calling uid = " << uid; |
||||
if (!security_policy_->IsAuthorized(uid)) { |
||||
// TODO(mingcl): For now we just ignore this unauthorized
|
||||
// SETUP_TRANSPORT transaction and ghost the client. Check if we should
|
||||
// send back a SHUTDOWN_TRANSPORT in this case.
|
||||
return absl::PermissionDeniedError( |
||||
"UID " + std::to_string(uid) + |
||||
" is not allowed to connect to this " |
||||
"server according to security policy."); |
||||
} |
||||
|
||||
int version; |
||||
absl::Status status = parcel->ReadInt32(&version); |
||||
if (!status.ok()) { |
||||
return status; |
||||
} |
||||
LOG(INFO) << "BinderTransport client protocol version = " << version; |
||||
// TODO(mingcl): Make sure we only give client a version that is not newer
|
||||
// than the version they specify. For now, we always tell client that we
|
||||
// only support version=1.
|
||||
std::unique_ptr<grpc_binder::Binder> client_binder{}; |
||||
status = parcel->ReadBinder(&client_binder); |
||||
if (!status.ok()) { |
||||
return status; |
||||
} |
||||
if (!client_binder) { |
||||
return absl::InvalidArgumentError("NULL binder read from the parcel"); |
||||
} |
||||
client_binder->Initialize(); |
||||
// Finish the second half of SETUP_TRANSPORT in
|
||||
// grpc_create_binder_transport_server().
|
||||
Transport* server_transport = grpc_create_binder_transport_server( |
||||
std::move(client_binder), security_policy_); |
||||
CHECK(server_transport); |
||||
grpc_error_handle error = server_->SetupTransport( |
||||
server_transport, nullptr, server_->channel_args(), nullptr); |
||||
return grpc_error_to_absl_status(error); |
||||
} |
||||
|
||||
Server* server_; |
||||
grpc_closure* on_destroy_done_ = nullptr; |
||||
std::string addr_; |
||||
BinderTxReceiverFactory factory_; |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy_; |
||||
void* endpoint_binder_ = nullptr; |
||||
std::unique_ptr<grpc_binder::TransactionReceiver> tx_receiver_; |
||||
}; |
||||
|
||||
bool AddBinderPort(const std::string& addr, grpc_server* server, |
||||
BinderTxReceiverFactory factory, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) { |
||||
// TODO(mingcl): Check if the addr is valid here after binder address resolver
|
||||
// related code are merged.
|
||||
const std::string kBinderUriScheme = "binder:"; |
||||
if (addr.compare(0, kBinderUriScheme.size(), kBinderUriScheme) != 0) { |
||||
return false; |
||||
} |
||||
std::string conn_id = addr.substr(kBinderUriScheme.size()); |
||||
Server* core_server = Server::FromC(server); |
||||
core_server->AddListener(MakeOrphanable<BinderServerListener>( |
||||
core_server, conn_id, std::move(factory), security_policy)); |
||||
return true; |
||||
} |
||||
|
||||
} // namespace grpc_core
|
||||
#endif |
@ -1,65 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_SERVER_BINDER_SERVER_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_SERVER_BINDER_SERVER_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
|
||||
#include <string> |
||||
|
||||
#include "absl/container/flat_hash_map.h" |
||||
#include "absl/status/status.h" |
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/lib/iomgr/exec_ctx.h" |
||||
#include "src/core/lib/transport/error_utils.h" |
||||
#include "src/core/server/server.h" |
||||
|
||||
// TODO(waynetu): This is part of the public API and should be moved to the
|
||||
// include/ folder.
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
namespace binder { |
||||
|
||||
void* GetEndpointBinder(const std::string& service); |
||||
void AddEndpointBinder(const std::string& service, void* endpoint_binder); |
||||
void RemoveEndpointBinder(const std::string& service); |
||||
|
||||
} // namespace binder
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
|
||||
void grpc_add_endpoint_binder(const std::string& service, |
||||
void* endpoint_binder); |
||||
void grpc_remove_endpoint_binder(const std::string& service); |
||||
void* grpc_get_endpoint_binder(const std::string& service); |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// Consume a callback, produce a transaction listener. This is used to perform
|
||||
// testing in non-Android environments where the actual binder is not available.
|
||||
using BinderTxReceiverFactory = |
||||
std::function<std::unique_ptr<grpc_binder::TransactionReceiver>( |
||||
grpc_binder::TransactionReceiver::OnTransactCb)>; |
||||
|
||||
bool AddBinderPort(const std::string& addr, grpc_server* server, |
||||
BinderTxReceiverFactory factory, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_SERVER_BINDER_SERVER_H
|
@ -1,71 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "absl/log/check.h" |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
#include <grpcpp/security/server_credentials.h> |
||||
|
||||
#include "src/core/ext/transport/binder/server/binder_server.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder_android.h" |
||||
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
|
||||
namespace { |
||||
|
||||
class BinderServerCredentialsImpl final : public ServerCredentials { |
||||
public: |
||||
explicit BinderServerCredentialsImpl( |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) |
||||
: ServerCredentials(nullptr), security_policy_(security_policy) {} |
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
int AddPortToServer(const std::string& addr, grpc_server* server) override { |
||||
return grpc_core::AddBinderPort( |
||||
std::string(addr), server, |
||||
[](grpc_binder::TransactionReceiver::OnTransactCb transact_cb) { |
||||
return std::make_unique<grpc_binder::TransactionReceiverAndroid>( |
||||
nullptr, std::move(transact_cb)); |
||||
}, |
||||
security_policy_); |
||||
} |
||||
#else |
||||
int AddPortToServer(const std::string& /*addr*/, |
||||
grpc_server* /*server*/) override { |
||||
return 0; |
||||
} |
||||
#endif // GPR_SUPPORT_BINDER_TRANSPORT
|
||||
|
||||
private: |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy_; |
||||
}; |
||||
|
||||
} // namespace
|
||||
|
||||
std::shared_ptr<ServerCredentials> BinderServerCredentials( |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) { |
||||
CHECK_NE(security_policy, nullptr); |
||||
return std::shared_ptr<ServerCredentials>( |
||||
new BinderServerCredentialsImpl(security_policy)); |
||||
} |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
#endif |
@ -1,117 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_TRANSPORT_BINDER_STREAM_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_TRANSPORT_BINDER_STREAM_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
|
||||
struct RecvInitialMetadataArgs { |
||||
grpc_binder_stream* stream; |
||||
grpc_binder_transport* transport; |
||||
int tx_code; |
||||
absl::StatusOr<grpc_binder::Metadata> initial_metadata; |
||||
}; |
||||
|
||||
struct RecvMessageArgs { |
||||
grpc_binder_stream* stream; |
||||
grpc_binder_transport* transport; |
||||
int tx_code; |
||||
absl::StatusOr<std::string> message; |
||||
}; |
||||
|
||||
struct RecvTrailingMetadataArgs { |
||||
grpc_binder_stream* stream; |
||||
grpc_binder_transport* transport; |
||||
int tx_code; |
||||
absl::StatusOr<grpc_binder::Metadata> trailing_metadata; |
||||
int status; |
||||
}; |
||||
|
||||
struct RegisterStreamArgs { |
||||
grpc_binder_stream* stream; |
||||
grpc_binder_transport* transport; |
||||
}; |
||||
|
||||
// TODO(mingcl): Figure out if we want to use class instead of struct here
|
||||
struct grpc_binder_stream { |
||||
// server_data will be null for client, and for server it will be whatever
|
||||
// passed in to the accept_stream_fn callback by client.
|
||||
grpc_binder_stream(grpc_binder_transport* t, grpc_stream_refcount* refcount, |
||||
const void* /*server_data*/, grpc_core::Arena* arena, |
||||
int tx_code, bool is_client) |
||||
: t(t), |
||||
refcount(refcount), |
||||
arena(arena), |
||||
tx_code(tx_code), |
||||
is_client(is_client), |
||||
is_closed(false) { |
||||
recv_initial_metadata_args.stream = this; |
||||
recv_initial_metadata_args.transport = t; |
||||
recv_message_args.stream = this; |
||||
recv_message_args.transport = t; |
||||
recv_trailing_metadata_args.stream = this; |
||||
recv_trailing_metadata_args.transport = t; |
||||
} |
||||
|
||||
~grpc_binder_stream() { |
||||
if (destroy_stream_then_closure != nullptr) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, destroy_stream_then_closure, |
||||
absl::OkStatus()); |
||||
} |
||||
} |
||||
|
||||
int GetTxCode() const { return tx_code; } |
||||
|
||||
grpc_binder_transport* t; |
||||
grpc_stream_refcount* refcount; |
||||
grpc_core::Arena* arena; |
||||
int tx_code; |
||||
const bool is_client; |
||||
bool is_closed; |
||||
|
||||
grpc_closure* destroy_stream_then_closure = nullptr; |
||||
grpc_closure destroy_stream; |
||||
|
||||
// The reason why this stream is cancelled and closed.
|
||||
grpc_error_handle cancel_self_error; |
||||
|
||||
grpc_closure recv_initial_metadata_closure; |
||||
RecvInitialMetadataArgs recv_initial_metadata_args; |
||||
grpc_closure recv_message_closure; |
||||
RecvMessageArgs recv_message_args; |
||||
grpc_closure recv_trailing_metadata_closure; |
||||
RecvTrailingMetadataArgs recv_trailing_metadata_args; |
||||
|
||||
grpc_closure register_stream_closure; |
||||
RegisterStreamArgs register_stream_args; |
||||
|
||||
// We store these fields passed from op batch, in order to access them through
|
||||
// grpc_binder_stream
|
||||
grpc_metadata_batch* recv_initial_metadata; |
||||
grpc_closure* recv_initial_metadata_ready = nullptr; |
||||
bool* trailing_metadata_available = nullptr; |
||||
absl::optional<grpc_core::SliceBuffer>* recv_message; |
||||
grpc_closure* recv_message_ready = nullptr; |
||||
bool* call_failed_before_recv_message = nullptr; |
||||
grpc_metadata_batch* recv_trailing_metadata; |
||||
grpc_closure* recv_trailing_metadata_finished = nullptr; |
||||
|
||||
bool trailing_metadata_sent = false; |
||||
bool need_to_call_trailing_metadata_callback = false; |
||||
}; |
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_TRANSPORT_BINDER_STREAM_H
|
@ -1,758 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/core/ext/transport/binder/transport/binder_transport.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <cstdint> |
||||
#include <memory> |
||||
#include <string> |
||||
#include <utility> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/memory/memory.h" |
||||
#include "absl/strings/str_cat.h" |
||||
#include "absl/strings/substitute.h" |
||||
#include "src/core/ext/transport/binder/transport/binder_stream.h" |
||||
#include "src/core/ext/transport/binder/utils/transport_stream_receiver.h" |
||||
#include "src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_reader.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_reader_impl.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_writer.h" |
||||
#include "src/core/lib/event_engine/default_event_engine.h" |
||||
#include "src/core/lib/iomgr/exec_ctx.h" |
||||
#include "src/core/lib/slice/slice_internal.h" |
||||
#include "src/core/lib/transport/error_utils.h" |
||||
#include "src/core/lib/transport/metadata_batch.h" |
||||
#include "src/core/lib/transport/transport.h" |
||||
#include "src/core/util/crash.h" |
||||
|
||||
#ifndef NDEBUG |
||||
static void grpc_binder_stream_ref(grpc_binder_stream* s, const char* reason) { |
||||
grpc_stream_ref(s->refcount, reason); |
||||
} |
||||
static void grpc_binder_stream_unref(grpc_binder_stream* s, |
||||
const char* reason) { |
||||
grpc_stream_unref(s->refcount, reason); |
||||
} |
||||
static void grpc_binder_ref_transport(grpc_binder_transport* t, |
||||
const char* reason, const char* file, |
||||
int line) { |
||||
t->refs.Ref(grpc_core::DebugLocation(file, line), reason); |
||||
} |
||||
static void grpc_binder_unref_transport(grpc_binder_transport* t, |
||||
const char* reason, const char* file, |
||||
int line) { |
||||
if (t->refs.Unref(grpc_core::DebugLocation(file, line), reason)) { |
||||
delete t; |
||||
} |
||||
} |
||||
#else |
||||
static void grpc_binder_stream_ref(grpc_binder_stream* s) { |
||||
grpc_stream_ref(s->refcount); |
||||
} |
||||
static void grpc_binder_stream_unref(grpc_binder_stream* s) { |
||||
grpc_stream_unref(s->refcount); |
||||
} |
||||
static void grpc_binder_ref_transport(grpc_binder_transport* t) { |
||||
t->refs.Ref(); |
||||
} |
||||
static void grpc_binder_unref_transport(grpc_binder_transport* t) { |
||||
if (t->refs.Unref()) { |
||||
delete t; |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
#ifndef NDEBUG |
||||
#define GRPC_BINDER_STREAM_REF(stream, reason) \ |
||||
grpc_binder_stream_ref(stream, reason) |
||||
#define GRPC_BINDER_STREAM_UNREF(stream, reason) \ |
||||
grpc_binder_stream_unref(stream, reason) |
||||
#define GRPC_BINDER_REF_TRANSPORT(t, r) \ |
||||
grpc_binder_ref_transport(t, r, __FILE__, __LINE__) |
||||
#define GRPC_BINDER_UNREF_TRANSPORT(t, r) \ |
||||
grpc_binder_unref_transport(t, r, __FILE__, __LINE__) |
||||
#else |
||||
#define GRPC_BINDER_STREAM_REF(stream, reason) grpc_binder_stream_ref(stream) |
||||
#define GRPC_BINDER_STREAM_UNREF(stream, reason) \ |
||||
grpc_binder_stream_unref(stream) |
||||
#define GRPC_BINDER_REF_TRANSPORT(t, r) grpc_binder_ref_transport(t) |
||||
#define GRPC_BINDER_UNREF_TRANSPORT(t, r) grpc_binder_unref_transport(t) |
||||
#endif |
||||
|
||||
static void register_stream_locked(void* arg, grpc_error_handle /*error*/) { |
||||
RegisterStreamArgs* args = static_cast<RegisterStreamArgs*>(arg); |
||||
args->transport->registered_stream[args->stream->GetTxCode()] = args->stream; |
||||
} |
||||
|
||||
void grpc_binder_transport::InitStream(grpc_stream* gs, |
||||
grpc_stream_refcount* refcount, |
||||
const void* server_data, |
||||
grpc_core::Arena* arena) { |
||||
LOG(INFO) << __func__ << " = " << this << " " << gs << " " << refcount << " " |
||||
<< server_data << " " << arena; |
||||
// Note that this function is not locked and may be invoked concurrently
|
||||
new (gs) grpc_binder_stream(this, refcount, server_data, arena, |
||||
NewStreamTxCode(), is_client); |
||||
|
||||
// `grpc_binder_transport::registered_stream` should only be updated in
|
||||
// combiner
|
||||
grpc_binder_stream* stream = reinterpret_cast<grpc_binder_stream*>(gs); |
||||
stream->register_stream_args.stream = stream; |
||||
stream->register_stream_args.transport = this; |
||||
grpc_core::ExecCtx exec_ctx; |
||||
combiner->Run(GRPC_CLOSURE_INIT(&stream->register_stream_closure, |
||||
register_stream_locked, |
||||
&stream->register_stream_args, nullptr), |
||||
absl::OkStatus()); |
||||
} |
||||
|
||||
static void AssignMetadata(grpc_metadata_batch* mb, |
||||
const grpc_binder::Metadata& md) { |
||||
mb->Clear(); |
||||
for (auto& p : md) { |
||||
mb->Append(p.first, grpc_core::Slice::FromCopiedString(p.second), |
||||
[&](absl::string_view error, const grpc_core::Slice&) { |
||||
VLOG(2) << "Failed to parse metadata: " |
||||
<< "key=" << p.first << " error=" << error; |
||||
}); |
||||
} |
||||
} |
||||
|
||||
static void cancel_stream_locked(grpc_binder_transport* transport, |
||||
grpc_binder_stream* stream, |
||||
grpc_error_handle error) { |
||||
LOG(INFO) << "cancel_stream_locked"; |
||||
if (!stream->is_closed) { |
||||
CHECK(stream->cancel_self_error.ok()); |
||||
stream->is_closed = true; |
||||
stream->cancel_self_error = error; |
||||
transport->transport_stream_receiver->CancelStream(stream->tx_code); |
||||
transport->registered_stream.erase(stream->tx_code); |
||||
if (stream->recv_initial_metadata_ready != nullptr) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, |
||||
stream->recv_initial_metadata_ready, error); |
||||
stream->recv_initial_metadata_ready = nullptr; |
||||
stream->recv_initial_metadata = nullptr; |
||||
stream->trailing_metadata_available = nullptr; |
||||
} |
||||
if (stream->recv_message_ready != nullptr) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, stream->recv_message_ready, |
||||
error); |
||||
stream->recv_message_ready = nullptr; |
||||
stream->recv_message->reset(); |
||||
stream->recv_message = nullptr; |
||||
stream->call_failed_before_recv_message = nullptr; |
||||
} |
||||
if (stream->recv_trailing_metadata_finished != nullptr) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, |
||||
stream->recv_trailing_metadata_finished, error); |
||||
stream->recv_trailing_metadata_finished = nullptr; |
||||
stream->recv_trailing_metadata = nullptr; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static bool ContainsAuthorityAndPath(const grpc_binder::Metadata& metadata) { |
||||
bool has_authority = false; |
||||
bool has_path = false; |
||||
for (const auto& kv : metadata) { |
||||
if (kv.first == ":authority") { |
||||
has_authority = true; |
||||
} |
||||
if (kv.first == ":path") { |
||||
has_path = true; |
||||
} |
||||
} |
||||
return has_authority && has_path; |
||||
} |
||||
|
||||
static void recv_initial_metadata_locked(void* arg, |
||||
grpc_error_handle /*error*/) { |
||||
RecvInitialMetadataArgs* args = static_cast<RecvInitialMetadataArgs*>(arg); |
||||
grpc_binder_stream* stream = args->stream; |
||||
|
||||
LOG(INFO) << "recv_initial_metadata_locked is_client = " << stream->is_client |
||||
<< " is_closed = " << stream->is_closed; |
||||
|
||||
if (!stream->is_closed) { |
||||
grpc_error_handle error = [&] { |
||||
CHECK(stream->recv_initial_metadata); |
||||
CHECK(stream->recv_initial_metadata_ready); |
||||
if (!args->initial_metadata.ok()) { |
||||
LOG(ERROR) << "Failed to parse initial metadata"; |
||||
return absl_status_to_grpc_error(args->initial_metadata.status()); |
||||
} |
||||
if (!stream->is_client) { |
||||
// For server, we expect :authority and :path in initial metadata.
|
||||
if (!ContainsAuthorityAndPath(*args->initial_metadata)) { |
||||
return GRPC_ERROR_CREATE( |
||||
"Missing :authority or :path in initial metadata"); |
||||
} |
||||
} |
||||
AssignMetadata(stream->recv_initial_metadata, *args->initial_metadata); |
||||
return absl::OkStatus(); |
||||
}(); |
||||
if (stream->t->registered_method_matcher_cb != nullptr) { |
||||
stream->t->registered_method_matcher_cb( |
||||
stream->t->accept_stream_user_data, stream->recv_initial_metadata); |
||||
} |
||||
grpc_closure* cb = stream->recv_initial_metadata_ready; |
||||
stream->recv_initial_metadata_ready = nullptr; |
||||
stream->recv_initial_metadata = nullptr; |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, cb, error); |
||||
} |
||||
GRPC_BINDER_STREAM_UNREF(stream, "recv_initial_metadata"); |
||||
} |
||||
|
||||
static void recv_message_locked(void* arg, grpc_error_handle /*error*/) { |
||||
RecvMessageArgs* args = static_cast<RecvMessageArgs*>(arg); |
||||
grpc_binder_stream* stream = args->stream; |
||||
|
||||
LOG(INFO) << "recv_message_locked is_client = " << stream->is_client |
||||
<< " is_closed = " << stream->is_closed; |
||||
|
||||
if (!stream->is_closed) { |
||||
grpc_error_handle error = [&] { |
||||
CHECK(stream->recv_message); |
||||
CHECK(stream->recv_message_ready); |
||||
if (!args->message.ok()) { |
||||
LOG(ERROR) << "Failed to receive message"; |
||||
if (args->message.status().message() == |
||||
grpc_binder::TransportStreamReceiver:: |
||||
kGrpcBinderTransportCancelledGracefully) { |
||||
LOG(ERROR) << "message cancelled gracefully"; |
||||
// Cancelled because we've already received trailing metadata.
|
||||
// It's not an error in this case.
|
||||
return absl::OkStatus(); |
||||
} else { |
||||
return absl_status_to_grpc_error(args->message.status()); |
||||
} |
||||
} |
||||
grpc_core::SliceBuffer buf; |
||||
buf.Append(grpc_core::Slice(grpc_slice_from_cpp_string(*args->message))); |
||||
*stream->recv_message = std::move(buf); |
||||
return absl::OkStatus(); |
||||
}(); |
||||
|
||||
if (!error.ok() && stream->call_failed_before_recv_message != nullptr) { |
||||
*stream->call_failed_before_recv_message = true; |
||||
} |
||||
grpc_closure* cb = stream->recv_message_ready; |
||||
stream->recv_message_ready = nullptr; |
||||
stream->recv_message = nullptr; |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, cb, error); |
||||
} |
||||
|
||||
GRPC_BINDER_STREAM_UNREF(stream, "recv_message"); |
||||
} |
||||
|
||||
static void recv_trailing_metadata_locked(void* arg, |
||||
grpc_error_handle /*error*/) { |
||||
RecvTrailingMetadataArgs* args = static_cast<RecvTrailingMetadataArgs*>(arg); |
||||
grpc_binder_stream* stream = args->stream; |
||||
|
||||
LOG(INFO) << "recv_trailing_metadata_locked is_client = " << stream->is_client |
||||
<< " is_closed = " << stream->is_closed; |
||||
|
||||
if (!stream->is_closed) { |
||||
grpc_error_handle error = [&] { |
||||
CHECK(stream->recv_trailing_metadata); |
||||
CHECK(stream->recv_trailing_metadata_finished); |
||||
if (!args->trailing_metadata.ok()) { |
||||
LOG(ERROR) << "Failed to receive trailing metadata"; |
||||
return absl_status_to_grpc_error(args->trailing_metadata.status()); |
||||
} |
||||
if (!stream->is_client) { |
||||
// Client will not send non-empty trailing metadata.
|
||||
if (!args->trailing_metadata.value().empty()) { |
||||
LOG(ERROR) << "Server receives non-empty trailing metadata."; |
||||
return absl::CancelledError(); |
||||
} |
||||
} else { |
||||
AssignMetadata(stream->recv_trailing_metadata, |
||||
*args->trailing_metadata); |
||||
// Append status to metadata
|
||||
// TODO(b/192208695): See if we can avoid to manually put status
|
||||
// code into the header
|
||||
LOG(INFO) << "status = " << args->status; |
||||
stream->recv_trailing_metadata->Set( |
||||
grpc_core::GrpcStatusMetadata(), |
||||
static_cast<grpc_status_code>(args->status)); |
||||
} |
||||
return absl::OkStatus(); |
||||
}(); |
||||
|
||||
if (stream->is_client || stream->trailing_metadata_sent) { |
||||
grpc_closure* cb = stream->recv_trailing_metadata_finished; |
||||
stream->recv_trailing_metadata_finished = nullptr; |
||||
stream->recv_trailing_metadata = nullptr; |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, cb, error); |
||||
} else { |
||||
// According to transport explaineer - "Server extra: This op shouldn't
|
||||
// actually be considered complete until the server has also sent trailing
|
||||
// metadata to provide the other side with final status"
|
||||
//
|
||||
// We haven't sent trailing metadata yet, so we have to delay completing
|
||||
// the recv_trailing_metadata callback.
|
||||
stream->need_to_call_trailing_metadata_callback = true; |
||||
} |
||||
} |
||||
GRPC_BINDER_STREAM_UNREF(stream, "recv_trailing_metadata"); |
||||
} |
||||
|
||||
namespace grpc_binder { |
||||
namespace { |
||||
|
||||
class MetadataEncoder { |
||||
public: |
||||
MetadataEncoder(bool is_client, Transaction* tx, Metadata* init_md) |
||||
: is_client_(is_client), tx_(tx), init_md_(init_md) {} |
||||
|
||||
void Encode(const grpc_core::Slice& key_slice, |
||||
const grpc_core::Slice& value_slice) { |
||||
absl::string_view key = key_slice.as_string_view(); |
||||
absl::string_view value = value_slice.as_string_view(); |
||||
init_md_->emplace_back(std::string(key), std::string(value)); |
||||
} |
||||
|
||||
void Encode(grpc_core::HttpPathMetadata, const grpc_core::Slice& value) { |
||||
// TODO(b/192208403): Figure out if it is correct to simply drop '/'
|
||||
// prefix and treat it as rpc method name
|
||||
CHECK(value[0] == '/'); |
||||
std::string path = std::string(value.as_string_view().substr(1)); |
||||
|
||||
// Only client send method ref.
|
||||
CHECK(is_client_); |
||||
tx_->SetMethodRef(path); |
||||
} |
||||
|
||||
void Encode(grpc_core::GrpcStatusMetadata, grpc_status_code status) { |
||||
LOG(INFO) << "send trailing metadata status = " << status; |
||||
tx_->SetStatus(status); |
||||
} |
||||
|
||||
template <typename Trait> |
||||
void Encode(Trait, const typename Trait::ValueType& value) { |
||||
init_md_->emplace_back(std::string(Trait::key()), |
||||
std::string(Trait::Encode(value).as_string_view())); |
||||
} |
||||
|
||||
private: |
||||
const bool is_client_; |
||||
Transaction* const tx_; |
||||
Metadata* const init_md_; |
||||
}; |
||||
|
||||
} // namespace
|
||||
} // namespace grpc_binder
|
||||
|
||||
static void accept_stream_locked(void* gt, grpc_error_handle /*error*/) { |
||||
grpc_binder_transport* transport = static_cast<grpc_binder_transport*>(gt); |
||||
if (transport->accept_stream_fn) { |
||||
LOG(INFO) << "Accepting a stream"; |
||||
// must pass in a non-null value.
|
||||
(*transport->accept_stream_fn)(transport->accept_stream_user_data, |
||||
transport, transport); |
||||
} else { |
||||
++transport->accept_stream_fn_called_count_; |
||||
LOG(INFO) << "accept_stream_fn not set, current count = " |
||||
<< transport->accept_stream_fn_called_count_; |
||||
} |
||||
} |
||||
|
||||
static void perform_stream_op_locked(void* stream_op, |
||||
grpc_error_handle /*error*/) { |
||||
grpc_transport_stream_op_batch* op = |
||||
static_cast<grpc_transport_stream_op_batch*>(stream_op); |
||||
grpc_binder_stream* stream = |
||||
static_cast<grpc_binder_stream*>(op->handler_private.extra_arg); |
||||
grpc_binder_transport* transport = stream->t; |
||||
if (op->cancel_stream) { |
||||
// TODO(waynetu): Is this true?
|
||||
CHECK(!op->send_initial_metadata && !op->send_message && |
||||
!op->send_trailing_metadata && !op->recv_initial_metadata && |
||||
!op->recv_message && !op->recv_trailing_metadata); |
||||
LOG(INFO) << "cancel_stream is_client = " << stream->is_client; |
||||
if (!stream->is_client) { |
||||
// Send trailing metadata to inform the other end about the cancellation,
|
||||
// regardless if we'd already done that or not.
|
||||
auto cancel_tx = std::make_unique<grpc_binder::Transaction>( |
||||
stream->GetTxCode(), transport->is_client); |
||||
cancel_tx->SetSuffix(grpc_binder::Metadata{}); |
||||
cancel_tx->SetStatus(1); |
||||
(void)transport->wire_writer->RpcCall(std::move(cancel_tx)); |
||||
} |
||||
cancel_stream_locked(transport, stream, |
||||
op->payload->cancel_stream.cancel_error); |
||||
if (op->on_complete != nullptr) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, op->on_complete, |
||||
absl::OkStatus()); |
||||
} |
||||
GRPC_BINDER_STREAM_UNREF(stream, "perform_stream_op"); |
||||
return; |
||||
} |
||||
|
||||
if (stream->is_closed) { |
||||
if (op->send_message) { |
||||
// Reset the send_message payload to prevent memory leaks.
|
||||
op->payload->send_message.send_message->Clear(); |
||||
} |
||||
if (op->recv_initial_metadata) { |
||||
grpc_core::ExecCtx::Run( |
||||
DEBUG_LOCATION, |
||||
op->payload->recv_initial_metadata.recv_initial_metadata_ready, |
||||
stream->cancel_self_error); |
||||
} |
||||
if (op->recv_message) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, |
||||
op->payload->recv_message.recv_message_ready, |
||||
stream->cancel_self_error); |
||||
} |
||||
if (op->recv_trailing_metadata) { |
||||
grpc_core::ExecCtx::Run( |
||||
DEBUG_LOCATION, |
||||
op->payload->recv_trailing_metadata.recv_trailing_metadata_ready, |
||||
stream->cancel_self_error); |
||||
} |
||||
if (op->on_complete != nullptr) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, op->on_complete, |
||||
stream->cancel_self_error); |
||||
} |
||||
GRPC_BINDER_STREAM_UNREF(stream, "perform_stream_op"); |
||||
return; |
||||
} |
||||
|
||||
int tx_code = stream->tx_code; |
||||
auto tx = |
||||
std::make_unique<grpc_binder::Transaction>(tx_code, transport->is_client); |
||||
|
||||
if (op->send_initial_metadata) { |
||||
LOG(INFO) << "send_initial_metadata"; |
||||
grpc_binder::Metadata init_md; |
||||
auto batch = op->payload->send_initial_metadata.send_initial_metadata; |
||||
|
||||
grpc_binder::MetadataEncoder encoder(transport->is_client, tx.get(), |
||||
&init_md); |
||||
batch->Encode(&encoder); |
||||
tx->SetPrefix(init_md); |
||||
} |
||||
if (op->send_message) { |
||||
LOG(INFO) << "send_message"; |
||||
tx->SetData(op->payload->send_message.send_message->JoinIntoString()); |
||||
} |
||||
|
||||
if (op->send_trailing_metadata) { |
||||
LOG(INFO) << "send_trailing_metadata"; |
||||
auto batch = op->payload->send_trailing_metadata.send_trailing_metadata; |
||||
grpc_binder::Metadata trailing_metadata; |
||||
|
||||
grpc_binder::MetadataEncoder encoder(transport->is_client, tx.get(), |
||||
&trailing_metadata); |
||||
batch->Encode(&encoder); |
||||
|
||||
// TODO(mingcl): Will we ever has key-value pair here? According to
|
||||
// wireformat client suffix data is always empty.
|
||||
tx->SetSuffix(trailing_metadata); |
||||
} |
||||
if (op->recv_initial_metadata) { |
||||
LOG(INFO) << "recv_initial_metadata"; |
||||
stream->recv_initial_metadata_ready = |
||||
op->payload->recv_initial_metadata.recv_initial_metadata_ready; |
||||
stream->recv_initial_metadata = |
||||
op->payload->recv_initial_metadata.recv_initial_metadata; |
||||
stream->trailing_metadata_available = |
||||
op->payload->recv_initial_metadata.trailing_metadata_available; |
||||
GRPC_BINDER_STREAM_REF(stream, "recv_initial_metadata"); |
||||
transport->transport_stream_receiver->RegisterRecvInitialMetadata( |
||||
tx_code, [tx_code, stream, transport]( |
||||
absl::StatusOr<grpc_binder::Metadata> initial_metadata) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
stream->recv_initial_metadata_args.tx_code = tx_code; |
||||
stream->recv_initial_metadata_args.initial_metadata = |
||||
std::move(initial_metadata); |
||||
transport->combiner->Run( |
||||
GRPC_CLOSURE_INIT(&stream->recv_initial_metadata_closure, |
||||
recv_initial_metadata_locked, |
||||
&stream->recv_initial_metadata_args, nullptr), |
||||
absl::OkStatus()); |
||||
}); |
||||
} |
||||
if (op->recv_message) { |
||||
LOG(INFO) << "recv_message"; |
||||
stream->recv_message_ready = op->payload->recv_message.recv_message_ready; |
||||
stream->recv_message = op->payload->recv_message.recv_message; |
||||
stream->call_failed_before_recv_message = |
||||
op->payload->recv_message.call_failed_before_recv_message; |
||||
if (op->payload->recv_message.flags != nullptr) { |
||||
*op->payload->recv_message.flags = 0; |
||||
} |
||||
GRPC_BINDER_STREAM_REF(stream, "recv_message"); |
||||
transport->transport_stream_receiver->RegisterRecvMessage( |
||||
tx_code, |
||||
[tx_code, stream, transport](absl::StatusOr<std::string> message) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
stream->recv_message_args.tx_code = tx_code; |
||||
stream->recv_message_args.message = std::move(message); |
||||
transport->combiner->Run( |
||||
GRPC_CLOSURE_INIT(&stream->recv_message_closure, |
||||
recv_message_locked, &stream->recv_message_args, |
||||
nullptr), |
||||
absl::OkStatus()); |
||||
}); |
||||
} |
||||
if (op->recv_trailing_metadata) { |
||||
LOG(INFO) << "recv_trailing_metadata"; |
||||
stream->recv_trailing_metadata_finished = |
||||
op->payload->recv_trailing_metadata.recv_trailing_metadata_ready; |
||||
stream->recv_trailing_metadata = |
||||
op->payload->recv_trailing_metadata.recv_trailing_metadata; |
||||
GRPC_BINDER_STREAM_REF(stream, "recv_trailing_metadata"); |
||||
transport->transport_stream_receiver->RegisterRecvTrailingMetadata( |
||||
tx_code, [tx_code, stream, transport]( |
||||
absl::StatusOr<grpc_binder::Metadata> trailing_metadata, |
||||
int status) { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
stream->recv_trailing_metadata_args.tx_code = tx_code; |
||||
stream->recv_trailing_metadata_args.trailing_metadata = |
||||
std::move(trailing_metadata); |
||||
stream->recv_trailing_metadata_args.status = status; |
||||
transport->combiner->Run( |
||||
GRPC_CLOSURE_INIT(&stream->recv_trailing_metadata_closure, |
||||
recv_trailing_metadata_locked, |
||||
&stream->recv_trailing_metadata_args, nullptr), |
||||
absl::OkStatus()); |
||||
}); |
||||
} |
||||
// Only send transaction when there's a send op presented.
|
||||
absl::Status status; |
||||
if (op->send_initial_metadata || op->send_message || |
||||
op->send_trailing_metadata) { |
||||
status = transport->wire_writer->RpcCall(std::move(tx)); |
||||
if (!stream->is_client && op->send_trailing_metadata) { |
||||
stream->trailing_metadata_sent = true; |
||||
// According to transport explaineer - "Server extra: This op shouldn't
|
||||
// actually be considered complete until the server has also sent trailing
|
||||
// metadata to provide the other side with final status"
|
||||
//
|
||||
// Because we've done sending trailing metadata here, we can safely
|
||||
// complete the recv_trailing_metadata callback here.
|
||||
if (stream->need_to_call_trailing_metadata_callback) { |
||||
grpc_closure* cb = stream->recv_trailing_metadata_finished; |
||||
stream->recv_trailing_metadata_finished = nullptr; |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, cb, absl::OkStatus()); |
||||
stream->need_to_call_trailing_metadata_callback = false; |
||||
} |
||||
} |
||||
} |
||||
// Note that this should only be scheduled when all non-recv ops are
|
||||
// completed
|
||||
if (op->on_complete != nullptr) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, op->on_complete, |
||||
absl_status_to_grpc_error(status)); |
||||
LOG(INFO) << "on_complete closure scheduled"; |
||||
} |
||||
GRPC_BINDER_STREAM_UNREF(stream, "perform_stream_op"); |
||||
} |
||||
|
||||
void grpc_binder_transport::PerformStreamOp( |
||||
grpc_stream* gs, grpc_transport_stream_op_batch* op) { |
||||
grpc_binder_stream* stream = reinterpret_cast<grpc_binder_stream*>(gs); |
||||
LOG(INFO) << __func__ << " = " << this << " " << gs << " " << op |
||||
<< " is_client = " << stream->is_client; |
||||
GRPC_BINDER_STREAM_REF(stream, "perform_stream_op"); |
||||
op->handler_private.extra_arg = stream; |
||||
combiner->Run(GRPC_CLOSURE_INIT(&op->handler_private.closure, |
||||
perform_stream_op_locked, op, nullptr), |
||||
absl::OkStatus()); |
||||
} |
||||
|
||||
static void close_transport_locked(grpc_binder_transport* transport) { |
||||
transport->state_tracker.SetState( |
||||
GRPC_CHANNEL_SHUTDOWN, absl::OkStatus(), |
||||
"transport closed due to disconnection/goaway"); |
||||
while (!transport->registered_stream.empty()) { |
||||
cancel_stream_locked( |
||||
transport, transport->registered_stream.begin()->second, |
||||
grpc_error_set_int(GRPC_ERROR_CREATE("transport closed"), |
||||
grpc_core::StatusIntProperty::kRpcStatus, |
||||
GRPC_STATUS_UNAVAILABLE)); |
||||
} |
||||
} |
||||
|
||||
static void perform_transport_op_locked(void* transport_op, |
||||
grpc_error_handle /*error*/) { |
||||
grpc_transport_op* op = static_cast<grpc_transport_op*>(transport_op); |
||||
grpc_binder_transport* transport = |
||||
static_cast<grpc_binder_transport*>(op->handler_private.extra_arg); |
||||
// TODO(waynetu): Should we lock here to avoid data race?
|
||||
if (op->start_connectivity_watch != nullptr) { |
||||
transport->state_tracker.AddWatcher( |
||||
op->start_connectivity_watch_state, |
||||
std::move(op->start_connectivity_watch)); |
||||
} |
||||
if (op->stop_connectivity_watch != nullptr) { |
||||
transport->state_tracker.RemoveWatcher(op->stop_connectivity_watch); |
||||
} |
||||
if (op->set_accept_stream) { |
||||
transport->accept_stream_user_data = op->set_accept_stream_user_data; |
||||
transport->accept_stream_fn = op->set_accept_stream_fn; |
||||
transport->registered_method_matcher_cb = |
||||
op->set_registered_method_matcher_fn; |
||||
VLOG(2) << "accept_stream_fn_called_count_ = " |
||||
<< transport->accept_stream_fn_called_count_; |
||||
while (transport->accept_stream_fn_called_count_ > 0) { |
||||
--transport->accept_stream_fn_called_count_; |
||||
transport->combiner->Run( |
||||
GRPC_CLOSURE_CREATE(accept_stream_locked, transport, nullptr), |
||||
absl::OkStatus()); |
||||
} |
||||
} |
||||
if (op->on_consumed) { |
||||
grpc_core::ExecCtx::Run(DEBUG_LOCATION, op->on_consumed, absl::OkStatus()); |
||||
} |
||||
bool do_close = false; |
||||
if (!op->disconnect_with_error.ok()) { |
||||
do_close = true; |
||||
} |
||||
if (!op->goaway_error.ok()) { |
||||
do_close = true; |
||||
} |
||||
if (do_close) { |
||||
close_transport_locked(transport); |
||||
} |
||||
GRPC_BINDER_UNREF_TRANSPORT(transport, "perform_transport_op"); |
||||
} |
||||
|
||||
void grpc_binder_transport::PerformOp(grpc_transport_op* op) { |
||||
LOG(INFO) << __func__; |
||||
op->handler_private.extra_arg = this; |
||||
GRPC_BINDER_REF_TRANSPORT(this, "perform_transport_op"); |
||||
combiner->Run(GRPC_CLOSURE_INIT(&op->handler_private.closure, |
||||
perform_transport_op_locked, op, nullptr), |
||||
absl::OkStatus()); |
||||
} |
||||
|
||||
static void destroy_stream_locked(void* sp, grpc_error_handle /*error*/) { |
||||
grpc_binder_stream* stream = static_cast<grpc_binder_stream*>(sp); |
||||
grpc_binder_transport* transport = stream->t; |
||||
cancel_stream_locked( |
||||
transport, stream, |
||||
grpc_error_set_int(GRPC_ERROR_CREATE("destroy stream"), |
||||
grpc_core::StatusIntProperty::kRpcStatus, |
||||
GRPC_STATUS_UNAVAILABLE)); |
||||
stream->~grpc_binder_stream(); |
||||
} |
||||
|
||||
void grpc_binder_transport::DestroyStream(grpc_stream* gs, |
||||
grpc_closure* then_schedule_closure) { |
||||
LOG(INFO) << __func__; |
||||
grpc_binder_stream* stream = reinterpret_cast<grpc_binder_stream*>(gs); |
||||
stream->destroy_stream_then_closure = then_schedule_closure; |
||||
stream->t->combiner->Run( |
||||
GRPC_CLOSURE_INIT(&stream->destroy_stream, destroy_stream_locked, stream, |
||||
nullptr), |
||||
absl::OkStatus()); |
||||
} |
||||
|
||||
static void destroy_transport_locked(void* gt, grpc_error_handle /*error*/) { |
||||
grpc_binder_transport* transport = static_cast<grpc_binder_transport*>(gt); |
||||
close_transport_locked(transport); |
||||
// Release the references held by the transport.
|
||||
transport->wire_reader = nullptr; |
||||
transport->transport_stream_receiver = nullptr; |
||||
transport->wire_writer = nullptr; |
||||
GRPC_BINDER_UNREF_TRANSPORT(transport, "transport destroyed"); |
||||
} |
||||
|
||||
void grpc_binder_transport::Orphan() { |
||||
LOG(INFO) << __func__; |
||||
combiner->Run(GRPC_CLOSURE_CREATE(destroy_transport_locked, this, nullptr), |
||||
absl::OkStatus()); |
||||
} |
||||
|
||||
size_t grpc_binder_transport::SizeOfStream() const { |
||||
return sizeof(grpc_binder_stream); |
||||
} |
||||
|
||||
grpc_binder_transport::grpc_binder_transport( |
||||
std::unique_ptr<grpc_binder::Binder> binder, bool is_client, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy) |
||||
: is_client(is_client), |
||||
combiner(grpc_combiner_create( |
||||
grpc_event_engine::experimental::GetDefaultEventEngine())), |
||||
state_tracker( |
||||
is_client ? "binder_transport_client" : "binder_transport_server", |
||||
GRPC_CHANNEL_READY), |
||||
refs(1, nullptr) { |
||||
LOG(INFO) << __func__; |
||||
transport_stream_receiver = |
||||
std::make_shared<grpc_binder::TransportStreamReceiverImpl>( |
||||
is_client, /*accept_stream_callback=*/[this] { |
||||
grpc_core::ExecCtx exec_ctx; |
||||
combiner->Run( |
||||
GRPC_CLOSURE_CREATE(accept_stream_locked, this, nullptr), |
||||
absl::OkStatus()); |
||||
}); |
||||
// WireReader holds a ref to grpc_binder_transport.
|
||||
GRPC_BINDER_REF_TRANSPORT(this, "wire reader"); |
||||
wire_reader = grpc_core::MakeOrphanable<grpc_binder::WireReaderImpl>( |
||||
transport_stream_receiver, is_client, security_policy, |
||||
// on_destruct_callback=
|
||||
[this] { |
||||
// Unref transport when destructed.
|
||||
GRPC_BINDER_UNREF_TRANSPORT(this, "wire reader"); |
||||
}); |
||||
wire_writer = wire_reader->SetupTransport(std::move(binder)); |
||||
} |
||||
|
||||
grpc_binder_transport::~grpc_binder_transport() { |
||||
GRPC_COMBINER_UNREF(combiner, "binder_transport"); |
||||
} |
||||
|
||||
grpc_core::Transport* grpc_create_binder_transport_client( |
||||
std::unique_ptr<grpc_binder::Binder> endpoint_binder, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) { |
||||
LOG(INFO) << __func__; |
||||
|
||||
CHECK(endpoint_binder != nullptr); |
||||
CHECK_NE(security_policy, nullptr); |
||||
|
||||
grpc_binder_transport* t = new grpc_binder_transport( |
||||
std::move(endpoint_binder), /*is_client=*/true, security_policy); |
||||
|
||||
return t; |
||||
} |
||||
|
||||
grpc_core::Transport* grpc_create_binder_transport_server( |
||||
std::unique_ptr<grpc_binder::Binder> client_binder, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy) { |
||||
LOG(INFO) << __func__; |
||||
|
||||
CHECK(client_binder != nullptr); |
||||
CHECK_NE(security_policy, nullptr); |
||||
|
||||
grpc_binder_transport* t = new grpc_binder_transport( |
||||
std::move(client_binder), /*is_client=*/false, security_policy); |
||||
|
||||
return t; |
||||
} |
||||
#endif |
@ -1,119 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_TRANSPORT_BINDER_TRANSPORT_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_TRANSPORT_BINDER_TRANSPORT_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
|
||||
#include <atomic> |
||||
#include <memory> |
||||
#include <string> |
||||
#include <utility> |
||||
#include <vector> |
||||
|
||||
#include "absl/container/flat_hash_map.h" |
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "src/core/ext/transport/binder/utils/transport_stream_receiver.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_reader.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_writer.h" |
||||
#include "src/core/lib/iomgr/combiner.h" |
||||
#include "src/core/lib/transport/transport.h" |
||||
#include "src/core/util/crash.h" |
||||
|
||||
struct grpc_binder_stream; |
||||
|
||||
// TODO(mingcl): Consider putting the struct in a namespace (Eventually this
|
||||
// depends on what style we want to follow)
|
||||
// TODO(mingcl): Decide casing for this class name. Should we use C-style class
|
||||
// name here or just go with C++ style?
|
||||
struct grpc_binder_transport final : public grpc_core::FilterStackTransport { |
||||
explicit grpc_binder_transport( |
||||
std::unique_ptr<grpc_binder::Binder> binder, bool is_client, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
~grpc_binder_transport() override; |
||||
|
||||
grpc_core::FilterStackTransport* filter_stack_transport() override { |
||||
return this; |
||||
} |
||||
grpc_core::ClientTransport* client_transport() override { return nullptr; } |
||||
grpc_core::ServerTransport* server_transport() override { return nullptr; } |
||||
absl::string_view GetTransportName() const override { return "binder"; } |
||||
void InitStream(grpc_stream* gs, grpc_stream_refcount* refcount, |
||||
const void* server_data, grpc_core::Arena* arena) override; |
||||
void SetPollset(grpc_stream*, grpc_pollset*) override {} |
||||
void SetPollsetSet(grpc_stream*, grpc_pollset_set*) override {} |
||||
void PerformOp(grpc_transport_op* op) override; |
||||
size_t SizeOfStream() const override; |
||||
bool HackyDisableStreamOpBatchCoalescingInConnectedChannel() const override { |
||||
return false; |
||||
} |
||||
void PerformStreamOp(grpc_stream* gs, |
||||
grpc_transport_stream_op_batch* op) override; |
||||
void DestroyStream(grpc_stream* gs, |
||||
grpc_closure* then_schedule_closure) override; |
||||
void Orphan() override; |
||||
|
||||
int NewStreamTxCode() { |
||||
// TODO(mingcl): Wrap around when all tx codes are used. "If we do detect a
|
||||
// collision however, we will fail the new call with UNAVAILABLE, and shut
|
||||
// down the transport gracefully."
|
||||
CHECK(next_free_tx_code <= LAST_CALL_TRANSACTION); |
||||
return next_free_tx_code++; |
||||
} |
||||
|
||||
std::shared_ptr<grpc_binder::TransportStreamReceiver> |
||||
transport_stream_receiver; |
||||
grpc_core::OrphanablePtr<grpc_binder::WireReader> wire_reader; |
||||
std::shared_ptr<grpc_binder::WireWriter> wire_writer; |
||||
|
||||
bool is_client; |
||||
// A set of currently registered streams (the key is the stream ID).
|
||||
absl::flat_hash_map<int, grpc_binder_stream*> registered_stream; |
||||
grpc_core::Combiner* combiner; |
||||
|
||||
// The callback and the data for the callback when the stream is connected
|
||||
// between client and server. registered_method_matcher_cb is called before
|
||||
// invoking the recv initial metadata callback.
|
||||
void (*accept_stream_fn)(void* user_data, grpc_core::Transport* transport, |
||||
const void* server_data) = nullptr; |
||||
void (*registered_method_matcher_cb)( |
||||
void* user_data, grpc_core::ServerMetadata* metadata) = nullptr; |
||||
void* accept_stream_user_data = nullptr; |
||||
// `accept_stream_locked()` could be called before `accept_stream_fn` has been
|
||||
// set, we need to remember those requests that comes too early and call them
|
||||
// later when we can.
|
||||
int accept_stream_fn_called_count_{0}; |
||||
|
||||
grpc_core::ConnectivityStateTracker state_tracker; |
||||
grpc_core::RefCount refs; |
||||
|
||||
private: |
||||
std::atomic<int> next_free_tx_code{grpc_binder::kFirstCallId}; |
||||
}; |
||||
|
||||
grpc_core::Transport* grpc_create_binder_transport_client( |
||||
std::unique_ptr<grpc_binder::Binder> endpoint_binder, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
grpc_core::Transport* grpc_create_binder_transport_server( |
||||
std::unique_ptr<grpc_binder::Binder> client_binder, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy); |
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_TRANSPORT_BINDER_TRANSPORT_H
|
@ -1,76 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_BINDER_AUTO_UTILS_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_BINDER_AUTO_UTILS_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
|
||||
#include "src/core/ext/transport/binder/utils/ndk_binder.h" |
||||
|
||||
namespace grpc_binder { |
||||
namespace ndk_util { |
||||
|
||||
///
|
||||
/// Represents one strong pointer to an AIBinder object.
|
||||
/// Copied from binder/ndk/include_cpp/android/binder_auto_utils.h
|
||||
///
|
||||
class SpAIBinder { |
||||
public: |
||||
SpAIBinder() : mBinder(nullptr) {} |
||||
explicit SpAIBinder(AIBinder* binder) : mBinder(binder) {} |
||||
SpAIBinder(std::nullptr_t) |
||||
: SpAIBinder() {} // NOLINT(google-explicit-constructor)
|
||||
SpAIBinder(const SpAIBinder& other) { *this = other; } |
||||
|
||||
~SpAIBinder() { set(nullptr); } |
||||
SpAIBinder& operator=(const SpAIBinder& other) { |
||||
if (this == &other) { |
||||
return *this; |
||||
} |
||||
AIBinder_incStrong(other.mBinder); |
||||
set(other.mBinder); |
||||
return *this; |
||||
} |
||||
|
||||
void set(AIBinder* binder) { |
||||
AIBinder* old = *const_cast<AIBinder* volatile*>(&mBinder); |
||||
if (old != nullptr) AIBinder_decStrong(old); |
||||
if (old != *const_cast<AIBinder* volatile*>(&mBinder)) { |
||||
__assert(__FILE__, __LINE__, "Race detected."); |
||||
} |
||||
mBinder = binder; |
||||
} |
||||
|
||||
AIBinder* get() const { return mBinder; } |
||||
AIBinder** getR() { return &mBinder; } |
||||
|
||||
bool operator!=(const SpAIBinder& rhs) const { return get() != rhs.get(); } |
||||
bool operator<(const SpAIBinder& rhs) const { return get() < rhs.get(); } |
||||
bool operator<=(const SpAIBinder& rhs) const { return get() <= rhs.get(); } |
||||
bool operator==(const SpAIBinder& rhs) const { return get() == rhs.get(); } |
||||
bool operator>(const SpAIBinder& rhs) const { return get() > rhs.get(); } |
||||
bool operator>=(const SpAIBinder& rhs) const { return get() >= rhs.get(); } |
||||
|
||||
private: |
||||
AIBinder* mBinder = nullptr; |
||||
}; |
||||
} // namespace ndk_util
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif |
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_BINDER_AUTO_UTILS_H
|
@ -1,218 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/core/ext/transport/binder/utils/ndk_binder.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
|
||||
#include <dlfcn.h> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "src/core/util/crash.h" |
||||
#include "src/core/util/sync.h" |
||||
|
||||
namespace { |
||||
void* GetNdkBinderHandle() { |
||||
// TODO(mingcl): Consider using RTLD_NOLOAD to check if it is already loaded
|
||||
// first
|
||||
static void* handle = dlopen("libbinder_ndk.so", RTLD_LAZY); |
||||
if (handle == nullptr) { |
||||
LOG(ERROR) << "Cannot open libbinder_ndk.so. Does this device support API " |
||||
"level 29?"; |
||||
CHECK(0); |
||||
} |
||||
return handle; |
||||
} |
||||
|
||||
JavaVM* g_jvm = nullptr; |
||||
grpc_core::Mutex g_jvm_mu; |
||||
|
||||
// Whether the thread has already attached to JVM (this is to prevent
|
||||
// repeated attachment in `AttachJvm()`)
|
||||
thread_local bool g_is_jvm_attached = false; |
||||
|
||||
void SetJvm(JNIEnv* env) { |
||||
// OK to lock here since this function will only be called once for each
|
||||
// connection.
|
||||
grpc_core::MutexLock lock(&g_jvm_mu); |
||||
if (g_jvm != nullptr) { |
||||
return; |
||||
} |
||||
JavaVM* jvm = nullptr; |
||||
jint error = env->GetJavaVM(&jvm); |
||||
if (error != JNI_OK) { |
||||
LOG(ERROR) << "Failed to get JVM"; |
||||
} |
||||
g_jvm = jvm; |
||||
LOG(INFO) << "JVM cached"; |
||||
} |
||||
|
||||
// `SetJvm` need to be called in the process before `AttachJvm`. This is always
|
||||
// the case because one of `AIBinder_fromJavaBinder`/`AIBinder_toJavaBinder`
|
||||
// will be called before we actually uses the binder. Return `false` if not able
|
||||
// to attach to JVM. Return `true` if JVM is attached (or already attached).
|
||||
bool AttachJvm() { |
||||
if (g_is_jvm_attached) { |
||||
return true; |
||||
} |
||||
// Note: The following code would be run at most once per thread.
|
||||
grpc_core::MutexLock lock(&g_jvm_mu); |
||||
if (g_jvm == nullptr) { |
||||
LOG(ERROR) << "JVM not cached yet"; |
||||
return false; |
||||
} |
||||
JNIEnv* env_unused; |
||||
// Note that attach a thread that is already attached is a no-op, so it is
|
||||
// fine to call this again if the thread has already been attached by other.
|
||||
g_jvm->AttachCurrentThread(&env_unused, /* thr_args= */ nullptr); |
||||
LOG(INFO) << "JVM attached successfully"; |
||||
g_is_jvm_attached = true; |
||||
return true; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
namespace grpc_binder { |
||||
namespace ndk_util { |
||||
|
||||
// Helper macro to obtain the function pointer corresponding to the name
|
||||
#define FORWARD(name) \ |
||||
typedef decltype(&name) func_type; \
|
||||
static func_type ptr = \
|
||||
reinterpret_cast<func_type>(dlsym(GetNdkBinderHandle(), #name)); \
|
||||
if (ptr == nullptr) { \
|
||||
LOG(ERROR) << "dlsym failed. Cannot find " << #name \
|
||||
<< " in libbinder_ndk.so. " \
|
||||
<< "BinderTransport requires API level >= 33"; \
|
||||
CHECK(0); \
|
||||
} \
|
||||
return ptr |
||||
|
||||
void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz) { |
||||
FORWARD(AIBinder_Class_disableInterfaceTokenHeader)(clazz); |
||||
} |
||||
|
||||
void* AIBinder_getUserData(AIBinder* binder) { |
||||
FORWARD(AIBinder_getUserData)(binder); |
||||
} |
||||
|
||||
uid_t AIBinder_getCallingUid() { FORWARD(AIBinder_getCallingUid)(); } |
||||
|
||||
AIBinder* AIBinder_fromJavaBinder(JNIEnv* env, jobject binder) { |
||||
SetJvm(env); |
||||
FORWARD(AIBinder_fromJavaBinder)(env, binder); |
||||
} |
||||
|
||||
AIBinder_Class* AIBinder_Class_define(const char* interfaceDescriptor, |
||||
AIBinder_Class_onCreate onCreate, |
||||
AIBinder_Class_onDestroy onDestroy, |
||||
AIBinder_Class_onTransact onTransact) { |
||||
FORWARD(AIBinder_Class_define) |
||||
(interfaceDescriptor, onCreate, onDestroy, onTransact); |
||||
} |
||||
|
||||
AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args) { |
||||
FORWARD(AIBinder_new)(clazz, args); |
||||
} |
||||
|
||||
bool AIBinder_associateClass(AIBinder* binder, const AIBinder_Class* clazz) { |
||||
FORWARD(AIBinder_associateClass)(binder, clazz); |
||||
} |
||||
|
||||
void AIBinder_incStrong(AIBinder* binder) { |
||||
FORWARD(AIBinder_incStrong)(binder); |
||||
} |
||||
|
||||
void AIBinder_decStrong(AIBinder* binder) { |
||||
FORWARD(AIBinder_decStrong)(binder); |
||||
} |
||||
|
||||
binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, |
||||
AParcel** in, AParcel** out, |
||||
binder_flags_t flags) { |
||||
if (!AttachJvm()) { |
||||
LOG(ERROR) << "failed to attach JVM. AIBinder_transact might fail."; |
||||
} |
||||
FORWARD(AIBinder_transact)(binder, code, in, out, flags); |
||||
} |
||||
|
||||
binder_status_t AParcel_readByteArray(const AParcel* parcel, void* arrayData, |
||||
AParcel_byteArrayAllocator allocator) { |
||||
FORWARD(AParcel_readByteArray)(parcel, arrayData, allocator); |
||||
} |
||||
|
||||
void AParcel_delete(AParcel* parcel) { FORWARD(AParcel_delete)(parcel); } |
||||
int32_t AParcel_getDataSize(const AParcel* parcel) { |
||||
FORWARD(AParcel_getDataSize)(parcel); |
||||
} |
||||
|
||||
binder_status_t AParcel_writeInt32(AParcel* parcel, int32_t value) { |
||||
FORWARD(AParcel_writeInt32)(parcel, value); |
||||
} |
||||
|
||||
binder_status_t AParcel_writeInt64(AParcel* parcel, int64_t value) { |
||||
FORWARD(AParcel_writeInt64)(parcel, value); |
||||
} |
||||
|
||||
binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) { |
||||
FORWARD(AParcel_writeStrongBinder)(parcel, binder); |
||||
} |
||||
|
||||
binder_status_t AParcel_writeString(AParcel* parcel, const char* string, |
||||
int32_t length) { |
||||
FORWARD(AParcel_writeString)(parcel, string, length); |
||||
} |
||||
|
||||
binder_status_t AParcel_readInt32(const AParcel* parcel, int32_t* value) { |
||||
FORWARD(AParcel_readInt32)(parcel, value); |
||||
} |
||||
|
||||
binder_status_t AParcel_readInt64(const AParcel* parcel, int64_t* value) { |
||||
FORWARD(AParcel_readInt64)(parcel, value); |
||||
} |
||||
|
||||
binder_status_t AParcel_readString(const AParcel* parcel, void* stringData, |
||||
AParcel_stringAllocator allocator) { |
||||
FORWARD(AParcel_readString)(parcel, stringData, allocator); |
||||
} |
||||
|
||||
binder_status_t AParcel_readStrongBinder(const AParcel* parcel, |
||||
AIBinder** binder) { |
||||
FORWARD(AParcel_readStrongBinder)(parcel, binder); |
||||
} |
||||
|
||||
binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* arrayData, |
||||
int32_t length) { |
||||
FORWARD(AParcel_writeByteArray)(parcel, arrayData, length); |
||||
} |
||||
|
||||
binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) { |
||||
FORWARD(AIBinder_prepareTransaction)(binder, in); |
||||
} |
||||
|
||||
jobject AIBinder_toJavaBinder(JNIEnv* env, AIBinder* binder) { |
||||
SetJvm(env); |
||||
FORWARD(AIBinder_toJavaBinder)(env, binder); |
||||
} |
||||
|
||||
} // namespace ndk_util
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif |
||||
#endif |
@ -1,107 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_NDK_BINDER_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_NDK_BINDER_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
|
||||
#include <assert.h> |
||||
#include <jni.h> |
||||
|
||||
#include <memory> |
||||
|
||||
// This file defines NdkBinder functions, variables, and types in
|
||||
// grpc_binder::ndk_util namespace. This allows us to dynamically load
|
||||
// libbinder_ndk at runtime, and make it possible to compile the code without
|
||||
// the library present at compile time.
|
||||
|
||||
// TODO(mingcl): Consider if we want to check API level and include NDK headers
|
||||
// normally if the level is high enough
|
||||
|
||||
namespace grpc_binder { |
||||
namespace ndk_util { |
||||
|
||||
struct AIBinder; |
||||
struct AParcel; |
||||
struct AIBinder_Class; |
||||
|
||||
// Only enum values used by the project is defined here
|
||||
enum { |
||||
FLAG_ONEWAY = 0x01, |
||||
}; |
||||
enum { |
||||
STATUS_OK = 0, |
||||
STATUS_UNKNOWN_ERROR = (-2147483647 - 1), |
||||
}; |
||||
|
||||
typedef int32_t binder_status_t; |
||||
typedef uint32_t binder_flags_t; |
||||
typedef uint32_t transaction_code_t; |
||||
|
||||
typedef bool (*AParcel_byteArrayAllocator)(void* arrayData, int32_t length, |
||||
int8_t** outBuffer); |
||||
typedef bool (*AParcel_stringAllocator)(void* stringData, int32_t length, |
||||
char** buffer); |
||||
typedef void* (*AIBinder_Class_onCreate)(void* args); |
||||
typedef void (*AIBinder_Class_onDestroy)(void* userData); |
||||
typedef binder_status_t (*AIBinder_Class_onTransact)(AIBinder* binder, |
||||
transaction_code_t code, |
||||
const AParcel* in, |
||||
AParcel* out); |
||||
|
||||
void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz); |
||||
void* AIBinder_getUserData(AIBinder* binder); |
||||
uid_t AIBinder_getCallingUid(); |
||||
AIBinder* AIBinder_fromJavaBinder(JNIEnv* env, jobject binder); |
||||
AIBinder_Class* AIBinder_Class_define(const char* interfaceDescriptor, |
||||
AIBinder_Class_onCreate onCreate, |
||||
AIBinder_Class_onDestroy onDestroy, |
||||
AIBinder_Class_onTransact onTransact); |
||||
AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args); |
||||
bool AIBinder_associateClass(AIBinder* binder, const AIBinder_Class* clazz); |
||||
void AIBinder_incStrong(AIBinder* binder); |
||||
void AIBinder_decStrong(AIBinder* binder); |
||||
binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, |
||||
AParcel** in, AParcel** out, |
||||
binder_flags_t flags); |
||||
binder_status_t AParcel_readByteArray(const AParcel* parcel, void* arrayData, |
||||
AParcel_byteArrayAllocator allocator); |
||||
void AParcel_delete(AParcel* parcel); |
||||
int32_t AParcel_getDataSize(const AParcel* parcel); |
||||
binder_status_t AParcel_writeInt32(AParcel* parcel, int32_t value); |
||||
binder_status_t AParcel_writeInt64(AParcel* parcel, int64_t value); |
||||
binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder); |
||||
binder_status_t AParcel_writeString(AParcel* parcel, const char* string, |
||||
int32_t length); |
||||
binder_status_t AParcel_readInt32(const AParcel* parcel, int32_t* value); |
||||
binder_status_t AParcel_readInt64(const AParcel* parcel, int64_t* value); |
||||
binder_status_t AParcel_readString(const AParcel* parcel, void* stringData, |
||||
AParcel_stringAllocator allocator); |
||||
binder_status_t AParcel_readStrongBinder(const AParcel* parcel, |
||||
AIBinder** binder); |
||||
binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* arrayData, |
||||
int32_t length); |
||||
binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in); |
||||
jobject AIBinder_toJavaBinder(JNIEnv* env, AIBinder* binder); |
||||
|
||||
} // namespace ndk_util
|
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GPR_SUPPORT_BINDER_TRANSPORT
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_NDK_BINDER_H
|
@ -1,70 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_TRANSPORT_STREAM_RECEIVER_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_TRANSPORT_STREAM_RECEIVER_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <functional> |
||||
#include <string> |
||||
#include <vector> |
||||
|
||||
#include "absl/status/statusor.h" |
||||
#include "src/core/ext/transport/binder/wire_format/transaction.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
typedef int StreamIdentifier; |
||||
|
||||
class TransportStreamReceiver { |
||||
public: |
||||
virtual ~TransportStreamReceiver() = default; |
||||
|
||||
using InitialMetadataCallbackType = |
||||
std::function<void(absl::StatusOr<Metadata>)>; |
||||
using MessageDataCallbackType = |
||||
std::function<void(absl::StatusOr<std::string>)>; |
||||
using TrailingMetadataCallbackType = |
||||
std::function<void(absl::StatusOr<Metadata>, int)>; |
||||
|
||||
// Only handles single time invocation. Callback object will be deleted.
|
||||
// The callback should be valid until invocation or unregister.
|
||||
virtual void RegisterRecvInitialMetadata(StreamIdentifier id, |
||||
InitialMetadataCallbackType cb) = 0; |
||||
virtual void RegisterRecvMessage(StreamIdentifier id, |
||||
MessageDataCallbackType cb) = 0; |
||||
virtual void RegisterRecvTrailingMetadata( |
||||
StreamIdentifier id, TrailingMetadataCallbackType cb) = 0; |
||||
|
||||
// For the following functions, the second arguments are the transaction
|
||||
// result received from the lower level. If it is None, that means there's
|
||||
// something wrong when receiving the corresponding transaction. In such case,
|
||||
// we should cancel the gRPC callback as well.
|
||||
virtual void NotifyRecvInitialMetadata( |
||||
StreamIdentifier id, absl::StatusOr<Metadata> initial_metadata) = 0; |
||||
virtual void NotifyRecvMessage(StreamIdentifier id, |
||||
absl::StatusOr<std::string> message) = 0; |
||||
virtual void NotifyRecvTrailingMetadata( |
||||
StreamIdentifier id, absl::StatusOr<Metadata> trailing_metadata, |
||||
int status) = 0; |
||||
// Remove all entries associated with stream number `id`.
|
||||
virtual void CancelStream(StreamIdentifier id) = 0; |
||||
|
||||
static const absl::string_view kGrpcBinderTransportCancelledGracefully; |
||||
}; |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_TRANSPORT_STREAM_RECEIVER_H
|
@ -1,257 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <functional> |
||||
#include <string> |
||||
#include <utility> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "src/core/util/crash.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
const absl::string_view |
||||
TransportStreamReceiver::kGrpcBinderTransportCancelledGracefully = |
||||
"grpc-binder-transport: cancelled gracefully"; |
||||
|
||||
void TransportStreamReceiverImpl::RegisterRecvInitialMetadata( |
||||
StreamIdentifier id, InitialMetadataCallbackType cb) { |
||||
LOG(INFO) << __func__ << " id = " << id << " is_client = " << is_client_; |
||||
absl::StatusOr<Metadata> initial_metadata{}; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
CHECK_EQ(initial_metadata_cbs_.count(id), 0u); |
||||
auto iter = pending_initial_metadata_.find(id); |
||||
if (iter == pending_initial_metadata_.end()) { |
||||
if (trailing_metadata_recvd_.count(id)) { |
||||
cb(absl::CancelledError("")); |
||||
} else { |
||||
initial_metadata_cbs_[id] = std::move(cb); |
||||
} |
||||
cb = nullptr; |
||||
} else { |
||||
initial_metadata = std::move(iter->second.front()); |
||||
iter->second.pop(); |
||||
if (iter->second.empty()) { |
||||
pending_initial_metadata_.erase(iter); |
||||
} |
||||
} |
||||
} |
||||
if (cb != nullptr) { |
||||
cb(std::move(initial_metadata)); |
||||
} |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::RegisterRecvMessage( |
||||
StreamIdentifier id, MessageDataCallbackType cb) { |
||||
LOG(INFO) << __func__ << " id = " << id << " is_client = " << is_client_; |
||||
absl::StatusOr<std::string> message{}; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
CHECK_EQ(message_cbs_.count(id), 0u); |
||||
auto iter = pending_message_.find(id); |
||||
if (iter == pending_message_.end()) { |
||||
// If we'd already received trailing-metadata and there's no pending
|
||||
// messages, cancel the callback.
|
||||
if (trailing_metadata_recvd_.count(id)) { |
||||
cb(absl::CancelledError( |
||||
TransportStreamReceiver::kGrpcBinderTransportCancelledGracefully)); |
||||
} else { |
||||
message_cbs_[id] = std::move(cb); |
||||
} |
||||
cb = nullptr; |
||||
} else { |
||||
// We'll still keep all pending messages received before the trailing
|
||||
// metadata since they're issued before the end of stream, as promised by
|
||||
// WireReader which keeps transactions commit in-order.
|
||||
message = std::move(iter->second.front()); |
||||
iter->second.pop(); |
||||
if (iter->second.empty()) { |
||||
pending_message_.erase(iter); |
||||
} |
||||
} |
||||
} |
||||
if (cb != nullptr) { |
||||
cb(std::move(message)); |
||||
} |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::RegisterRecvTrailingMetadata( |
||||
StreamIdentifier id, TrailingMetadataCallbackType cb) { |
||||
LOG(INFO) << __func__ << " id = " << id << " is_client = " << is_client_; |
||||
std::pair<absl::StatusOr<Metadata>, int> trailing_metadata{}; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
CHECK_EQ(trailing_metadata_cbs_.count(id), 0u); |
||||
auto iter = pending_trailing_metadata_.find(id); |
||||
if (iter == pending_trailing_metadata_.end()) { |
||||
trailing_metadata_cbs_[id] = std::move(cb); |
||||
cb = nullptr; |
||||
} else { |
||||
trailing_metadata = std::move(iter->second.front()); |
||||
iter->second.pop(); |
||||
if (iter->second.empty()) { |
||||
pending_trailing_metadata_.erase(iter); |
||||
} |
||||
} |
||||
} |
||||
if (cb != nullptr) { |
||||
cb(std::move(trailing_metadata.first), trailing_metadata.second); |
||||
} |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::NotifyRecvInitialMetadata( |
||||
StreamIdentifier id, absl::StatusOr<Metadata> initial_metadata) { |
||||
LOG(INFO) << __func__ << " id = " << id << " is_client = " << is_client_; |
||||
if (!is_client_ && accept_stream_callback_ && initial_metadata.ok()) { |
||||
accept_stream_callback_(); |
||||
} |
||||
InitialMetadataCallbackType cb; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
auto iter = initial_metadata_cbs_.find(id); |
||||
if (iter != initial_metadata_cbs_.end()) { |
||||
cb = iter->second; |
||||
initial_metadata_cbs_.erase(iter); |
||||
} else { |
||||
pending_initial_metadata_[id].push(std::move(initial_metadata)); |
||||
return; |
||||
} |
||||
} |
||||
cb(std::move(initial_metadata)); |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::NotifyRecvMessage( |
||||
StreamIdentifier id, absl::StatusOr<std::string> message) { |
||||
LOG(INFO) << __func__ << " id = " << id << " is_client = " << is_client_; |
||||
MessageDataCallbackType cb; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
auto iter = message_cbs_.find(id); |
||||
if (iter != message_cbs_.end()) { |
||||
cb = iter->second; |
||||
message_cbs_.erase(iter); |
||||
} else { |
||||
pending_message_[id].push(std::move(message)); |
||||
return; |
||||
} |
||||
} |
||||
cb(std::move(message)); |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::NotifyRecvTrailingMetadata( |
||||
StreamIdentifier id, absl::StatusOr<Metadata> trailing_metadata, |
||||
int status) { |
||||
// Trailing metadata mark the end of the stream. Since TransportStreamReceiver
|
||||
// assumes in-order commitments of transactions and that trailing metadata is
|
||||
// parsed after message data, we can safely cancel all upcoming callbacks of
|
||||
// recv_message.
|
||||
LOG(INFO) << __func__ << " id = " << id << " is_client = " << is_client_; |
||||
OnRecvTrailingMetadata(id); |
||||
TrailingMetadataCallbackType cb; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
auto iter = trailing_metadata_cbs_.find(id); |
||||
if (iter != trailing_metadata_cbs_.end()) { |
||||
cb = iter->second; |
||||
trailing_metadata_cbs_.erase(iter); |
||||
} else { |
||||
pending_trailing_metadata_[id].emplace(std::move(trailing_metadata), |
||||
status); |
||||
return; |
||||
} |
||||
} |
||||
cb(std::move(trailing_metadata), status); |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::CancelInitialMetadataCallback( |
||||
StreamIdentifier id, absl::Status error) { |
||||
InitialMetadataCallbackType callback = nullptr; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
auto iter = initial_metadata_cbs_.find(id); |
||||
if (iter != initial_metadata_cbs_.end()) { |
||||
callback = std::move(iter->second); |
||||
initial_metadata_cbs_.erase(iter); |
||||
} |
||||
} |
||||
if (callback != nullptr) { |
||||
std::move(callback)(error); |
||||
} |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::CancelMessageCallback(StreamIdentifier id, |
||||
absl::Status error) { |
||||
MessageDataCallbackType callback = nullptr; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
auto iter = message_cbs_.find(id); |
||||
if (iter != message_cbs_.end()) { |
||||
callback = std::move(iter->second); |
||||
message_cbs_.erase(iter); |
||||
} |
||||
} |
||||
if (callback != nullptr) { |
||||
std::move(callback)(error); |
||||
} |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::CancelTrailingMetadataCallback( |
||||
StreamIdentifier id, absl::Status error) { |
||||
TrailingMetadataCallbackType callback = nullptr; |
||||
{ |
||||
grpc_core::MutexLock l(&m_); |
||||
auto iter = trailing_metadata_cbs_.find(id); |
||||
if (iter != trailing_metadata_cbs_.end()) { |
||||
callback = std::move(iter->second); |
||||
trailing_metadata_cbs_.erase(iter); |
||||
} |
||||
} |
||||
if (callback != nullptr) { |
||||
std::move(callback)(error, 0); |
||||
} |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::OnRecvTrailingMetadata(StreamIdentifier id) { |
||||
LOG(INFO) << __func__ << " id = " << id << " is_client = " << is_client_; |
||||
m_.Lock(); |
||||
trailing_metadata_recvd_.insert(id); |
||||
m_.Unlock(); |
||||
CancelInitialMetadataCallback(id, absl::CancelledError("")); |
||||
CancelMessageCallback( |
||||
id, |
||||
absl::CancelledError( |
||||
TransportStreamReceiver::kGrpcBinderTransportCancelledGracefully)); |
||||
} |
||||
|
||||
void TransportStreamReceiverImpl::CancelStream(StreamIdentifier id) { |
||||
LOG(INFO) << __func__ << " id = " << id << " is_client = " << is_client_; |
||||
CancelInitialMetadataCallback(id, absl::CancelledError("Stream cancelled")); |
||||
CancelMessageCallback(id, absl::CancelledError("Stream cancelled")); |
||||
CancelTrailingMetadataCallback(id, absl::CancelledError("Stream cancelled")); |
||||
grpc_core::MutexLock l(&m_); |
||||
trailing_metadata_recvd_.erase(id); |
||||
pending_initial_metadata_.erase(id); |
||||
pending_message_.erase(id); |
||||
pending_trailing_metadata_.erase(id); |
||||
} |
||||
} // namespace grpc_binder
|
||||
#endif |
@ -1,112 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_TRANSPORT_STREAM_RECEIVER_IMPL_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_TRANSPORT_STREAM_RECEIVER_IMPL_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <functional> |
||||
#include <map> |
||||
#include <queue> |
||||
#include <set> |
||||
#include <string> |
||||
#include <vector> |
||||
|
||||
#include "src/core/ext/transport/binder/utils/transport_stream_receiver.h" |
||||
#include "src/core/util/sync.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
// Routes the data received from transport to corresponding streams
|
||||
class TransportStreamReceiverImpl : public TransportStreamReceiver { |
||||
public: |
||||
explicit TransportStreamReceiverImpl( |
||||
bool is_client, std::function<void()> accept_stream_callback = nullptr) |
||||
: is_client_(is_client), |
||||
accept_stream_callback_(accept_stream_callback) {} |
||||
void RegisterRecvInitialMetadata(StreamIdentifier id, |
||||
InitialMetadataCallbackType cb) override; |
||||
void RegisterRecvMessage(StreamIdentifier id, |
||||
MessageDataCallbackType cb) override; |
||||
void RegisterRecvTrailingMetadata(StreamIdentifier id, |
||||
TrailingMetadataCallbackType cb) override; |
||||
void NotifyRecvInitialMetadata( |
||||
StreamIdentifier id, absl::StatusOr<Metadata> initial_metadata) override; |
||||
void NotifyRecvMessage(StreamIdentifier id, |
||||
absl::StatusOr<std::string> message) override; |
||||
void NotifyRecvTrailingMetadata(StreamIdentifier id, |
||||
absl::StatusOr<Metadata> trailing_metadata, |
||||
int status) override; |
||||
|
||||
void CancelStream(StreamIdentifier id) override; |
||||
|
||||
private: |
||||
// Trailing metadata marks the end of one-side of the stream. Thus, after
|
||||
// receiving trailing metadata from the other-end, we know that there will
|
||||
// never be in-coming message data anymore, and all recv_message callbacks
|
||||
// (as well as recv_initial_metadata callback, if there's any) registered will
|
||||
// never be satisfied. This function cancels all such callbacks gracefully
|
||||
// (with absl::OkStatus()) to avoid being blocked waiting for them.
|
||||
void OnRecvTrailingMetadata(StreamIdentifier id); |
||||
|
||||
void CancelInitialMetadataCallback(StreamIdentifier id, absl::Status error); |
||||
void CancelMessageCallback(StreamIdentifier id, absl::Status error); |
||||
void CancelTrailingMetadataCallback(StreamIdentifier id, absl::Status error); |
||||
|
||||
std::map<StreamIdentifier, InitialMetadataCallbackType> initial_metadata_cbs_; |
||||
std::map<StreamIdentifier, MessageDataCallbackType> message_cbs_; |
||||
std::map<StreamIdentifier, TrailingMetadataCallbackType> |
||||
trailing_metadata_cbs_; |
||||
// TODO(waynetu): Better thread safety design. For example, use separate
|
||||
// mutexes for different type of messages.
|
||||
grpc_core::Mutex m_; |
||||
// TODO(waynetu): gRPC surface layer will not wait for the current message to
|
||||
// be delivered before sending the next message. The following implementation
|
||||
// is still buggy with the current implementation of wire writer if
|
||||
// transaction issued first completes after the one issued later does. This is
|
||||
// because we just take the first element out of the queue and assume it's the
|
||||
// one issued first without further checking, which results in callbacks being
|
||||
// invoked with incorrect data.
|
||||
//
|
||||
// This should be fixed in the wire writer level and make sure out-of-order
|
||||
// messages will be re-ordered by it. In such case, the queueing approach will
|
||||
// work fine. Refer to the TODO in WireWriterImpl::ProcessTransaction() at
|
||||
// wire_reader_impl.cc for detecting and resolving out-of-order transactions.
|
||||
//
|
||||
// TODO(waynetu): Use absl::flat_hash_map.
|
||||
std::map<StreamIdentifier, std::queue<absl::StatusOr<Metadata>>> |
||||
pending_initial_metadata_ ABSL_GUARDED_BY(m_); |
||||
std::map<StreamIdentifier, std::queue<absl::StatusOr<std::string>>> |
||||
pending_message_ ABSL_GUARDED_BY(m_); |
||||
std::map<StreamIdentifier, |
||||
std::queue<std::pair<absl::StatusOr<Metadata>, int>>> |
||||
pending_trailing_metadata_ ABSL_GUARDED_BY(m_); |
||||
// Record whether or not the recv_message callbacks of a given stream is
|
||||
// cancelled. Although we explicitly cancel the registered recv_message() in
|
||||
// CancelRecvMessageCallbacksDueToTrailingMetadata(), there are chances that
|
||||
// the registration comes "after" we receive trailing metadata. Therefore,
|
||||
// when RegisterRecvMessage() gets called, we should check whether
|
||||
// recv_message_cancelled_ contains the corresponding stream ID, and if so,
|
||||
// directly cancel the callback gracefully without pending it.
|
||||
std::set<StreamIdentifier> trailing_metadata_recvd_ ABSL_GUARDED_BY(m_); |
||||
|
||||
bool is_client_; |
||||
// Called when receiving initial metadata to inform the server about a new
|
||||
// stream.
|
||||
std::function<void()> accept_stream_callback_; |
||||
}; |
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_UTILS_TRANSPORT_STREAM_RECEIVER_IMPL_H
|
@ -1,104 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <cstdint> |
||||
#include <functional> |
||||
#include <memory> |
||||
#include <string> |
||||
|
||||
#include "absl/status/status.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder_constants.h" |
||||
#include "src/core/util/orphanable.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
class HasRawBinder { |
||||
public: |
||||
virtual ~HasRawBinder() = default; |
||||
virtual void* GetRawBinder() = 0; |
||||
}; |
||||
|
||||
class Binder; |
||||
|
||||
// TODO(waynetu): We might need other methods as well.
|
||||
// TODO(waynetu): Find a better way to express the returned status than
|
||||
// binder_status_t.
|
||||
class WritableParcel { |
||||
public: |
||||
virtual ~WritableParcel() = default; |
||||
virtual int32_t GetDataSize() const = 0; |
||||
virtual absl::Status WriteInt32(int32_t data) = 0; |
||||
virtual absl::Status WriteInt64(int64_t data) = 0; |
||||
virtual absl::Status WriteBinder(HasRawBinder* binder) = 0; |
||||
virtual absl::Status WriteString(absl::string_view s) = 0; |
||||
virtual absl::Status WriteByteArray(const int8_t* buffer, int32_t length) = 0; |
||||
|
||||
absl::Status WriteByteArrayWithLength(absl::string_view buffer) { |
||||
absl::Status status = WriteInt32(buffer.length()); |
||||
if (!status.ok()) return status; |
||||
if (buffer.empty()) return absl::OkStatus(); |
||||
return WriteByteArray(reinterpret_cast<const int8_t*>(buffer.data()), |
||||
buffer.length()); |
||||
} |
||||
}; |
||||
|
||||
// TODO(waynetu): We might need other methods as well.
|
||||
// TODO(waynetu): Find a better way to express the returned status than
|
||||
// binder_status_t.
|
||||
class ReadableParcel { |
||||
public: |
||||
virtual ~ReadableParcel() = default; |
||||
virtual int32_t GetDataSize() const = 0; |
||||
virtual absl::Status ReadInt32(int32_t* data) = 0; |
||||
virtual absl::Status ReadInt64(int64_t* data) = 0; |
||||
virtual absl::Status ReadBinder(std::unique_ptr<Binder>* data) = 0; |
||||
virtual absl::Status ReadByteArray(std::string* data) = 0; |
||||
virtual absl::Status ReadString(std::string* str) = 0; |
||||
}; |
||||
|
||||
class TransactionReceiver : public HasRawBinder { |
||||
public: |
||||
using OnTransactCb = |
||||
std::function<absl::Status(transaction_code_t, ReadableParcel*, int uid)>; |
||||
|
||||
~TransactionReceiver() override = default; |
||||
}; |
||||
|
||||
class WireReader; |
||||
|
||||
class Binder : public HasRawBinder { |
||||
public: |
||||
~Binder() override = default; |
||||
|
||||
virtual void Initialize() = 0; |
||||
virtual absl::Status PrepareTransaction() = 0; |
||||
virtual absl::Status Transact(BinderTransportTxCode tx_code) = 0; |
||||
|
||||
virtual WritableParcel* GetWritableParcel() const = 0; |
||||
|
||||
// TODO(waynetu): Can we decouple the receiver from the binder?
|
||||
virtual std::unique_ptr<TransactionReceiver> ConstructTxReceiver( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb transact_cb) const = 0; |
||||
}; |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_H
|
@ -1,308 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
|
||||
#include <map> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/memory/memory.h" |
||||
#include "absl/strings/str_cat.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder_android.h" |
||||
#include "src/core/util/crash.h" |
||||
#include "src/core/util/sync.h" |
||||
|
||||
namespace grpc_binder { |
||||
namespace { |
||||
|
||||
struct BinderUserData { |
||||
explicit BinderUserData(grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb* callback) |
||||
: wire_reader_ref(wire_reader_ref), callback(callback) {} |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref; |
||||
TransactionReceiver::OnTransactCb* callback; |
||||
}; |
||||
|
||||
struct OnCreateArgs { |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref; |
||||
TransactionReceiver::OnTransactCb* callback; |
||||
}; |
||||
|
||||
void* f_onCreate_userdata(void* data) { |
||||
auto* args = static_cast<OnCreateArgs*>(data); |
||||
return new BinderUserData(args->wire_reader_ref, args->callback); |
||||
} |
||||
|
||||
void f_onDestroy_delete(void* data) { |
||||
auto* user_data = static_cast<BinderUserData*>(data); |
||||
delete user_data; |
||||
} |
||||
|
||||
void* f_onCreate_noop(void* /*args*/) { return nullptr; } |
||||
void f_onDestroy_noop(void* /*userData*/) {} |
||||
|
||||
// TODO(mingcl): Consider if thread safety is a requirement here
|
||||
ndk_util::binder_status_t f_onTransact(ndk_util::AIBinder* binder, |
||||
transaction_code_t code, |
||||
const ndk_util::AParcel* in, |
||||
ndk_util::AParcel* /*out*/) { |
||||
LOG(INFO) << __func__; |
||||
LOG(INFO) << "tx code = " << code; |
||||
|
||||
auto* user_data = |
||||
static_cast<BinderUserData*>(ndk_util::AIBinder_getUserData(binder)); |
||||
TransactionReceiver::OnTransactCb* callback = user_data->callback; |
||||
// Wrap the parcel in a ReadableParcel.
|
||||
std::unique_ptr<ReadableParcel> output = |
||||
std::make_unique<ReadableParcelAndroid>(in); |
||||
// The lock should be released "after" the callback finishes.
|
||||
absl::Status status = |
||||
(*callback)(code, output.get(), ndk_util::AIBinder_getCallingUid()); |
||||
if (status.ok()) { |
||||
return ndk_util::STATUS_OK; |
||||
} else { |
||||
LOG(ERROR) << "Callback failed: " << status.ToString(); |
||||
return ndk_util::STATUS_UNKNOWN_ERROR; |
||||
} |
||||
} |
||||
|
||||
// StdStringAllocator, ReadString, StdVectorAllocator, and ReadVector's
|
||||
// implementations are copied from android/binder_parcel_utils.h
|
||||
// We cannot include the header because it does not compile in C++11
|
||||
|
||||
bool StdStringAllocator(void* stringData, int32_t length, char** buffer) { |
||||
if (length <= 0) return false; |
||||
|
||||
std::string* str = static_cast<std::string*>(stringData); |
||||
str->resize(static_cast<size_t>(length) - 1); |
||||
*buffer = &(*str)[0]; |
||||
return true; |
||||
} |
||||
|
||||
ndk_util::binder_status_t AParcelReadString(const ndk_util::AParcel* parcel, |
||||
std::string* str) { |
||||
void* stringData = static_cast<void*>(str); |
||||
return ndk_util::AParcel_readString(parcel, stringData, StdStringAllocator); |
||||
} |
||||
|
||||
template <typename T> |
||||
bool StdVectorAllocator(void* vectorData, int32_t length, T** outBuffer) { |
||||
if (length < 0) return false; |
||||
|
||||
std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData); |
||||
if (static_cast<size_t>(length) > vec->max_size()) return false; |
||||
|
||||
vec->resize(static_cast<size_t>(length)); |
||||
*outBuffer = vec->data(); |
||||
return true; |
||||
} |
||||
|
||||
ndk_util::binder_status_t AParcelReadVector(const ndk_util::AParcel* parcel, |
||||
std::vector<uint8_t>* vec) { |
||||
void* vectorData = static_cast<void*>(vec); |
||||
return ndk_util::AParcel_readByteArray(parcel, vectorData, |
||||
StdVectorAllocator<int8_t>); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
ndk_util::SpAIBinder FromJavaBinder(JNIEnv* jni_env, jobject binder) { |
||||
return ndk_util::SpAIBinder( |
||||
ndk_util::AIBinder_fromJavaBinder(jni_env, binder)); |
||||
} |
||||
|
||||
TransactionReceiverAndroid::TransactionReceiverAndroid( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
OnTransactCb transact_cb) |
||||
: transact_cb_(transact_cb) { |
||||
// TODO(mingcl): For now interface descriptor is always empty, figure out if
|
||||
// we want it to be something more meaningful (we can probably manually change
|
||||
// interface descriptor by modifying Java code's reply to
|
||||
// os.IBinder.INTERFACE_TRANSACTION)
|
||||
ndk_util::AIBinder_Class* aibinder_class = ndk_util::AIBinder_Class_define( |
||||
/*interfaceDescriptor=*/"", f_onCreate_userdata, f_onDestroy_delete, |
||||
f_onTransact); |
||||
|
||||
ndk_util::AIBinder_Class_disableInterfaceTokenHeader(aibinder_class); |
||||
|
||||
// Pass the on-transact callback to the on-create function of the binder. The
|
||||
// on-create function equips the callback with a mutex and gives it to the
|
||||
// user data stored in the binder which can be retrieved later.
|
||||
// Also Ref() (called implicitly by the copy constructor of RefCountedPtr) the
|
||||
// wire reader so that it would not be destructed during the callback
|
||||
// invocation.
|
||||
OnCreateArgs args; |
||||
args.wire_reader_ref = wire_reader_ref; |
||||
args.callback = &transact_cb_; |
||||
binder_ = ndk_util::AIBinder_new(aibinder_class, &args); |
||||
CHECK(binder_); |
||||
LOG(INFO) << "ndk_util::AIBinder_associateClass = " |
||||
<< ndk_util::AIBinder_associateClass(binder_, aibinder_class); |
||||
} |
||||
|
||||
TransactionReceiverAndroid::~TransactionReceiverAndroid() { |
||||
// Release the binder.
|
||||
ndk_util::AIBinder_decStrong(binder_); |
||||
} |
||||
|
||||
namespace { |
||||
|
||||
ndk_util::binder_status_t f_onTransact_noop(ndk_util::AIBinder* /*binder*/, |
||||
transaction_code_t /*code*/, |
||||
const ndk_util::AParcel* /*in*/, |
||||
ndk_util::AParcel* /*out*/) { |
||||
return {}; |
||||
} |
||||
|
||||
void AssociateWithNoopClass(ndk_util::AIBinder* binder) { |
||||
// Need to associate class before using it
|
||||
ndk_util::AIBinder_Class* aibinder_class = ndk_util::AIBinder_Class_define( |
||||
"", f_onCreate_noop, f_onDestroy_noop, f_onTransact_noop); |
||||
|
||||
ndk_util::AIBinder_Class_disableInterfaceTokenHeader(aibinder_class); |
||||
|
||||
LOG(INFO) << "ndk_util::AIBinder_associateClass = " |
||||
<< ndk_util::AIBinder_associateClass(binder, aibinder_class); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
void BinderAndroid::Initialize() { |
||||
ndk_util::AIBinder* binder = binder_.get(); |
||||
AssociateWithNoopClass(binder); |
||||
} |
||||
|
||||
absl::Status BinderAndroid::PrepareTransaction() { |
||||
ndk_util::AIBinder* binder = binder_.get(); |
||||
return ndk_util::AIBinder_prepareTransaction( |
||||
binder, &input_parcel_->parcel_) == ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError( |
||||
"ndk_util::AIBinder_prepareTransaction failed"); |
||||
} |
||||
|
||||
absl::Status BinderAndroid::Transact(BinderTransportTxCode tx_code) { |
||||
ndk_util::AIBinder* binder = binder_.get(); |
||||
// We only do one-way transaction and thus the output parcel is never used.
|
||||
ndk_util::AParcel* unused_output_parcel; |
||||
absl::Status result = |
||||
(ndk_util::AIBinder_transact( |
||||
binder, static_cast<transaction_code_t>(tx_code), |
||||
&input_parcel_->parcel_, &unused_output_parcel, |
||||
ndk_util::FLAG_ONEWAY) == ndk_util::STATUS_OK) |
||||
? absl::OkStatus() |
||||
: absl::InternalError("ndk_util::AIBinder_transact failed"); |
||||
ndk_util::AParcel_delete(unused_output_parcel); |
||||
return result; |
||||
} |
||||
|
||||
std::unique_ptr<TransactionReceiver> BinderAndroid::ConstructTxReceiver( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb transact_cb) const { |
||||
return std::make_unique<TransactionReceiverAndroid>(wire_reader_ref, |
||||
transact_cb); |
||||
} |
||||
|
||||
int32_t WritableParcelAndroid::GetDataSize() const { |
||||
return ndk_util::AParcel_getDataSize(parcel_); |
||||
} |
||||
|
||||
absl::Status WritableParcelAndroid::WriteInt32(int32_t data) { |
||||
return ndk_util::AParcel_writeInt32(parcel_, data) == ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError("AParcel_writeInt32 failed"); |
||||
} |
||||
|
||||
absl::Status WritableParcelAndroid::WriteInt64(int64_t data) { |
||||
return ndk_util::AParcel_writeInt64(parcel_, data) == ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError("AParcel_writeInt64 failed"); |
||||
} |
||||
|
||||
absl::Status WritableParcelAndroid::WriteBinder(HasRawBinder* binder) { |
||||
return ndk_util::AParcel_writeStrongBinder( |
||||
parcel_, reinterpret_cast<ndk_util::AIBinder*>( |
||||
binder->GetRawBinder())) == ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError("AParcel_writeStrongBinder failed"); |
||||
} |
||||
|
||||
absl::Status WritableParcelAndroid::WriteString(absl::string_view s) { |
||||
return ndk_util::AParcel_writeString(parcel_, s.data(), s.length()) == |
||||
ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError("AParcel_writeString failed"); |
||||
} |
||||
|
||||
absl::Status WritableParcelAndroid::WriteByteArray(const int8_t* buffer, |
||||
int32_t length) { |
||||
return ndk_util::AParcel_writeByteArray(parcel_, buffer, length) == |
||||
ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError("AParcel_writeByteArray failed"); |
||||
} |
||||
|
||||
int32_t ReadableParcelAndroid::GetDataSize() const { |
||||
return ndk_util::AParcel_getDataSize(parcel_); |
||||
} |
||||
|
||||
absl::Status ReadableParcelAndroid::ReadInt32(int32_t* data) { |
||||
return ndk_util::AParcel_readInt32(parcel_, data) == ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError("AParcel_readInt32 failed"); |
||||
} |
||||
|
||||
absl::Status ReadableParcelAndroid::ReadInt64(int64_t* data) { |
||||
return ndk_util::AParcel_readInt64(parcel_, data) == ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError("AParcel_readInt64 failed"); |
||||
} |
||||
|
||||
absl::Status ReadableParcelAndroid::ReadBinder(std::unique_ptr<Binder>* data) { |
||||
ndk_util::AIBinder* binder; |
||||
if (AParcel_readStrongBinder(parcel_, &binder) != ndk_util::STATUS_OK) { |
||||
*data = nullptr; |
||||
return absl::InternalError("AParcel_readStrongBinder failed"); |
||||
} |
||||
*data = std::make_unique<BinderAndroid>(ndk_util::SpAIBinder(binder)); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status ReadableParcelAndroid::ReadByteArray(std::string* data) { |
||||
std::vector<uint8_t> vec; |
||||
if (AParcelReadVector(parcel_, &vec) == ndk_util::STATUS_OK) { |
||||
data->resize(vec.size()); |
||||
if (!vec.empty()) { |
||||
memcpy(&((*data)[0]), vec.data(), vec.size()); |
||||
} |
||||
return absl::OkStatus(); |
||||
} |
||||
return absl::InternalError("AParcel_readByteArray failed"); |
||||
} |
||||
|
||||
absl::Status ReadableParcelAndroid::ReadString(std::string* str) { |
||||
return AParcelReadString(parcel_, str) == ndk_util::STATUS_OK |
||||
? absl::OkStatus() |
||||
: absl::InternalError("AParcel_readString failed"); |
||||
} |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GPR_SUPPORT_BINDER_TRANSPORT
|
||||
#endif |
@ -1,121 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_ANDROID_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_ANDROID_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifdef GPR_SUPPORT_BINDER_TRANSPORT |
||||
|
||||
#include <jni.h> |
||||
|
||||
#include <memory> |
||||
|
||||
#include "absl/memory/memory.h" |
||||
#include "src/core/ext/transport/binder/utils/binder_auto_utils.h" |
||||
#include "src/core/ext/transport/binder/utils/ndk_binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_reader.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
ndk_util::SpAIBinder FromJavaBinder(JNIEnv* jni_env, jobject binder); |
||||
|
||||
class BinderAndroid; |
||||
|
||||
class WritableParcelAndroid final : public WritableParcel { |
||||
public: |
||||
WritableParcelAndroid() = default; |
||||
explicit WritableParcelAndroid(ndk_util::AParcel* parcel) : parcel_(parcel) {} |
||||
~WritableParcelAndroid() override = default; |
||||
|
||||
int32_t GetDataSize() const override; |
||||
absl::Status WriteInt32(int32_t data) override; |
||||
absl::Status WriteInt64(int64_t data) override; |
||||
absl::Status WriteBinder(HasRawBinder* binder) override; |
||||
absl::Status WriteString(absl::string_view s) override; |
||||
absl::Status WriteByteArray(const int8_t* buffer, int32_t length) override; |
||||
|
||||
private: |
||||
ndk_util::AParcel* parcel_ = nullptr; |
||||
|
||||
friend class BinderAndroid; |
||||
}; |
||||
|
||||
class ReadableParcelAndroid final : public ReadableParcel { |
||||
public: |
||||
ReadableParcelAndroid() = default; |
||||
// TODO(waynetu): Get rid of the const_cast.
|
||||
explicit ReadableParcelAndroid(const ndk_util::AParcel* parcel) |
||||
: parcel_(parcel) {} |
||||
~ReadableParcelAndroid() override = default; |
||||
|
||||
int32_t GetDataSize() const override; |
||||
absl::Status ReadInt32(int32_t* data) override; |
||||
absl::Status ReadInt64(int64_t* data) override; |
||||
absl::Status ReadBinder(std::unique_ptr<Binder>* data) override; |
||||
absl::Status ReadByteArray(std::string* data) override; |
||||
absl::Status ReadString(std::string* str) override; |
||||
|
||||
private: |
||||
const ndk_util::AParcel* parcel_ = nullptr; |
||||
|
||||
friend class BinderAndroid; |
||||
}; |
||||
|
||||
class BinderAndroid final : public Binder { |
||||
public: |
||||
explicit BinderAndroid(ndk_util::SpAIBinder binder) |
||||
: binder_(binder), |
||||
input_parcel_(std::make_unique<WritableParcelAndroid>()) {} |
||||
~BinderAndroid() override = default; |
||||
|
||||
void* GetRawBinder() override { return binder_.get(); } |
||||
|
||||
void Initialize() override; |
||||
absl::Status PrepareTransaction() override; |
||||
absl::Status Transact(BinderTransportTxCode tx_code) override; |
||||
|
||||
WritableParcel* GetWritableParcel() const override { |
||||
return input_parcel_.get(); |
||||
} |
||||
|
||||
std::unique_ptr<TransactionReceiver> ConstructTxReceiver( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
TransactionReceiver::OnTransactCb transact_cb) const override; |
||||
|
||||
private: |
||||
ndk_util::SpAIBinder binder_; |
||||
std::unique_ptr<WritableParcelAndroid> input_parcel_; |
||||
}; |
||||
|
||||
class TransactionReceiverAndroid final : public TransactionReceiver { |
||||
public: |
||||
TransactionReceiverAndroid( |
||||
grpc_core::RefCountedPtr<WireReader> wire_reader_ref, |
||||
OnTransactCb transaction_cb); |
||||
~TransactionReceiverAndroid() override; |
||||
void* GetRawBinder() override { return binder_; } |
||||
|
||||
private: |
||||
ndk_util::AIBinder* binder_; |
||||
OnTransactCb transact_cb_; |
||||
}; |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GPR_SUPPORT_BINDER_TRANSPORT
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_ANDROID_H
|
@ -1,29 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include "src/core/ext/transport/binder/wire_format/binder_constants.h" |
||||
|
||||
ABSL_CONST_INIT const int FIRST_CALL_TRANSACTION = 0x00000001; |
||||
ABSL_CONST_INIT const int LAST_CALL_TRANSACTION = 0x00FFFFFF; |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
ABSL_CONST_INIT const int kFirstCallId = FIRST_CALL_TRANSACTION + 1000; |
||||
|
||||
} // namespace grpc_binder
|
||||
#endif |
@ -1,43 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_CONSTANTS_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_CONSTANTS_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <cstdint> |
||||
|
||||
#include "absl/base/attributes.h" |
||||
|
||||
using transaction_code_t = uint32_t; |
||||
|
||||
ABSL_CONST_INIT extern const int FIRST_CALL_TRANSACTION; |
||||
ABSL_CONST_INIT extern const int LAST_CALL_TRANSACTION; |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
enum class BinderTransportTxCode : int32_t { |
||||
SETUP_TRANSPORT = 1, |
||||
SHUTDOWN_TRANSPORT = 2, |
||||
ACKNOWLEDGE_BYTES = 3, |
||||
PING = 4, |
||||
PING_RESPONSE = 5, |
||||
}; |
||||
|
||||
ABSL_CONST_INIT extern const int kFirstCallId; |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_BINDER_CONSTANTS_H
|
@ -1,33 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include "src/core/ext/transport/binder/wire_format/transaction.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
ABSL_CONST_INIT const int kFlagPrefix = 0x1; |
||||
ABSL_CONST_INIT const int kFlagMessageData = 0x2; |
||||
ABSL_CONST_INIT const int kFlagSuffix = 0x4; |
||||
ABSL_CONST_INIT const int kFlagOutOfBandClose = 0x8; |
||||
ABSL_CONST_INIT const int kFlagExpectSingleMessage = 0x10; |
||||
ABSL_CONST_INIT const int kFlagStatusDescription = 0x20; |
||||
ABSL_CONST_INIT const int kFlagMessageDataIsParcelable = 0x40; |
||||
ABSL_CONST_INIT const int kFlagMessageDataIsPartial = 0x80; |
||||
|
||||
} // namespace grpc_binder
|
||||
#endif |
@ -1,106 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_TRANSACTION_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_TRANSACTION_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <string> |
||||
#include <vector> |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "src/core/util/crash.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
ABSL_CONST_INIT extern const int kFlagPrefix; |
||||
ABSL_CONST_INIT extern const int kFlagMessageData; |
||||
ABSL_CONST_INIT extern const int kFlagSuffix; |
||||
ABSL_CONST_INIT extern const int kFlagOutOfBandClose; |
||||
ABSL_CONST_INIT extern const int kFlagExpectSingleMessage; |
||||
ABSL_CONST_INIT extern const int kFlagStatusDescription; |
||||
ABSL_CONST_INIT extern const int kFlagMessageDataIsParcelable; |
||||
ABSL_CONST_INIT extern const int kFlagMessageDataIsPartial; |
||||
|
||||
using Metadata = std::vector<std::pair<std::string, std::string>>; |
||||
|
||||
class Transaction { |
||||
public: |
||||
Transaction(int tx_code, bool is_client) |
||||
: tx_code_(tx_code), is_client_(is_client) {} |
||||
// TODO(mingcl): Consider using string_view
|
||||
void SetPrefix(Metadata prefix_metadata) { |
||||
prefix_metadata_ = prefix_metadata; |
||||
CHECK_EQ((flags_ & kFlagPrefix), 0); |
||||
flags_ |= kFlagPrefix; |
||||
} |
||||
void SetMethodRef(std::string method_ref) { |
||||
CHECK(is_client_); |
||||
method_ref_ = method_ref; |
||||
} |
||||
void SetData(std::string message_data) { |
||||
message_data_ = message_data; |
||||
CHECK_EQ((flags_ & kFlagMessageData), 0); |
||||
flags_ |= kFlagMessageData; |
||||
} |
||||
void SetSuffix(Metadata suffix_metadata) { |
||||
if (is_client_) CHECK(suffix_metadata.empty()); |
||||
suffix_metadata_ = suffix_metadata; |
||||
CHECK_EQ((flags_ & kFlagSuffix), 0); |
||||
flags_ |= kFlagSuffix; |
||||
} |
||||
void SetStatusDescription(std::string status_desc) { |
||||
CHECK(!is_client_); |
||||
CHECK_EQ((flags_ & kFlagStatusDescription), 0); |
||||
status_desc_ = status_desc; |
||||
} |
||||
void SetStatus(int status) { |
||||
CHECK(!is_client_); |
||||
CHECK_EQ((flags_ >> 16), 0); |
||||
CHECK(status < (1 << 16)); |
||||
flags_ |= (status << 16); |
||||
} |
||||
|
||||
bool IsClient() const { return is_client_; } |
||||
bool IsServer() const { return !is_client_; } |
||||
int GetTxCode() const { return tx_code_; } |
||||
int GetFlags() const { return flags_; } |
||||
|
||||
absl::string_view GetMethodRef() const { return method_ref_; } |
||||
const Metadata& GetPrefixMetadata() const { return prefix_metadata_; } |
||||
const Metadata& GetSuffixMetadata() const { return suffix_metadata_; } |
||||
absl::string_view GetMessageData() const { return message_data_; } |
||||
absl::string_view GetStatusDesc() const { return status_desc_; } |
||||
|
||||
Transaction(const Transaction&) = delete; |
||||
void operator=(const Transaction&) = delete; |
||||
|
||||
private: |
||||
int tx_code_; |
||||
bool is_client_; |
||||
Metadata prefix_metadata_; |
||||
Metadata suffix_metadata_; |
||||
std::string method_ref_; |
||||
std::string message_data_; |
||||
std::string status_desc_; |
||||
|
||||
int flags_ = 0; |
||||
}; |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_TRANSACTION_H
|
@ -1,38 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_READER_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_READER_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <memory> |
||||
#include <utility> |
||||
|
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_writer.h" |
||||
#include "src/core/util/orphanable.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
class WireReader : public grpc_core::InternallyRefCounted<WireReader> { |
||||
public: |
||||
~WireReader() override = default; |
||||
virtual std::shared_ptr<WireWriter> SetupTransport( |
||||
std::unique_ptr<Binder> endpoint_binder) = 0; |
||||
}; |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_READER_H
|
@ -1,450 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/core/ext/transport/binder/wire_format/wire_reader_impl.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <functional> |
||||
#include <limits> |
||||
#include <string> |
||||
#include <utility> |
||||
#include <vector> |
||||
|
||||
#include "absl/functional/any_invocable.h" |
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/memory/memory.h" |
||||
#include "absl/status/statusor.h" |
||||
#include "src/core/ext/transport/binder/utils/transport_stream_receiver.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_writer.h" |
||||
#include "src/core/util/crash.h" |
||||
#include "src/core/util/status_helper.h" |
||||
|
||||
namespace grpc_binder { |
||||
namespace { |
||||
|
||||
const int32_t kWireFormatVersion = 1; |
||||
const char kAuthorityMetadataKey[] = ":authority"; |
||||
|
||||
absl::StatusOr<Metadata> parse_metadata(ReadableParcel* reader) { |
||||
int num_header; |
||||
GRPC_RETURN_IF_ERROR(reader->ReadInt32(&num_header)); |
||||
if (num_header < 0) { |
||||
return absl::InvalidArgumentError("num_header cannot be negative"); |
||||
} |
||||
std::vector<std::pair<std::string, std::string>> ret; |
||||
for (int i = 0; i < num_header; i++) { |
||||
int count; |
||||
GRPC_RETURN_IF_ERROR(reader->ReadInt32(&count)); |
||||
std::string key{}; |
||||
if (count > 0) GRPC_RETURN_IF_ERROR(reader->ReadByteArray(&key)); |
||||
GRPC_RETURN_IF_ERROR(reader->ReadInt32(&count)); |
||||
std::string value{}; |
||||
if (count > 0) GRPC_RETURN_IF_ERROR(reader->ReadByteArray(&value)); |
||||
ret.emplace_back(key, value); |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
WireReaderImpl::WireReaderImpl( |
||||
std::shared_ptr<TransportStreamReceiver> transport_stream_receiver, |
||||
bool is_client, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy, |
||||
std::function<void()> on_destruct_callback) |
||||
: transport_stream_receiver_(std::move(transport_stream_receiver)), |
||||
is_client_(is_client), |
||||
security_policy_(security_policy), |
||||
on_destruct_callback_(on_destruct_callback) {} |
||||
|
||||
WireReaderImpl::~WireReaderImpl() { |
||||
if (on_destruct_callback_) { |
||||
on_destruct_callback_(); |
||||
} |
||||
} |
||||
|
||||
std::shared_ptr<WireWriter> WireReaderImpl::SetupTransport( |
||||
std::unique_ptr<Binder> binder) { |
||||
if (!is_client_) { |
||||
connected_ = true; |
||||
SendSetupTransport(binder.get()); |
||||
{ |
||||
grpc_core::MutexLock lock(&mu_); |
||||
wire_writer_ = std::make_shared<WireWriterImpl>(std::move(binder)); |
||||
} |
||||
wire_writer_ready_notification_.Notify(); |
||||
return wire_writer_; |
||||
} else { |
||||
SendSetupTransport(binder.get()); |
||||
auto other_end_binder = RecvSetupTransport(); |
||||
{ |
||||
grpc_core::MutexLock lock(&mu_); |
||||
connected_ = true; |
||||
wire_writer_ = |
||||
std::make_shared<WireWriterImpl>(std::move(other_end_binder)); |
||||
} |
||||
wire_writer_ready_notification_.Notify(); |
||||
return wire_writer_; |
||||
} |
||||
} |
||||
|
||||
void WireReaderImpl::SendSetupTransport(Binder* binder) { |
||||
binder->Initialize(); |
||||
const absl::Status prep_transaction_status = binder->PrepareTransaction(); |
||||
VLOG(2) << "prepare transaction = " << prep_transaction_status; |
||||
WritableParcel* writable_parcel = binder->GetWritableParcel(); |
||||
const absl::Status write_status = |
||||
writable_parcel->WriteInt32(kWireFormatVersion); |
||||
VLOG(2) << "write int32 = " << write_status; |
||||
// The lifetime of the transaction receiver is the same as the wire writer's.
|
||||
// The transaction receiver is responsible for not calling the on-transact
|
||||
// callback when it's dead.
|
||||
// Give TransactionReceiver a Ref() since WireReader cannot be destructed
|
||||
// during callback execution. TransactionReceiver should make sure that the
|
||||
// callback owns a Ref() when it's being invoked.
|
||||
tx_receiver_ = binder->ConstructTxReceiver( |
||||
/*wire_reader_ref=*/Ref(), |
||||
[this](transaction_code_t code, ReadableParcel* readable_parcel, |
||||
int uid) { |
||||
return this->ProcessTransaction(code, readable_parcel, uid); |
||||
}); |
||||
|
||||
VLOG(2) << "tx_receiver = " << tx_receiver_->GetRawBinder(); |
||||
const absl::Status write_binder_status = |
||||
writable_parcel->WriteBinder(tx_receiver_.get()); |
||||
VLOG(2) << "AParcel_writeStrongBinder = " << write_binder_status; |
||||
const absl::Status transact_status = |
||||
binder->Transact(BinderTransportTxCode::SETUP_TRANSPORT); |
||||
VLOG(2) << "AIBinder_transact = " << transact_status; |
||||
} |
||||
|
||||
std::unique_ptr<Binder> WireReaderImpl::RecvSetupTransport() { |
||||
// TODO(b/191941760): avoid blocking, handle wire_writer_noti lifetime
|
||||
// better
|
||||
VLOG(2) << "start waiting for noti"; |
||||
connection_noti_.WaitForNotification(); |
||||
VLOG(2) << "end waiting for noti"; |
||||
return std::move(other_end_binder_); |
||||
} |
||||
|
||||
absl::Status WireReaderImpl::ProcessTransaction(transaction_code_t code, |
||||
ReadableParcel* parcel, |
||||
int uid) { |
||||
if (code >= static_cast<unsigned>(kFirstCallId)) { |
||||
return ProcessStreamingTransaction(code, parcel); |
||||
} |
||||
|
||||
if (!(code >= static_cast<transaction_code_t>( |
||||
BinderTransportTxCode::SETUP_TRANSPORT) && |
||||
code <= static_cast<transaction_code_t>( |
||||
BinderTransportTxCode::PING_RESPONSE))) { |
||||
LOG(INFO) |
||||
<< "Received unknown control message. Shutdown transport gracefully."; |
||||
// TODO(waynetu): Shutdown transport gracefully.
|
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
{ |
||||
grpc_core::MutexLock lock(&mu_); |
||||
if (static_cast<BinderTransportTxCode>(code) != |
||||
BinderTransportTxCode::SETUP_TRANSPORT && |
||||
!connected_) { |
||||
return absl::InvalidArgumentError("Transports not connected yet"); |
||||
} |
||||
} |
||||
|
||||
// TODO(mingcl): See if we want to check the security policy for every RPC
|
||||
// call or just during transport setup.
|
||||
|
||||
switch (static_cast<BinderTransportTxCode>(code)) { |
||||
case BinderTransportTxCode::SETUP_TRANSPORT: { |
||||
grpc_core::MutexLock lock(&mu_); |
||||
if (recvd_setup_transport_) { |
||||
return absl::InvalidArgumentError( |
||||
"Already received a SETUP_TRANSPORT request"); |
||||
} |
||||
recvd_setup_transport_ = true; |
||||
|
||||
VLOG(2) << "calling uid = " << uid; |
||||
if (!security_policy_->IsAuthorized(uid)) { |
||||
return absl::PermissionDeniedError( |
||||
"UID " + std::to_string(uid) + |
||||
" is not allowed to connect to this " |
||||
"transport according to security policy."); |
||||
} |
||||
|
||||
int version; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadInt32(&version)); |
||||
VLOG(2) << "The other end respond with version = " << version; |
||||
// We only support this single lowest possible version, so server must
|
||||
// respond that version too.
|
||||
if (version != kWireFormatVersion) { |
||||
LOG(ERROR) << "The other end respond with version = " << version |
||||
<< ", but we requested version " << kWireFormatVersion |
||||
<< ", trying to continue anyway"; |
||||
} |
||||
std::unique_ptr<Binder> binder{}; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadBinder(&binder)); |
||||
if (!binder) { |
||||
return absl::InternalError("Read NULL binder from the parcel"); |
||||
} |
||||
binder->Initialize(); |
||||
other_end_binder_ = std::move(binder); |
||||
connection_noti_.Notify(); |
||||
break; |
||||
} |
||||
case BinderTransportTxCode::SHUTDOWN_TRANSPORT: { |
||||
LOG(ERROR) |
||||
<< "Received SHUTDOWN_TRANSPORT request but not implemented yet."; |
||||
return absl::UnimplementedError("SHUTDOWN_TRANSPORT"); |
||||
} |
||||
case BinderTransportTxCode::ACKNOWLEDGE_BYTES: { |
||||
int64_t num_bytes = -1; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadInt64(&num_bytes)); |
||||
VLOG(2) << "received acknowledge bytes = " << num_bytes; |
||||
if (!wire_writer_ready_notification_.WaitForNotificationWithTimeout( |
||||
absl::Seconds(5))) { |
||||
return absl::DeadlineExceededError( |
||||
"wire_writer_ is not ready in time!"); |
||||
} |
||||
wire_writer_->OnAckReceived(num_bytes); |
||||
break; |
||||
} |
||||
case BinderTransportTxCode::PING: { |
||||
if (is_client_) { |
||||
return absl::FailedPreconditionError("Receive PING request in client"); |
||||
} |
||||
int ping_id = -1; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadInt32(&ping_id)); |
||||
VLOG(2) << "received ping id = " << ping_id; |
||||
// TODO(waynetu): Ping back.
|
||||
break; |
||||
} |
||||
case BinderTransportTxCode::PING_RESPONSE: { |
||||
int value = -1; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadInt32(&value)); |
||||
VLOG(2) << "received ping response = " << value; |
||||
break; |
||||
} |
||||
} |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status WireReaderImpl::ProcessStreamingTransaction( |
||||
transaction_code_t code, ReadableParcel* parcel) { |
||||
bool need_to_send_ack = false; |
||||
int64_t num_bytes = 0; |
||||
// Indicates which callbacks should be cancelled. It will be initialized as
|
||||
// the flags the in-coming transaction carries, and when a particular
|
||||
// callback is completed, the corresponding bit in cancellation_flag will be
|
||||
// set to 0 so that we won't cancel it afterward.
|
||||
int cancellation_flags = 0; |
||||
// The queue saves the actions needed to be done "WITHOUT" `mu_`.
|
||||
// It prevents deadlock against wire writer issues.
|
||||
std::queue<absl::AnyInvocable<void() &&>> deferred_func_queue; |
||||
absl::Status tx_process_result; |
||||
|
||||
{ |
||||
grpc_core::MutexLock lock(&mu_); |
||||
if (!connected_) { |
||||
return absl::InvalidArgumentError("Transports not connected yet"); |
||||
} |
||||
|
||||
tx_process_result = ProcessStreamingTransactionImpl( |
||||
code, parcel, &cancellation_flags, deferred_func_queue); |
||||
if ((num_incoming_bytes_ - num_acknowledged_bytes_) >= |
||||
kFlowControlAckBytes) { |
||||
need_to_send_ack = true; |
||||
num_bytes = num_incoming_bytes_; |
||||
num_acknowledged_bytes_ = num_incoming_bytes_; |
||||
} |
||||
} |
||||
// Executes all actions in the queue.
|
||||
while (!deferred_func_queue.empty()) { |
||||
std::move(deferred_func_queue.front())(); |
||||
deferred_func_queue.pop(); |
||||
} |
||||
|
||||
if (!tx_process_result.ok()) { |
||||
LOG(ERROR) << "Failed to process streaming transaction: " |
||||
<< tx_process_result.ToString(); |
||||
// Something went wrong when receiving transaction. Cancel failed requests.
|
||||
if (cancellation_flags & kFlagPrefix) { |
||||
LOG(INFO) << "cancelling initial metadata"; |
||||
transport_stream_receiver_->NotifyRecvInitialMetadata(code, |
||||
tx_process_result); |
||||
} |
||||
if (cancellation_flags & kFlagMessageData) { |
||||
LOG(INFO) << "cancelling message data"; |
||||
transport_stream_receiver_->NotifyRecvMessage(code, tx_process_result); |
||||
} |
||||
if (cancellation_flags & kFlagSuffix) { |
||||
LOG(INFO) << "cancelling trailing metadata"; |
||||
transport_stream_receiver_->NotifyRecvTrailingMetadata( |
||||
code, tx_process_result, 0); |
||||
} |
||||
} |
||||
|
||||
if (need_to_send_ack) { |
||||
if (!wire_writer_ready_notification_.WaitForNotificationWithTimeout( |
||||
absl::Seconds(5))) { |
||||
return absl::DeadlineExceededError("wire_writer_ is not ready in time!"); |
||||
} |
||||
CHECK(wire_writer_); |
||||
// wire_writer_ should not be accessed while holding mu_!
|
||||
// Otherwise, it is possible that
|
||||
// 1. wire_writer_::mu_ is acquired before mu_ (NDK call back during
|
||||
// transaction)
|
||||
// 2. mu_ is acquired before wire_writer_::mu_ (e.g. Java call back us, and
|
||||
// we call WireWriter::SendAck which will try to acquire wire_writer_::mu_)
|
||||
absl::Status ack_status = wire_writer_->SendAck(num_bytes); |
||||
if (tx_process_result.ok()) { |
||||
return ack_status; |
||||
} |
||||
} |
||||
return tx_process_result; |
||||
} |
||||
|
||||
absl::Status WireReaderImpl::ProcessStreamingTransactionImpl( |
||||
transaction_code_t code, ReadableParcel* parcel, int* cancellation_flags, |
||||
std::queue<absl::AnyInvocable<void() &&>>& deferred_func_queue) { |
||||
CHECK(cancellation_flags); |
||||
num_incoming_bytes_ += parcel->GetDataSize(); |
||||
LOG(INFO) << "Total incoming bytes: " << num_incoming_bytes_; |
||||
|
||||
int flags; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadInt32(&flags)); |
||||
*cancellation_flags = flags; |
||||
|
||||
// Ignore in-coming transaction with flag = 0 to match with Java
|
||||
// implementation.
|
||||
// TODO(waynetu): Check with grpc-java team to see whether this is the
|
||||
// intended behavior.
|
||||
// TODO(waynetu): What should be returned here?
|
||||
if (flags == 0) { |
||||
LOG(INFO) << "[WARNING] Receive empty transaction. Ignored."; |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
int status = flags >> 16; |
||||
VLOG(2) << "status = " << status; |
||||
VLOG(2) << "FLAG_PREFIX = " << (flags & kFlagPrefix); |
||||
VLOG(2) << "FLAG_MESSAGE_DATA = " << (flags & kFlagMessageData); |
||||
VLOG(2) << "FLAG_SUFFIX = " << (flags & kFlagSuffix); |
||||
int seq_num; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadInt32(&seq_num)); |
||||
// TODO(waynetu): For now we'll just assume that the transactions commit in
|
||||
// the same order they're issued. The following assertion detects
|
||||
// out-of-order or missing transactions. WireReaderImpl should be fixed if
|
||||
// we indeed found such behavior.
|
||||
int32_t& expectation = expected_seq_num_[code]; |
||||
if (seq_num < 0 || seq_num != expectation) { |
||||
// Unexpected sequence number.
|
||||
return absl::InternalError("Unexpected sequence number"); |
||||
} |
||||
// TODO(waynetu): According to the protocol, "The sequence number will wrap
|
||||
// around to 0 if more than 2^31 messages are sent." For now we'll just
|
||||
// assert that it never reach such circumstances.
|
||||
CHECK(expectation < std::numeric_limits<int32_t>::max()) |
||||
<< "Sequence number too large"; |
||||
expectation++; |
||||
VLOG(2) << "sequence number = " << seq_num; |
||||
if (flags & kFlagPrefix) { |
||||
std::string method_ref; |
||||
if (!is_client_) { |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadString(&method_ref)); |
||||
} |
||||
absl::StatusOr<Metadata> initial_metadata_or_error = parse_metadata(parcel); |
||||
if (!initial_metadata_or_error.ok()) { |
||||
return initial_metadata_or_error.status(); |
||||
} |
||||
if (!is_client_) { |
||||
// In BinderChannel wireformat specification, path is not encoded as part
|
||||
// of metadata. So we extract the path and turn it into metadata here
|
||||
// (this is what core API layer expects).
|
||||
initial_metadata_or_error->emplace_back(":path", |
||||
std::string("/") + method_ref); |
||||
// Since authority metadata is not part of BinderChannel wireformat
|
||||
// specification, and gRPC core API layer expects the presence of
|
||||
// authority for message sent from client to server, we add one if
|
||||
// missing (it will be missing if client grpc-java).
|
||||
bool has_authority = false; |
||||
for (const auto& p : *initial_metadata_or_error) { |
||||
if (p.first == kAuthorityMetadataKey) has_authority = true; |
||||
} |
||||
if (!has_authority) { |
||||
initial_metadata_or_error->emplace_back(kAuthorityMetadataKey, |
||||
"binder.authority"); |
||||
} |
||||
} |
||||
deferred_func_queue.emplace([this, code, |
||||
initial_metadata_or_error = std::move( |
||||
initial_metadata_or_error)]() mutable { |
||||
this->transport_stream_receiver_->NotifyRecvInitialMetadata( |
||||
code, std::move(initial_metadata_or_error)); |
||||
}); |
||||
*cancellation_flags &= ~kFlagPrefix; |
||||
} |
||||
if (flags & kFlagMessageData) { |
||||
int count; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadInt32(&count)); |
||||
VLOG(2) << "count = " << count; |
||||
std::string msg_data{}; |
||||
if (count > 0) { |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadByteArray(&msg_data)); |
||||
} |
||||
message_buffer_[code] += msg_data; |
||||
if ((flags & kFlagMessageDataIsPartial) == 0) { |
||||
std::string s = std::move(message_buffer_[code]); |
||||
message_buffer_.erase(code); |
||||
deferred_func_queue.emplace([this, code, s = std::move(s)]() mutable { |
||||
this->transport_stream_receiver_->NotifyRecvMessage(code, std::move(s)); |
||||
}); |
||||
} |
||||
*cancellation_flags &= ~kFlagMessageData; |
||||
} |
||||
if (flags & kFlagSuffix) { |
||||
if (flags & kFlagStatusDescription) { |
||||
// FLAG_STATUS_DESCRIPTION set
|
||||
std::string desc; |
||||
GRPC_RETURN_IF_ERROR(parcel->ReadString(&desc)); |
||||
VLOG(2) << "description = " << desc; |
||||
} |
||||
Metadata trailing_metadata; |
||||
if (is_client_) { |
||||
absl::StatusOr<Metadata> trailing_metadata_or_error = |
||||
parse_metadata(parcel); |
||||
if (!trailing_metadata_or_error.ok()) { |
||||
return trailing_metadata_or_error.status(); |
||||
} |
||||
trailing_metadata = *trailing_metadata_or_error; |
||||
} |
||||
deferred_func_queue.emplace( |
||||
[this, code, trailing_metadata = std::move(trailing_metadata), |
||||
status]() mutable { |
||||
this->transport_stream_receiver_->NotifyRecvTrailingMetadata( |
||||
code, std::move(trailing_metadata), status); |
||||
}); |
||||
*cancellation_flags &= ~kFlagSuffix; |
||||
} |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
} // namespace grpc_binder
|
||||
#endif |
@ -1,158 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_READER_IMPL_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_READER_IMPL_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpcpp/security/binder_security_policy.h> |
||||
|
||||
#include <memory> |
||||
#include <queue> |
||||
#include <utility> |
||||
|
||||
#include "absl/container/flat_hash_map.h" |
||||
#include "absl/functional/any_invocable.h" |
||||
#include "src/core/ext/transport/binder/utils/transport_stream_receiver.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_reader.h" |
||||
#include "src/core/ext/transport/binder/wire_format/wire_writer.h" |
||||
#include "src/core/util/notification.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
class WireReaderImpl : public WireReader { |
||||
public: |
||||
WireReaderImpl( |
||||
std::shared_ptr<TransportStreamReceiver> transport_stream_receiver, |
||||
bool is_client, |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> |
||||
security_policy, |
||||
std::function<void()> on_destruct_callback = nullptr); |
||||
~WireReaderImpl() override; |
||||
|
||||
void Orphan() override { Unref(); } |
||||
|
||||
/// Setup the transport between endpoint binders.
|
||||
///
|
||||
/// The client and the server both call SetupTransport() when constructing
|
||||
/// transport.
|
||||
///
|
||||
/// High-level overview of transaction setup:
|
||||
/// 0. Client obtains an |endpoint_binder| from the server (in the Android
|
||||
/// setting, this can be achieved by "binding" to the server APK).
|
||||
/// 1. Client creates a binder |client_binder| and hook its on-transaction
|
||||
/// callback to client's ProcessTransaction(). Client then sends
|
||||
/// |client_binder| through |endpoint_binder| to server.
|
||||
/// 2. Server receives |client_binder| via |endpoint_binder|.
|
||||
/// 3. Server creates a binder |server_binder| and hook its on-transaction
|
||||
/// callback to server's ProcessTransaction(). Server then sends
|
||||
/// |server_binder| through |client_binder| back to the client.
|
||||
/// 4. Client receives |server_binder| via |client_binder|'s on-transaction
|
||||
/// callback.
|
||||
///
|
||||
/// The parameter \p binder here means different things for client nad server.
|
||||
/// For client, \p binder refers to |endpoint_binder|, and for server, \p
|
||||
/// binder refers to |client_binder|. That is, for server-side transport
|
||||
/// setup, we assume that the first half of SETUP_TRANSPORT (up to step 2) is
|
||||
/// already done somewhere else (see test/end2end/binder_transport_test.cc for
|
||||
/// how it's handled in the testing environment).
|
||||
std::shared_ptr<WireWriter> SetupTransport( |
||||
std::unique_ptr<Binder> binder) override; |
||||
|
||||
absl::Status ProcessTransaction(transaction_code_t code, |
||||
ReadableParcel* parcel, int uid); |
||||
|
||||
/// Send SETUP_TRANSPORT request through \p binder.
|
||||
///
|
||||
/// This is the one half (for client it's the first half, and for server it's
|
||||
/// the second) of the SETUP_TRANSPORT negotiation process. First, a new
|
||||
/// binder is created. We take its "receiving" part and construct the
|
||||
/// transaction receiver with it, and sends the "sending" part along with the
|
||||
/// SETUP_TRANSPORT message through \p binder.
|
||||
void SendSetupTransport(Binder* binder); |
||||
|
||||
/// Recv SETUP_TRANSPORT request.
|
||||
///
|
||||
/// This is the other half of the SETUP_TRANSPORT process. We wait for
|
||||
/// in-coming SETUP_TRANSPORT request with the "sending" part of a binder from
|
||||
/// the other end. For client, the message is coming from the transaction
|
||||
/// receiver we just constructed in SendSetupTransport(). For server, we
|
||||
/// assume that this step is already completed.
|
||||
// TODO(waynetu): In the testing environment, we still use this method (on
|
||||
// another WireReader instance) for server-side transport setup, and thus it
|
||||
// is marked as public. Try moving this method back to private, and hopefully
|
||||
// we can also avoid moving |other_end_binder_| out in the implementation.
|
||||
std::unique_ptr<Binder> RecvSetupTransport(); |
||||
|
||||
private: |
||||
absl::Status ProcessStreamingTransaction(transaction_code_t code, |
||||
ReadableParcel* parcel); |
||||
absl::Status ProcessStreamingTransactionImpl( |
||||
transaction_code_t code, ReadableParcel* parcel, int* cancellation_flags, |
||||
// The queue saves the actions needed to be done "WITHOUT" `mu_`.
|
||||
// It prevents deadlock against wire writer issues.
|
||||
std::queue<absl::AnyInvocable<void() &&>>& deferred_func_queue) |
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); |
||||
|
||||
std::shared_ptr<TransportStreamReceiver> transport_stream_receiver_; |
||||
grpc_core::Notification connection_noti_; |
||||
grpc_core::Mutex mu_; |
||||
std::atomic_bool connected_{false}; |
||||
bool recvd_setup_transport_ ABSL_GUARDED_BY(mu_) = false; |
||||
// NOTE: other_end_binder_ will be moved out when RecvSetupTransport() is
|
||||
// called. Be cautious not to access it afterward.
|
||||
std::unique_ptr<Binder> other_end_binder_; |
||||
absl::flat_hash_map<transaction_code_t, int32_t> expected_seq_num_ |
||||
ABSL_GUARDED_BY(mu_); |
||||
absl::flat_hash_map<transaction_code_t, std::string> message_buffer_ |
||||
ABSL_GUARDED_BY(mu_); |
||||
std::unique_ptr<TransactionReceiver> tx_receiver_; |
||||
bool is_client_; |
||||
std::shared_ptr<grpc::experimental::binder::SecurityPolicy> security_policy_; |
||||
// When WireReaderImpl gets destructed, call on_destruct_callback_. This is
|
||||
// mostly for decrementing the reference count of its transport.
|
||||
std::function<void()> on_destruct_callback_; |
||||
|
||||
// ACK every 16k bytes.
|
||||
static constexpr int64_t kFlowControlAckBytes = 16 * 1024; |
||||
int64_t num_incoming_bytes_ ABSL_GUARDED_BY(mu_) = 0; |
||||
int64_t num_acknowledged_bytes_ ABSL_GUARDED_BY(mu_) = 0; |
||||
|
||||
// Used to send ACK.
|
||||
std::shared_ptr<WireWriter> wire_writer_; |
||||
|
||||
// Workaround for race condition.
|
||||
//
|
||||
// In `SetupTransport()`, we set `connected_` to true, call
|
||||
// `SendSetupTransport()`, and construct `wire_writer_`. There is a potential
|
||||
// race condition between calling `SendSetupTransport()` and constructing
|
||||
// `wire_writer_`. So use this notification to wait. This should be very fast
|
||||
// and waiting is acceptable.
|
||||
//
|
||||
// The original problem was that we can't move `connected_ = true` and
|
||||
// `SendSetupTransport()` into the mutex, as it will deadlock if
|
||||
// `ProcessTransaction()` is called in the same call chain.
|
||||
//
|
||||
// Note: this is not the perfect solution, the system will still deadlock if,
|
||||
// e.g., the first request is 64K and we entered the sending ACK code path.
|
||||
//
|
||||
// TODO(littlecvr): Figure out a better solution to not causing any potential
|
||||
// deadlock and not having to wait.
|
||||
grpc_core::Notification wire_writer_ready_notification_; |
||||
}; |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_READER_IMPL_H
|
@ -1,393 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/core/ext/transport/binder/wire_format/wire_writer.h" |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#ifndef GRPC_NO_BINDER |
||||
|
||||
#include <utility> |
||||
|
||||
#include "absl/cleanup/cleanup.h" |
||||
#include "absl/log/check.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/types/variant.h" |
||||
#include "src/core/lib/event_engine/default_event_engine.h" |
||||
#include "src/core/util/crash.h" |
||||
|
||||
#define RETURN_IF_ERROR(expr) \ |
||||
do { \
|
||||
const absl::Status status = (expr); \
|
||||
if (!status.ok()) return status; \
|
||||
} while (0) |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
bool CanBeSentInOneTransaction(const Transaction& tx) { |
||||
return (tx.GetFlags() & kFlagMessageData) == 0 || |
||||
static_cast<int64_t>(tx.GetMessageData().size()) <= |
||||
WireWriterImpl::kBlockSize; |
||||
} |
||||
|
||||
// Simply forward the call to `WireWriterImpl::RunScheduledTx`.
|
||||
void RunScheduledTx(void* arg, grpc_error_handle /*error*/) { |
||||
auto* run_scheduled_tx_args = |
||||
static_cast<WireWriterImpl::RunScheduledTxArgs*>(arg); |
||||
run_scheduled_tx_args->writer->RunScheduledTxInternal(run_scheduled_tx_args); |
||||
} |
||||
|
||||
absl::Status WriteInitialMetadata(const Transaction& tx, |
||||
WritableParcel* parcel) { |
||||
if (tx.IsClient()) { |
||||
// Only client sends method ref.
|
||||
RETURN_IF_ERROR(parcel->WriteString(tx.GetMethodRef())); |
||||
} |
||||
RETURN_IF_ERROR(parcel->WriteInt32(tx.GetPrefixMetadata().size())); |
||||
for (const auto& md : tx.GetPrefixMetadata()) { |
||||
RETURN_IF_ERROR(parcel->WriteByteArrayWithLength(md.first)); |
||||
RETURN_IF_ERROR(parcel->WriteByteArrayWithLength(md.second)); |
||||
} |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status WriteTrailingMetadata(const Transaction& tx, |
||||
WritableParcel* parcel) { |
||||
if (tx.IsServer()) { |
||||
if (tx.GetFlags() & kFlagStatusDescription) { |
||||
RETURN_IF_ERROR(parcel->WriteString(tx.GetStatusDesc())); |
||||
} |
||||
RETURN_IF_ERROR(parcel->WriteInt32(tx.GetSuffixMetadata().size())); |
||||
for (const auto& md : tx.GetSuffixMetadata()) { |
||||
RETURN_IF_ERROR(parcel->WriteByteArrayWithLength(md.first)); |
||||
RETURN_IF_ERROR(parcel->WriteByteArrayWithLength(md.second)); |
||||
} |
||||
} else { |
||||
// client suffix currently is always empty according to the wireformat
|
||||
if (!tx.GetSuffixMetadata().empty()) { |
||||
LOG(ERROR) << "Got non-empty suffix metadata from client."; |
||||
} |
||||
} |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
WireWriterImpl::WireWriterImpl(std::unique_ptr<Binder> binder) |
||||
: binder_(std::move(binder)), |
||||
combiner_(grpc_combiner_create( |
||||
grpc_event_engine::experimental::GetDefaultEventEngine())) {} |
||||
|
||||
WireWriterImpl::~WireWriterImpl() { |
||||
GRPC_COMBINER_UNREF(combiner_, "wire_writer_impl"); |
||||
while (!pending_outgoing_tx_.empty()) { |
||||
delete pending_outgoing_tx_.front(); |
||||
pending_outgoing_tx_.pop(); |
||||
} |
||||
} |
||||
|
||||
// Flow control constant are specified at
|
||||
// https://github.com/grpc/proposal/blob/master/L73-java-binderchannel/wireformat.md#flow-control
|
||||
const int64_t WireWriterImpl::kBlockSize = 16 * 1024; |
||||
const int64_t WireWriterImpl::kFlowControlWindowSize = 128 * 1024; |
||||
|
||||
absl::Status WireWriterImpl::MakeBinderTransaction( |
||||
BinderTransportTxCode tx_code, |
||||
std::function<absl::Status(WritableParcel*)> fill_parcel) { |
||||
grpc_core::MutexLock lock(&write_mu_); |
||||
RETURN_IF_ERROR(binder_->PrepareTransaction()); |
||||
WritableParcel* parcel = binder_->GetWritableParcel(); |
||||
RETURN_IF_ERROR(fill_parcel(parcel)); |
||||
// Only stream transaction is accounted in flow control spec.
|
||||
if (static_cast<int32_t>(tx_code) >= kFirstCallId) { |
||||
int64_t parcel_size = parcel->GetDataSize(); |
||||
if (parcel_size > 2 * kBlockSize) { |
||||
LOG(ERROR) << "Unexpected large transaction (possibly caused by a very " |
||||
"large metadata). This might overflow the binder " |
||||
"transaction buffer. Size: " |
||||
<< parcel_size << " bytes"; |
||||
} |
||||
num_outgoing_bytes_ += parcel_size; |
||||
LOG(INFO) << "Total outgoing bytes: " << num_outgoing_bytes_.load(); |
||||
} |
||||
CHECK(!is_transacting_); |
||||
is_transacting_ = true; |
||||
absl::Status result = binder_->Transact(tx_code); |
||||
is_transacting_ = false; |
||||
return result; |
||||
} |
||||
|
||||
absl::Status WireWriterImpl::RpcCallFastPath(std::unique_ptr<Transaction> tx) { |
||||
return MakeBinderTransaction( |
||||
static_cast<BinderTransportTxCode>(tx->GetTxCode()), |
||||
[this, tx = tx.get()]( |
||||
WritableParcel* parcel) ABSL_EXCLUSIVE_LOCKS_REQUIRED(write_mu_) { |
||||
RETURN_IF_ERROR(parcel->WriteInt32(tx->GetFlags())); |
||||
RETURN_IF_ERROR(parcel->WriteInt32(next_seq_num_[tx->GetTxCode()]++)); |
||||
if (tx->GetFlags() & kFlagPrefix) { |
||||
RETURN_IF_ERROR(WriteInitialMetadata(*tx, parcel)); |
||||
} |
||||
if (tx->GetFlags() & kFlagMessageData) { |
||||
RETURN_IF_ERROR( |
||||
parcel->WriteByteArrayWithLength(tx->GetMessageData())); |
||||
} |
||||
if (tx->GetFlags() & kFlagSuffix) { |
||||
RETURN_IF_ERROR(WriteTrailingMetadata(*tx, parcel)); |
||||
} |
||||
return absl::OkStatus(); |
||||
}); |
||||
} |
||||
|
||||
absl::Status WireWriterImpl::RunStreamTx( |
||||
RunScheduledTxArgs::StreamTx* stream_tx, WritableParcel* parcel, |
||||
bool* is_last_chunk) { |
||||
Transaction* tx = stream_tx->tx.get(); |
||||
// Transaction without data flag should go to fast path.
|
||||
CHECK(tx->GetFlags() & kFlagMessageData); |
||||
|
||||
absl::string_view data = tx->GetMessageData(); |
||||
CHECK(stream_tx->bytes_sent <= static_cast<int64_t>(data.size())); |
||||
|
||||
int flags = kFlagMessageData; |
||||
|
||||
if (stream_tx->bytes_sent == 0) { |
||||
// This is the first transaction. Include initial
|
||||
// metadata if there's any.
|
||||
if (tx->GetFlags() & kFlagPrefix) { |
||||
flags |= kFlagPrefix; |
||||
} |
||||
} |
||||
// There is also prefix/suffix in transaction beside the transaction data so
|
||||
// actual transaction size will be greater than `kBlockSize`. This is
|
||||
// unavoidable because we cannot split the prefix metadata and trailing
|
||||
// metadata into different binder transactions. In most cases this is fine
|
||||
// because single transaction size is not required to be strictly lower than
|
||||
// `kBlockSize`, as long as it won't overflow Android's binder buffer.
|
||||
int64_t size = std::min<int64_t>(WireWriterImpl::kBlockSize, |
||||
data.size() - stream_tx->bytes_sent); |
||||
if (stream_tx->bytes_sent + WireWriterImpl::kBlockSize >= |
||||
static_cast<int64_t>(data.size())) { |
||||
// This is the last transaction. Include trailing
|
||||
// metadata if there's any.
|
||||
if (tx->GetFlags() & kFlagSuffix) { |
||||
flags |= kFlagSuffix; |
||||
} |
||||
size = data.size() - stream_tx->bytes_sent; |
||||
*is_last_chunk = true; |
||||
} else { |
||||
// There are more messages to send.
|
||||
flags |= kFlagMessageDataIsPartial; |
||||
*is_last_chunk = false; |
||||
} |
||||
RETURN_IF_ERROR(parcel->WriteInt32(flags)); |
||||
RETURN_IF_ERROR(parcel->WriteInt32(next_seq_num_[tx->GetTxCode()]++)); |
||||
if (flags & kFlagPrefix) { |
||||
RETURN_IF_ERROR(WriteInitialMetadata(*tx, parcel)); |
||||
} |
||||
RETURN_IF_ERROR(parcel->WriteByteArrayWithLength( |
||||
data.substr(stream_tx->bytes_sent, size))); |
||||
if (flags & kFlagSuffix) { |
||||
RETURN_IF_ERROR(WriteTrailingMetadata(*tx, parcel)); |
||||
} |
||||
stream_tx->bytes_sent += size; |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
void WireWriterImpl::RunScheduledTxInternal(RunScheduledTxArgs* args) { |
||||
CHECK(args->writer == this); |
||||
if (absl::holds_alternative<RunScheduledTxArgs::AckTx>(args->tx)) { |
||||
int64_t num_bytes = |
||||
absl::get<RunScheduledTxArgs::AckTx>(args->tx).num_bytes; |
||||
absl::Status result = |
||||
MakeBinderTransaction(BinderTransportTxCode::ACKNOWLEDGE_BYTES, |
||||
[num_bytes](WritableParcel* parcel) { |
||||
RETURN_IF_ERROR(parcel->WriteInt64(num_bytes)); |
||||
return absl::OkStatus(); |
||||
}); |
||||
if (!result.ok()) { |
||||
LOG(ERROR) << "Failed to make binder transaction " << result; |
||||
} |
||||
delete args; |
||||
return; |
||||
} |
||||
CHECK(absl::holds_alternative<RunScheduledTxArgs::StreamTx>(args->tx)); |
||||
RunScheduledTxArgs::StreamTx* stream_tx = |
||||
&absl::get<RunScheduledTxArgs::StreamTx>(args->tx); |
||||
// Be reservative. Decrease CombinerTxCount after the data size of this
|
||||
// transaction has already been added to `num_outgoing_bytes_`, to make sure
|
||||
// we never underestimate `num_outgoing_bytes_`.
|
||||
auto decrease_combiner_tx_count = absl::MakeCleanup([this]() { |
||||
{ |
||||
grpc_core::MutexLock lock(&flow_control_mu_); |
||||
CHECK_GT(num_non_acked_tx_in_combiner_, 0); |
||||
num_non_acked_tx_in_combiner_--; |
||||
} |
||||
// New transaction might be ready to be scheduled.
|
||||
TryScheduleTransaction(); |
||||
}); |
||||
if (CanBeSentInOneTransaction(*stream_tx->tx.get())) { // NOLINT
|
||||
absl::Status result = RpcCallFastPath(std::move(stream_tx->tx)); |
||||
if (!result.ok()) { |
||||
LOG(ERROR) << "Failed to handle non-chunked RPC call " << result; |
||||
} |
||||
delete args; |
||||
return; |
||||
} |
||||
bool is_last_chunk = true; |
||||
absl::Status result = MakeBinderTransaction( |
||||
static_cast<BinderTransportTxCode>(stream_tx->tx->GetTxCode()), |
||||
[stream_tx, &is_last_chunk, this](WritableParcel* parcel) |
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(write_mu_) { |
||||
return RunStreamTx(stream_tx, parcel, &is_last_chunk); |
||||
}); |
||||
if (!result.ok()) { |
||||
LOG(ERROR) << "Failed to make binder transaction " << result; |
||||
} |
||||
if (!is_last_chunk) { |
||||
{ |
||||
grpc_core::MutexLock lock(&flow_control_mu_); |
||||
pending_outgoing_tx_.push(args); |
||||
} |
||||
TryScheduleTransaction(); |
||||
} else { |
||||
delete args; |
||||
} |
||||
} |
||||
|
||||
absl::Status WireWriterImpl::RpcCall(std::unique_ptr<Transaction> tx) { |
||||
// TODO(mingcl): check tx_code <= last call id
|
||||
CHECK(tx->GetTxCode() >= kFirstCallId); |
||||
auto args = new RunScheduledTxArgs(); |
||||
args->writer = this; |
||||
args->tx = RunScheduledTxArgs::StreamTx(); |
||||
absl::get<RunScheduledTxArgs::StreamTx>(args->tx).tx = std::move(tx); |
||||
absl::get<RunScheduledTxArgs::StreamTx>(args->tx).bytes_sent = 0; |
||||
{ |
||||
grpc_core::MutexLock lock(&flow_control_mu_); |
||||
pending_outgoing_tx_.push(args); |
||||
} |
||||
TryScheduleTransaction(); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::Status WireWriterImpl::SendAck(int64_t num_bytes) { |
||||
// Ensure combiner will be run if this is not called from top-level gRPC API
|
||||
// entrypoint.
|
||||
grpc_core::ExecCtx exec_ctx; |
||||
LOG(INFO) << "Ack " << num_bytes << " bytes received"; |
||||
if (is_transacting_) { |
||||
// This can happen because NDK might call our registered callback function
|
||||
// in the same thread while we are telling it to send a transaction
|
||||
// `is_transacting_` will be true. `Binder::Transact` is now being called on
|
||||
// the same thread or the other thread. We are currently in the call stack
|
||||
// of other transaction, Liveness of ACK is still guaranteed even if this is
|
||||
// a race with another thread.
|
||||
LOG(INFO) << "Scheduling ACK transaction instead of directly execute it to " |
||||
"avoid deadlock."; |
||||
auto args = new RunScheduledTxArgs(); |
||||
args->writer = this; |
||||
args->tx = RunScheduledTxArgs::AckTx(); |
||||
absl::get<RunScheduledTxArgs::AckTx>(args->tx).num_bytes = num_bytes; |
||||
auto cl = GRPC_CLOSURE_CREATE(RunScheduledTx, args, nullptr); |
||||
combiner_->Run(cl, absl::OkStatus()); |
||||
return absl::OkStatus(); |
||||
} |
||||
// Otherwise, we can directly send ack.
|
||||
absl::Status result = |
||||
MakeBinderTransaction((BinderTransportTxCode::ACKNOWLEDGE_BYTES), |
||||
[num_bytes](WritableParcel* parcel) { |
||||
RETURN_IF_ERROR(parcel->WriteInt64(num_bytes)); |
||||
return absl::OkStatus(); |
||||
}); |
||||
if (!result.ok()) { |
||||
LOG(ERROR) << "Failed to make binder transaction " << result; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
void WireWriterImpl::OnAckReceived(int64_t num_bytes) { |
||||
// Ensure combiner will be run if this is not called from top-level gRPC API
|
||||
// entrypoint.
|
||||
grpc_core::ExecCtx exec_ctx; |
||||
LOG(INFO) << "OnAckReceived " << num_bytes; |
||||
// Do not try to obtain `write_mu_` in this function. NDKBinder might invoke
|
||||
// the callback to notify us about new incoming binder transaction when we are
|
||||
// sending transaction. i.e. `write_mu_` might have already been acquired by
|
||||
// this thread.
|
||||
{ |
||||
grpc_core::MutexLock lock(&flow_control_mu_); |
||||
num_acknowledged_bytes_ = std::max(num_acknowledged_bytes_, num_bytes); |
||||
int64_t num_outgoing_bytes = num_outgoing_bytes_; |
||||
if (num_acknowledged_bytes_ > num_outgoing_bytes) { |
||||
LOG(ERROR) << "The other end of transport acked more bytes than we ever " |
||||
"sent, " |
||||
<< num_acknowledged_bytes_ << " > " << num_outgoing_bytes; |
||||
} |
||||
} |
||||
TryScheduleTransaction(); |
||||
} |
||||
|
||||
void WireWriterImpl::TryScheduleTransaction() { |
||||
while (true) { |
||||
grpc_core::MutexLock lock(&flow_control_mu_); |
||||
if (pending_outgoing_tx_.empty()) { |
||||
// Nothing to be schduled.
|
||||
break; |
||||
} |
||||
// Number of bytes we have scheduled in combiner but have not yet be
|
||||
// executed by combiner. Here we make an assumption that every binder
|
||||
// transaction will take `kBlockSize`. This should be close to truth when a
|
||||
// large message is being cut to `kBlockSize` chunks.
|
||||
int64_t num_bytes_scheduled_in_combiner = |
||||
num_non_acked_tx_in_combiner_ * kBlockSize; |
||||
// An estimation of number of bytes of traffic we will eventually send to
|
||||
// the other end, assuming all tasks in combiner will be executed and we
|
||||
// receive no new ACK from the other end of transport.
|
||||
int64_t num_total_bytes_will_be_sent = |
||||
num_outgoing_bytes_ + num_bytes_scheduled_in_combiner; |
||||
// An estimation of number of bytes of traffic that will not be
|
||||
// acknowledged, assuming all tasks in combiner will be executed and we
|
||||
// receive no new ack message fomr the other end of transport.
|
||||
int64_t num_non_acked_bytes_estimation = |
||||
num_total_bytes_will_be_sent - num_acknowledged_bytes_; |
||||
if (num_non_acked_bytes_estimation < 0) { |
||||
LOG(ERROR) << "Something went wrong. `num_non_acked_bytes_estimation` " |
||||
"should be non-negative but it is " |
||||
<< num_non_acked_bytes_estimation; |
||||
} |
||||
// If we can schedule another transaction (which has size estimation of
|
||||
// `kBlockSize`) without exceeding `kFlowControlWindowSize`, schedule it.
|
||||
if ((num_non_acked_bytes_estimation + kBlockSize < |
||||
kFlowControlWindowSize)) { |
||||
num_non_acked_tx_in_combiner_++; |
||||
combiner_->Run(GRPC_CLOSURE_CREATE(RunScheduledTx, |
||||
pending_outgoing_tx_.front(), nullptr), |
||||
absl::OkStatus()); |
||||
pending_outgoing_tx_.pop(); |
||||
} else { |
||||
// It is common to fill `kFlowControlWindowSize` completely because
|
||||
// transactions are send at faster rate than the other end of transport
|
||||
// can handle it, so here we use VLOG(2).
|
||||
VLOG(2) << "Some work cannot be scheduled yet due to slow ack from the " |
||||
"other end of transport. This transport might be blocked if " |
||||
"this number don't go down. pending_outgoing_tx_.size() = " |
||||
<< pending_outgoing_tx_.size() |
||||
<< " pending_outgoing_tx_.front() = " |
||||
<< pending_outgoing_tx_.front(); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif |
@ -1,126 +0,0 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_WRITER_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_WRITER_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <queue> |
||||
#include <string> |
||||
#include <vector> |
||||
|
||||
#include "absl/container/flat_hash_map.h" |
||||
#include "src/core/ext/transport/binder/wire_format/binder.h" |
||||
#include "src/core/ext/transport/binder/wire_format/transaction.h" |
||||
#include "src/core/lib/iomgr/combiner.h" |
||||
#include "src/core/util/sync.h" |
||||
|
||||
namespace grpc_binder { |
||||
|
||||
// Member functions are thread safe.
|
||||
class WireWriter { |
||||
public: |
||||
virtual ~WireWriter() = default; |
||||
virtual absl::Status RpcCall(std::unique_ptr<Transaction> tx) = 0; |
||||
virtual absl::Status SendAck(int64_t num_bytes) = 0; |
||||
virtual void OnAckReceived(int64_t num_bytes) = 0; |
||||
}; |
||||
|
||||
class WireWriterImpl : public WireWriter { |
||||
public: |
||||
explicit WireWriterImpl(std::unique_ptr<Binder> binder); |
||||
~WireWriterImpl() override; |
||||
absl::Status RpcCall(std::unique_ptr<Transaction> tx) override; |
||||
absl::Status SendAck(int64_t num_bytes) override; |
||||
void OnAckReceived(int64_t num_bytes) override; |
||||
|
||||
// Required to be public because we would like to call this in combiner (which
|
||||
// cannot invoke class member function). `RunScheduledTxArgs` and
|
||||
// `RunScheduledTxInternal` should not be used by user directly.
|
||||
struct RunScheduledTxArgs { |
||||
WireWriterImpl* writer; |
||||
struct StreamTx { |
||||
std::unique_ptr<Transaction> tx; |
||||
// How many data in transaction's `data` field has been sent.
|
||||
int64_t bytes_sent = 0; |
||||
}; |
||||
struct AckTx { |
||||
int64_t num_bytes; |
||||
}; |
||||
absl::variant<AckTx, StreamTx> tx; |
||||
}; |
||||
|
||||
void RunScheduledTxInternal(RunScheduledTxArgs* arg); |
||||
|
||||
// Split long message into chunks of size 16k. This doesn't necessarily have
|
||||
// to be the same as the flow control acknowledgement size, but it should not
|
||||
// exceed 128k.
|
||||
static const int64_t kBlockSize; |
||||
|
||||
// Flow control allows sending at most 128k between acknowledgements.
|
||||
static const int64_t kFlowControlWindowSize; |
||||
|
||||
private: |
||||
// Fast path: send data in one transaction.
|
||||
absl::Status RpcCallFastPath(std::unique_ptr<Transaction> tx); |
||||
|
||||
// This function will acquire `write_mu_` to make sure the binder is not used
|
||||
// concurrently, so this can be called by different threads safely.
|
||||
absl::Status MakeBinderTransaction( |
||||
BinderTransportTxCode tx_code, |
||||
std::function<absl::Status(WritableParcel*)> fill_parcel); |
||||
|
||||
// Send a stream to `binder_`. Set `is_last_chunk` to `true` if the stream
|
||||
// transaction has been sent completely. Otherwise set to `false`.
|
||||
absl::Status RunStreamTx(RunScheduledTxArgs::StreamTx* stream_tx, |
||||
WritableParcel* parcel, bool* is_last_chunk) |
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(write_mu_); |
||||
|
||||
// Schdule `RunScheduledTxArgs*` in `pending_outgoing_tx_` to `combiner_`, as
|
||||
// many as possible (under the constraint of `kFlowControlWindowSize`).
|
||||
void TryScheduleTransaction(); |
||||
|
||||
// Guards variables related to transport state.
|
||||
grpc_core::Mutex write_mu_; |
||||
std::unique_ptr<Binder> binder_ ABSL_GUARDED_BY(write_mu_); |
||||
|
||||
// Maps the transaction code (which identifies streams) to their next
|
||||
// available sequence number. See
|
||||
// https://github.com/grpc/proposal/blob/master/L73-java-binderchannel/wireformat.md#sequence-number
|
||||
absl::flat_hash_map<int, int> next_seq_num_ ABSL_GUARDED_BY(write_mu_); |
||||
|
||||
// Number of bytes we have already sent in stream transactions.
|
||||
std::atomic<int64_t> num_outgoing_bytes_{0}; |
||||
|
||||
// Guards variables related to flow control logic.
|
||||
grpc_core::Mutex flow_control_mu_; |
||||
int64_t num_acknowledged_bytes_ ABSL_GUARDED_BY(flow_control_mu_) = 0; |
||||
|
||||
// The queue takes ownership of the pointer.
|
||||
std::queue<RunScheduledTxArgs*> pending_outgoing_tx_ |
||||
ABSL_GUARDED_BY(flow_control_mu_); |
||||
int num_non_acked_tx_in_combiner_ ABSL_GUARDED_BY(flow_control_mu_) = 0; |
||||
|
||||
// Helper variable for determining if we are currently calling into
|
||||
// `Binder::Transact`. Useful for avoiding the attempt of acquiring
|
||||
// `write_mu_` multiple times on the same thread.
|
||||
std::atomic_bool is_transacting_{false}; |
||||
|
||||
grpc_core::Combiner* combiner_; |
||||
}; |
||||
|
||||
} // namespace grpc_binder
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_BINDER_WIRE_FORMAT_WIRE_WRITER_H
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue