diff --git a/WORKSPACE b/WORKSPACE index 9f7b7e5e51e..2288b605a47 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -91,6 +91,16 @@ android_ndk_repository( # https://github.com/bazelbuild/bazel/issues/13421 ) +# Prevents bazel's '...' expansion from including the following folder. +# This is required because the BUILD file in the following folder +# will trigger bazel failure when Android SDK is not configured. +# The targets in the following folder need to be included in APK and will +# be invoked by binder transport implementation through JNI. +local_repository( + name = "binder_transport_android_helper", + path = "./src/core/ext/transport/binder/java", +) + # Create msan toolchain configuration for remote execution. rbe_autoconfig( name = "rbe_msan", diff --git a/src/core/ext/transport/binder/java/WORKSPACE b/src/core/ext/transport/binder/java/WORKSPACE new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/core/ext/transport/binder/java/io/grpc/binder/cpp/BUILD b/src/core/ext/transport/binder/java/io/grpc/binder/cpp/BUILD new file mode 100644 index 00000000000..7af224e3022 --- /dev/null +++ b/src/core/ext/transport/binder/java/io/grpc/binder/cpp/BUILD @@ -0,0 +1,27 @@ +# 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_library") + +licenses(["notice"]) + +android_library( + name = "connection_helper", + srcs = [ + "NativeConnectionHelper.java", + "SyncServiceConnection.java", + ], + visibility = ["//visibility:public"], + deps = [], +) diff --git a/src/core/ext/transport/binder/java/io/grpc/binder/cpp/NativeConnectionHelper.java b/src/core/ext/transport/binder/java/io/grpc/binder/cpp/NativeConnectionHelper.java new file mode 100644 index 00000000000..8913e184f59 --- /dev/null +++ b/src/core/ext/transport/binder/java/io/grpc/binder/cpp/NativeConnectionHelper.java @@ -0,0 +1,43 @@ +// 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.os.IBinder; +import android.os.Parcel; + +/** + * This class will be invoked by gRPC binder transport internal implementation to perform operations + * that are only possible in Java + */ +final class NativeConnectionHelper { + static SyncServiceConnection s; + + static void tryEstablishConnection(Context context) { + s = new SyncServiceConnection(context); + s.tryConnect(); + } + + // TODO(mingcl): We should notify C++ once we got the service binder so they don't need to call + // this function to check. For now we assume that this function will only be called after + // successful connection + static IBinder getServiceBinder() { + return s.getIBinder(); + } + + static Parcel getEmptyParcel() { + return Parcel.obtain(); + } +} diff --git a/src/core/ext/transport/binder/java/io/grpc/binder/cpp/SyncServiceConnection.java b/src/core/ext/transport/binder/java/io/grpc/binder/cpp/SyncServiceConnection.java new file mode 100644 index 00000000000..69e17295ba0 --- /dev/null +++ b/src/core/ext/transport/binder/java/io/grpc/binder/cpp/SyncServiceConnection.java @@ -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. + +package io.grpc.binder.cpp; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.util.Log; + +/* Connects to a service synchronously */ +public class SyncServiceConnection implements ServiceConnection { + private final String logTag = "SyncServiceConnection"; + + private Context mContext; + private IBinder mService; + + public SyncServiceConnection(Context context) { + mContext = context; + } + + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + Log.e(logTag, "Service has connected: "); + synchronized (this) { + mService = service; + } + } + + @Override + public void onServiceDisconnected(ComponentName className) { + Log.e(logTag, "Service has disconnected: "); + } + + public void tryConnect() { + synchronized (this) { + Intent intent = new Intent("grpc.io.action.BIND"); + // TODO(mingcl): The component name is currently hard-coded here and should be changed + // manually before compile. We should pump the component name from C++ to here instead after + // we have a server ready for integration test. + ComponentName compName = new ComponentName("redacted", "redacted"); + intent.setComponent(compName); + // 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 ok"); + } else { + Log.e(logTag, "bindService not ok"); + } + } + } + + public IBinder getIBinder() { + return mService; + } +} 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 45e05f42676..af55391902b 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 @@ -31,3 +31,8 @@ echo $ANDROID_NDK_HOME # clang compiler to check if sources can pass a set of warning options. bazel build --define=use_strict_warning=true \ //examples/android/binder/java/io/grpc/binder/cpp/example:app + +# Make sure the Java code that will be invoked by binder transport +# implementation builds +bazel build --define=use_strict_warning=true \ + @binder_transport_android_helper//io/grpc/binder/cpp:connection_helper