Import binder transport channel create interface (#27007)

Also try use the API in example apk to make sure the binder transport
code and the rest of gRPC compiles with the Android toolchain

We will properly expose our interface later.
pull/27069/head
Ming-Chuan 4 years ago committed by GitHub
parent f292f001ee
commit 9da755a61e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      examples/android/binder/java/io/grpc/binder/cpp/example/BUILD
  2. 14
      examples/android/binder/java/io/grpc/binder/cpp/example/native.cc
  3. 71
      src/core/ext/transport/binder/client/BUILD
  4. 99
      src/core/ext/transport/binder/client/channel_create.cc
  5. 50
      src/core/ext/transport/binder/client/channel_create.h
  6. 61
      src/core/ext/transport/binder/client/channel_create_impl.cc
  7. 33
      src/core/ext/transport/binder/client/channel_create_impl.h
  8. 63
      src/core/ext/transport/binder/client/jni_utils.cc
  9. 38
      src/core/ext/transport/binder/client/jni_utils.h
  10. 13
      src/core/ext/transport/binder/wire_format/binder_android.cc
  11. 4
      src/core/ext/transport/binder/wire_format/binder_constants.cc
  12. 7
      tools/internal_ci/linux/grpc_binder_transport_apk_build_in_docker.sh

@ -20,9 +20,14 @@ cc_library(
linkopts = [
"-ldl",
"-llog",
"-lm",
"-lbinder_ndk",
"-Wl,--no-undefined",
],
deps = [],
deps = [
# Temporarily directly depend on this target before we expose a public API
"//src/core/ext/transport/binder/client:grpc_transport_binder_client",
],
alwayslink = True,
)
@ -36,6 +41,7 @@ android_library(
resource_files = glob(["res/**"]),
deps = [
":jni_lib",
"@binder_transport_android_helper//io/grpc/binder/cpp:connection_helper",
],
)

@ -1,30 +1,34 @@
// 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 <android/log.h>
#include <jni.h>
#include "src/core/ext/transport/binder/client/channel_create.h"
extern "C" JNIEXPORT jstring JNICALL
Java_io_grpc_binder_cpp_example_ButtonPressHandler_native_1entry(
JNIEnv* env, jobject /*this*/, jobject /*application*/) {
JNIEnv* env, jobject /*this*/, jobject application) {
static bool first = true;
__android_log_print(ANDROID_LOG_INFO, "Demo", "Line number %d", __LINE__);
if (first) {
first = false;
grpc::experimental::BindToOnDeviceServerService(env, application, "", "");
return env->NewStringUTF("Clicked 1 time");
} else {
// Create a channel. For now we only want to make sure it compiles.
auto channel =
grpc::experimental::CreateBinderChannel(env, application, "", "");
return env->NewStringUTF("Clicked more than 1 time");
}
}

@ -0,0 +1,71 @@
# Copyright 2021 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
load("//bazel:grpc_build_system.bzl", "grpc_cc_library")
licenses(["notice"])
package(
default_visibility = ["//visibility:public"],
)
# TODO(mingcl): See if it is possible to add flags that warns about undefined symbol at link time
grpc_cc_library(
name = "grpc_transport_binder_client",
srcs = [
"channel_create.cc",
"channel_create.h",
"jni_utils.cc",
],
hdrs = [
"channel_create.h",
"jni_utils.h",
],
external_deps = [
"absl/strings",
"absl/time",
],
deps = [
":grpc_transport_binder_client_impl",
"//:gpr",
"//:gpr_base",
"//:grpc",
"//:grpc++_base",
"//:grpc_base",
"//:grpc_base_c",
"//:grpc_codegen",
"//src/core/ext/transport/binder/transport",
"//src/core/ext/transport/binder/utils:transport_stream_receiver",
"//src/core/ext/transport/binder/wire_format:binder_android",
"//src/core/ext/transport/binder/wire_format:wire_reader",
],
)
grpc_cc_library(
name = "grpc_transport_binder_client_impl",
srcs = ["channel_create_impl.cc"],
hdrs = ["channel_create_impl.h"],
external_deps = [],
deps = [
"//:gpr",
"//:gpr_base",
"//:grpc",
"//:grpc++_base",
"//:grpc_base",
"//:grpc_base_c",
"//:grpc_codegen",
"//src/core/ext/transport/binder/transport",
"//src/core/ext/transport/binder/wire_format:binder",
],
)

@ -0,0 +1,99 @@
// 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/impl/codegen/port_platform.h>
#include "src/core/ext/transport/binder/client/channel_create.h"
#if defined(ANDROID) || defined(__ANDROID__)
#include <android/binder_auto_utils.h>
#include <android/binder_ibinder.h>
#include <android/binder_ibinder_jni.h>
#include <android/binder_interface_utils.h>
#include <grpc/grpc.h>
#include <grpc/grpc_posix.h>
#include <grpc/support/log.h>
#include <grpc/support/port_platform.h>
#include <grpcpp/impl/grpc_library.h>
#include "absl/memory/memory.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "src/core/ext/transport/binder/client/channel_create_impl.h"
#include "src/core/ext/transport/binder/client/jni_utils.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/surface/channel.h"
#include "src/core/lib/transport/transport.h"
#include "src/cpp/client/create_channel_internal.h"
namespace grpc {
namespace experimental {
// This should be called before calling CreateBinderChannel
// TODO(mingcl): Pass package_name and class_name down to connection helper
// TODO(mingcl): Invoke a callback and pass binder object to caller after a
// successful bind
void BindToOnDeviceServerService(void* jni_env_void, jobject application,
absl::string_view /*package_name*/,
absl::string_view /*class_name*/
) {
// Init gRPC library first so gpr_log works
grpc::internal::GrpcLibrary init_lib;
init_lib.init();
JNIEnv* jni_env = static_cast<JNIEnv*>(jni_env_void);
// clang-format off
CallStaticJavaMethod(jni_env,
"io/grpc/binder/cpp/NativeConnectionHelper",
"tryEstablishConnection",
"(Landroid/content/Context;)V",
application);
// clang-format on
}
// BindToOndeviceServerService need to be called before this, in a different
// task (due to Android API design). (Reference:
// https://stackoverflow.com/a/3055749)
// TODO(mingcl): Support multiple endpoint binder objects
std::shared_ptr<grpc::Channel> CreateBinderChannel(
void* jni_env_void, jobject /*application*/,
absl::string_view /*package_name*/, absl::string_view /*class_name*/) {
JNIEnv* jni_env = static_cast<JNIEnv*>(jni_env_void);
// clang-format off
jobject object = CallStaticJavaMethodForObject(
jni_env,
"io/grpc/binder/cpp/NativeConnectionHelper",
"getServiceBinder",
"()Landroid/os/IBinder;");
// clang-format on
return CreateChannelInternal(
"",
::grpc::internal::CreateChannelFromBinderImpl(
absl::make_unique<grpc_binder::BinderAndroid>(
grpc_binder::FromJavaBinder(jni_env, object)),
nullptr),
std::vector<
std::unique_ptr<experimental::ClientInterceptorFactoryInterface>>());
}
} // namespace experimental
} // namespace grpc
#endif // ANDROID

@ -0,0 +1,50 @@
// 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_CORE_EXT_TRANSPORT_BINDER_CLIENT_CHANNEL_CREATE_H
#define GRPC_CORE_EXT_TRANSPORT_BINDER_CLIENT_CHANNEL_CREATE_H
#if defined(ANDROID) || defined(__ANDROID__)
#include <grpc/impl/codegen/port_platform.h>
#include <grpc/impl/codegen/grpc_types.h>
#include <grpc/support/port_platform.h>
#include <grpcpp/channel.h>
#include <jni.h>
#include "absl/strings/string_view.h"
namespace grpc {
namespace experimental {
// This need be called before calling CreateBinderChannel, and the thread need
// to be free before invoking CreateBinderChannel.
// TODO(mingcl): Add more explanation on this after we determine the interfaces.
void BindToOnDeviceServerService(void* jni_env_void, jobject application,
absl::string_view /*package_name*/,
absl::string_view /*class_name*/);
// Need to be invoked after BindToOnDeviceServerService
// Create a new Channel from server package name and service class name
std::shared_ptr<grpc::Channel> CreateBinderChannel(
void* jni_env_void, jobject application, absl::string_view package_name,
absl::string_view class_name);
} // namespace experimental
} // namespace grpc
#endif
#endif // GRPC_CORE_EXT_TRANSPORT_BINDER_CLIENT_CHANNEL_CREATE_H

@ -0,0 +1,61 @@
// 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/impl/codegen/port_platform.h>
#include "src/core/ext/transport/binder/client/channel_create_impl.h"
#include <memory>
#include <utility>
#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/surface/api_trace.h"
#include "src/core/lib/surface/channel.h"
namespace grpc {
namespace internal {
grpc_channel* CreateChannelFromBinderImpl(
std::unique_ptr<grpc_binder::Binder> endpoint_binder,
const grpc_channel_args* args) {
grpc_core::ExecCtx exec_ctx;
GRPC_API_TRACE("grpc_channel_create_from_binder(target=%p, args=%p)", 2,
((void*)1234, args));
grpc_transport* transport =
grpc_create_binder_transport_client(std::move(endpoint_binder));
GPR_ASSERT(transport);
// TODO(b/192207753): check binder alive and ping binder
// TODO(b/192207758): Figure out if we are required to set authority here
grpc_arg default_authority_arg = grpc_channel_arg_string_create(
const_cast<char*>(GRPC_ARG_DEFAULT_AUTHORITY),
const_cast<char*>("test.authority"));
grpc_channel_args* final_args =
grpc_channel_args_copy_and_add(args, &default_authority_arg, 1);
grpc_error_handle error = GRPC_ERROR_NONE;
grpc_channel* channel = grpc_channel_create(
"binder_target_placeholder", final_args, GRPC_CLIENT_DIRECT_CHANNEL,
transport, nullptr, 0, &error);
// TODO(mingcl): Handle error properly
GPR_ASSERT(error == GRPC_ERROR_NONE);
grpc_channel_args_destroy(final_args);
return channel;
}
} // namespace internal
} // namespace grpc

@ -0,0 +1,33 @@
// 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_CORE_EXT_TRANSPORT_BINDER_CLIENT_CHANNEL_CREATE_IMPL_H
#define GRPC_CORE_EXT_TRANSPORT_BINDER_CLIENT_CHANNEL_CREATE_IMPL_H
#include <grpc/impl/codegen/port_platform.h>
#include "src/core/ext/transport/binder/wire_format/binder.h"
#include "src/core/lib/channel/channel_args.h"
namespace grpc {
namespace internal {
grpc_channel* CreateChannelFromBinderImpl(
std::unique_ptr<grpc_binder::Binder> endpoint_binder,
const grpc_channel_args* args);
} // namespace internal
} // namespace grpc
#endif // GRPC_CORE_EXT_TRANSPORT_BINDER_CLIENT_CHANNEL_CREATE_IMPL_H

@ -0,0 +1,63 @@
// 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/impl/codegen/port_platform.h>
#include "src/core/ext/transport/binder/client/jni_utils.h"
#include <grpc/support/log.h>
#if defined(ANDROID) || defined(__ANDROID__)
void CallStaticJavaMethod(JNIEnv* env, const std::string& clazz,
const std::string& method, const std::string& type,
jobject application) {
jclass cl = env->FindClass(clazz.c_str());
if (cl == nullptr) {
gpr_log(GPR_ERROR, "No class %s", clazz.c_str());
}
jmethodID mid = env->GetStaticMethodID(cl, method.c_str(), type.c_str());
if (mid == nullptr) {
gpr_log(GPR_ERROR, "No method id %s", method.c_str());
}
env->CallStaticVoidMethod(cl, mid, application);
}
jobject CallStaticJavaMethodForObject(JNIEnv* env, const std::string& clazz,
const std::string& method,
const std::string& type) {
jclass cl = env->FindClass(clazz.c_str());
if (cl == nullptr) {
gpr_log(GPR_ERROR, "No class %s", clazz.c_str());
return nullptr;
}
jmethodID mid = env->GetStaticMethodID(cl, method.c_str(), type.c_str());
if (mid == nullptr) {
gpr_log(GPR_ERROR, "No method id %s", method.c_str());
return nullptr;
}
jobject object = env->CallStaticObjectMethod(cl, mid);
if (object == nullptr) {
gpr_log(GPR_ERROR, "Got null object from Java");
return nullptr;
}
return object;
}
#endif

@ -0,0 +1,38 @@
// 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_CORE_EXT_TRANSPORT_BINDER_CLIENT_JNI_UTILS_H
#define GRPC_CORE_EXT_TRANSPORT_BINDER_CLIENT_JNI_UTILS_H
#if defined(ANDROID) || defined(__ANDROID__)
#include <grpc/impl/codegen/port_platform.h>
#include <jni.h>
#include <string>
// TODO(mingcl): Put these functions in a proper namespace
// TODO(mingcl): Use string_view
void CallStaticJavaMethod(JNIEnv* env, const std::string& clazz,
const std::string& method, const std::string& type,
jobject application);
jobject CallStaticJavaMethodForObject(JNIEnv* env, const std::string& clazz,
const std::string& method,
const std::string& type);
#endif
#endif // GRPC_CORE_EXT_TRANSPORT_BINDER_CLIENT_JNI_UTILS_H

@ -52,12 +52,12 @@ void f_onDestroy_delete(void* data) {
delete user_data;
}
void* f_onCreate_noop(void* args) { return nullptr; }
void f_onDestroy_noop(void* userData) {}
void* f_onCreate_noop(void* /*args*/) { return nullptr; }
void f_onDestroy_noop(void* /*userData*/) {}
// TODO(mingcl): Consider if thread safety is a requirement here
binder_status_t f_onTransact(AIBinder* binder, transaction_code_t code,
const AParcel* in, AParcel* out) {
const AParcel* in, AParcel* /*out*/) {
gpr_log(GPR_INFO, __func__);
gpr_log(GPR_INFO, "tx code = %u", code);
@ -115,8 +115,9 @@ TransactionReceiverAndroid::~TransactionReceiverAndroid() {
namespace {
binder_status_t f_onTransact_noop(AIBinder* binder, transaction_code_t code,
const AParcel* in, AParcel* out) {
binder_status_t f_onTransact_noop(AIBinder* /*binder*/,
transaction_code_t /*code*/,
const AParcel* /*in*/, AParcel* /*out*/) {
return {};
}
@ -220,7 +221,7 @@ bool byte_array_allocator(void* arrayData, int32_t length, int8_t** outBuffer) {
tmp.resize(length);
*reinterpret_cast<std::string*>(arrayData) = tmp;
*outBuffer = reinterpret_cast<int8_t*>(
&((*reinterpret_cast<std::string*>(arrayData))[0]));
&(*reinterpret_cast<std::string*>(arrayData))[0]);
return true;
}

@ -16,12 +16,12 @@
#include "src/core/ext/transport/binder/wire_format/binder_constants.h"
#ifndef ANDROID
#if !(defined(ANDROID) || defined(__ANDROID__))
const int FIRST_CALL_TRANSACTION = 0x00000001;
const int LAST_CALL_TRANSACTION = 0x00FFFFFF;
#endif // ANDROID
#endif // !(defined(ANDROID) || defined(__ANDROID__))
namespace grpc_binder {

@ -27,9 +27,14 @@ cd /var/local/git/grpc
echo $ANDROID_HOME
echo $ANDROID_NDK_HOME
# Build all basic targets using the strict warning option which leverages the
# Build all targets using the strict warning option which leverages the
# clang compiler to check if sources can pass a set of warning options.
# CPU are specified because gRPC does not build with 32bit NDK (which has socklen_t
# defined as int due to an accident).
# The python option is for disabling python2 enforcement when packing APK
bazel build --define=use_strict_warning=true \
--fat_apk_cpu=x86_64,arm64-v8a \
--extra_toolchains=@rules_python//python:autodetecting_toolchain_nonstrict \
//examples/android/binder/java/io/grpc/binder/cpp/example:app
# Make sure the Java code that will be invoked by binder transport

Loading…
Cancel
Save