diff --git a/examples/android/binder/java/io/grpc/binder/cpp/example/AndroidManifest.xml b/examples/android/binder/java/io/grpc/binder/cpp/example/AndroidManifest.xml
index ffec2023630..7a60e908523 100644
--- a/examples/android/binder/java/io/grpc/binder/cpp/example/AndroidManifest.xml
+++ b/examples/android/binder/java/io/grpc/binder/cpp/example/AndroidManifest.xml
@@ -1,3 +1,4 @@
+
+
+
+
+
#include
+#include "examples/protos/helloworld.grpc.pb.h"
+#include "examples/protos/helloworld.pb.h"
#include "src/core/ext/transport/binder/client/channel_create.h"
extern "C" JNIEXPORT jstring JNICALL
@@ -23,12 +25,22 @@ Java_io_grpc_binder_cpp_example_ButtonPressHandler_native_1entry(
__android_log_print(ANDROID_LOG_INFO, "Demo", "Line number %d", __LINE__);
if (first) {
first = false;
- grpc::experimental::BindToOnDeviceServerService(env, application, "", "");
+ grpc::experimental::BindToOnDeviceServerService(
+ env, application, "io.grpc.binder.cpp.exampleserver",
+ "io.grpc.binder.cpp.exampleserver.ExportedEndpointService");
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");
+ auto stub = helloworld::Greeter::NewStub(channel);
+ grpc::ClientContext context;
+ helloworld::HelloRequest request;
+ helloworld::HelloReply response;
+ request.set_name("BinderTransportClient");
+ grpc::Status status = stub->SayHello(&context, request, &response);
+ if (status.ok()) {
+ return env->NewStringUTF(response.message().c_str());
+ }
+ return env->NewStringUTF("Clicked more than 1 time. Status not ok");
}
}
diff --git a/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/AndroidManifest.xml b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/AndroidManifest.xml
new file mode 100644
index 00000000000..fe2cd2e7d95
--- /dev/null
+++ b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/AndroidManifest.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/AndroidManifest_endpoint.xml b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/AndroidManifest_endpoint.xml
new file mode 100644
index 00000000000..a9c9d5c4f68
--- /dev/null
+++ b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/AndroidManifest_endpoint.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/BUILD b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/BUILD
new file mode 100644
index 00000000000..dda10b1337d
--- /dev/null
+++ b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/BUILD
@@ -0,0 +1,65 @@
+# 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("@build_bazel_rules_android//android:rules.bzl", "android_binary", "android_library")
+
+cc_library(
+ name = "jni_lib",
+ srcs = ["native.cc"],
+ linkopts = [
+ "-ldl",
+ "-llog",
+ "-lm",
+ "-lbinder_ndk",
+ "-Wl,--no-undefined",
+ ],
+ deps = [
+ # Temporarily directly depend on this target before we expose a public API
+ # TODO(mingcl): Uncomment this after server interfaces are merged
+ # "//src/core/ext/transport/binder/server:grpc_transport_binder_server",
+ "//:grpc++",
+ "//examples/protos:helloworld_cc_grpc",
+ ],
+ alwayslink = True,
+)
+
+android_library(
+ name = "activity",
+ srcs = [
+ "ButtonPressHandler.java",
+ "MainActivity.java",
+ ],
+ manifest = "AndroidManifest.xml",
+ resource_files = glob(["res/**"]),
+ deps = [
+ ":endpoint",
+ ":jni_lib",
+ ],
+)
+
+android_library(
+ name = "endpoint",
+ srcs = ["ExportedEndpointService.java"],
+ exports_manifest = True,
+ manifest = "AndroidManifest_endpoint.xml",
+ deps = [],
+)
+
+android_binary(
+ name = "app",
+ manifest = "AndroidManifest.xml",
+ deps = [
+ ":activity",
+ ],
+)
diff --git a/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/ButtonPressHandler.java b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/ButtonPressHandler.java
new file mode 100644
index 00000000000..34d38ccf58a
--- /dev/null
+++ b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/ButtonPressHandler.java
@@ -0,0 +1,13 @@
+package io.grpc.binder.cpp.exampleserver;
+
+import android.app.Application;
+
+public class ButtonPressHandler {
+ static {
+ System.loadLibrary("app");
+ }
+
+ public String onPressed(Application application) {
+ return "Server Button Pressed";
+ }
+}
diff --git a/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/ExportedEndpointService.java b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/ExportedEndpointService.java
new file mode 100644
index 00000000000..287b0662002
--- /dev/null
+++ b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/ExportedEndpointService.java
@@ -0,0 +1,28 @@
+package io.grpc.binder.cpp.exampleserver;
+
+import android.app.Service;
+import android.os.IBinder;
+import android.content.Intent;
+
+/** Exposes gRPC services running in the main process */
+public final class ExportedEndpointService extends Service {
+ private final IBinder binder;
+
+ static {
+ System.loadLibrary("app");
+ }
+
+ public ExportedEndpointService() {
+ init_grpc_server();
+ binder = get_endpoint_binder();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return binder;
+ }
+
+ public native void init_grpc_server();
+
+ public native IBinder get_endpoint_binder();
+}
diff --git a/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/MainActivity.java b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/MainActivity.java
new file mode 100644
index 00000000000..d252e5f2bd3
--- /dev/null
+++ b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/MainActivity.java
@@ -0,0 +1,27 @@
+package io.grpc.binder.cpp.exampleserver;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Button;
+import android.widget.TextView;
+import io.grpc.binder.cpp.exampleserver.R;
+
+/** Main class for the example app. */
+public class MainActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.v("Example", "hello, world");
+
+ setContentView(R.layout.activity_main);
+
+ Button clickMeButton = findViewById(R.id.clickMeButton);
+ TextView exampleTextView = findViewById(R.id.exampleTextView);
+
+ ButtonPressHandler h = new ButtonPressHandler();
+
+ clickMeButton.setOnClickListener(
+ v -> exampleTextView.setText(h.onPressed(getApplication())));
+ }
+}
diff --git a/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/native.cc b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/native.cc
new file mode 100644
index 00000000000..a64a49a2db9
--- /dev/null
+++ b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/native.cc
@@ -0,0 +1,87 @@
+// 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
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "examples/protos/helloworld.grpc.pb.h"
+#include "examples/protos/helloworld.pb.h"
+
+// TODO(mingcl): Uncomment this after server interfaces are merged
+// #include "src/core/ext/transport/binder/server/binder_server.h"
+// #include "src/core/ext/transport/binder/server/binder_server_credentials.h"
+
+namespace {
+class GreeterService : public helloworld::Greeter::Service {
+ public:
+ grpc::Status SayHello(grpc::ServerContext*,
+ const helloworld::HelloRequest* request,
+ helloworld::HelloReply* response) override {
+ __android_log_print(ANDROID_LOG_INFO, "DemoServer", "Line number %d",
+ __LINE__);
+ __android_log_print(ANDROID_LOG_INFO, "DemoServer", "Got hello request: %s",
+ request->name().c_str());
+ response->set_message("Hi, " + request->name());
+ return grpc::Status::OK;
+ }
+};
+
+} // namespace
+
+extern "C" JNIEXPORT void JNICALL
+Java_io_grpc_binder_cpp_exampleserver_ExportedEndpointService_init_1grpc_1server(
+ JNIEnv* env, jobject /*this*/) {
+ __android_log_print(ANDROID_LOG_INFO, "DemoServer", "Line number %d",
+ __LINE__);
+ static std::unique_ptr server = nullptr;
+
+ if (server != nullptr) {
+ // Already initiated
+ return;
+ }
+
+ static GreeterService service;
+ grpc::ServerBuilder server_builder;
+ server_builder.RegisterService(&service);
+
+ // TODO(mingcl): Uncomment this after server interfaces are merged
+ //
+ // grpc_endpoint_binder_pool_init();
+ // server_builder.AddListeningPort("binder://example.service",
+ // grpc::experimental::BinderServerCredentials());
+
+ server = server_builder.BuildAndStart();
+}
+
+extern "C" JNIEXPORT jobject JNICALL
+Java_io_grpc_binder_cpp_exampleserver_ExportedEndpointService_get_1endpoint_1binder(
+ JNIEnv* env, jobject /*this*/) {
+ __android_log_print(ANDROID_LOG_INFO, "DemoServer", "Line number %d",
+ __LINE__);
+
+ // TODO(mingcl): Uncomment this after server interfaces are merged
+ // auto ai_binder =
+ // static_cast(grpc::experimental::binder::GetEndpointBinder("example.service"));
+
+ AIBinder* ai_binder = nullptr;
+
+ __android_log_print(ANDROID_LOG_INFO, "DemoServer", "ai_binder = %p",
+ ai_binder);
+ return AIBinder_toJavaBinder(env, ai_binder);
+}
diff --git a/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/res/layout/activity_main.xml b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/res/layout/activity_main.xml
new file mode 100644
index 00000000000..e866d8df894
--- /dev/null
+++ b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/res/layout/activity_main.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
diff --git a/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/res/values/strings.xml b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/res/values/strings.xml
new file mode 100644
index 00000000000..7593624fa84
--- /dev/null
+++ b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+
+ Run example
+ 🤔
+
diff --git a/tools/internal_ci/linux/grpc_binder_transport_apk_build_in_docker.sh b/tools/internal_ci/linux/grpc_binder_transport_apk_build_in_docker.sh
index 2fd14da8ec1..d744f90b5ea 100644
--- a/tools/internal_ci/linux/grpc_binder_transport_apk_build_in_docker.sh
+++ b/tools/internal_ci/linux/grpc_binder_transport_apk_build_in_docker.sh
@@ -35,7 +35,8 @@ echo $ANDROID_NDK_HOME
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
+ //examples/android/binder/java/io/grpc/binder/cpp/example:app \
+ //examples/android/binder/java/io/grpc/binder/cpp/exampleserver:app
# Make sure the Java code that will be invoked by binder transport
# implementation builds