1 // Copyright 2021 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <grpc/support/port_platform.h>
16 
17 #include "src/core/ext/transport/binder/client/jni_utils.h"
18 
19 #ifndef GRPC_NO_BINDER
20 
21 #include <grpc/support/log.h>
22 
23 #include "src/core/lib/gprpp/crash.h"
24 
25 #if defined(ANDROID) || defined(__ANDROID__)
26 
27 namespace grpc_binder {
28 
FindNativeConnectionHelper(JNIEnv * env)29 jclass FindNativeConnectionHelper(JNIEnv* env) {
30   return FindNativeConnectionHelper(
31       env, [env](std::string cl) { return env->FindClass(cl.c_str()); });
32 }
33 
FindNativeConnectionHelper(JNIEnv * env,std::function<void * (std::string)> class_finder)34 jclass FindNativeConnectionHelper(
35     JNIEnv* env, std::function<void*(std::string)> class_finder) {
36   auto do_find = [env, class_finder]() {
37     jclass cl = static_cast<jclass>(
38         class_finder("io/grpc/binder/cpp/NativeConnectionHelper"));
39     if (cl == nullptr) {
40       return cl;
41     }
42     jclass global_cl = static_cast<jclass>(env->NewGlobalRef(cl));
43     env->DeleteLocalRef(cl);
44     GPR_ASSERT(global_cl != nullptr);
45     return global_cl;
46   };
47   static jclass connection_helper_class = do_find();
48   if (connection_helper_class != nullptr) {
49     return connection_helper_class;
50   }
51   // Some possible reasons:
52   //   * There is no Java class in the call stack and this is not invoked
53   //   from JNI_OnLoad
54   //   * The APK does not correctly depends on the helper class, or the
55   //   class get shrinked
56   gpr_log(GPR_ERROR,
57           "Cannot find binder transport Java helper class. Did you invoke "
58           "grpc::experimental::InitializeBinderChannelJavaClass correctly "
59           "beforehand? Did the APK correctly include the connection helper "
60           "class (i.e depends on build target "
61           "src/core/ext/transport/binder/java/io/grpc/binder/"
62           "cpp:connection_helper) ?");
63   // TODO(mingcl): Maybe it is worth to try again so the failure can be fixed
64   // by invoking this function again at a different thread.
65   return nullptr;
66 }
67 
TryEstablishConnection(JNIEnv * env,jobject application,absl::string_view pkg,absl::string_view cls,absl::string_view action_name,absl::string_view conn_id)68 void TryEstablishConnection(JNIEnv* env, jobject application,
69                             absl::string_view pkg, absl::string_view cls,
70                             absl::string_view action_name,
71                             absl::string_view conn_id) {
72   std::string method = "tryEstablishConnection";
73   std::string type =
74       "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/"
75       "lang/String;Ljava/lang/String;)V";
76 
77   jclass cl = FindNativeConnectionHelper(env);
78   if (cl == nullptr) {
79     return;
80   }
81 
82   jmethodID mid = env->GetStaticMethodID(cl, method.c_str(), type.c_str());
83   if (mid == nullptr) {
84     gpr_log(GPR_ERROR, "No method id %s", method.c_str());
85   }
86 
87   env->CallStaticVoidMethod(cl, mid, application,
88                             env->NewStringUTF(std::string(pkg).c_str()),
89                             env->NewStringUTF(std::string(cls).c_str()),
90                             env->NewStringUTF(std::string(action_name).c_str()),
91                             env->NewStringUTF(std::string(conn_id).c_str()));
92 }
93 
TryEstablishConnectionWithUri(JNIEnv * env,jobject application,absl::string_view uri,absl::string_view conn_id)94 void TryEstablishConnectionWithUri(JNIEnv* env, jobject application,
95                                    absl::string_view uri,
96                                    absl::string_view conn_id) {
97   std::string method = "tryEstablishConnectionWithUri";
98   std::string type =
99       "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V";
100 
101   jclass cl = FindNativeConnectionHelper(env);
102   if (cl == nullptr) {
103     return;
104   }
105 
106   jmethodID mid = env->GetStaticMethodID(cl, method.c_str(), type.c_str());
107   if (mid == nullptr) {
108     gpr_log(GPR_ERROR, "No method id %s", method.c_str());
109   }
110 
111   env->CallStaticVoidMethod(cl, mid, application,
112                             env->NewStringUTF(std::string(uri).c_str()),
113                             env->NewStringUTF(std::string(conn_id).c_str()));
114 }
115 
IsSignatureMatch(JNIEnv * env,jobject context,int uid1,int uid2)116 bool IsSignatureMatch(JNIEnv* env, jobject context, int uid1, int uid2) {
117   const std::string method = "isSignatureMatch";
118   const std::string type = "(Landroid/content/Context;II)Z";
119 
120   jclass cl = FindNativeConnectionHelper(env);
121   if (cl == nullptr) {
122     return false;
123   }
124 
125   jmethodID mid = env->GetStaticMethodID(cl, method.c_str(), type.c_str());
126   if (mid == nullptr) {
127     gpr_log(GPR_ERROR, "No method id %s", method.c_str());
128   }
129 
130   jboolean result = env->CallStaticBooleanMethod(cl, mid, context, uid1, uid2);
131   return result == JNI_TRUE;
132 }
133 
134 }  // namespace grpc_binder
135 
136 #endif
137 #endif
138