1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #include "base/android_jni_mock.h"
31 
32 #include "base/logging.h"
33 
34 namespace mozc {
35 namespace jni {
36 
MockJNIEnv()37 MockJNIEnv::MockJNIEnv() {
38   SetUpJNIEnv();
39 }
40 
~MockJNIEnv()41 MockJNIEnv::~MockJNIEnv() {
42   ClearArrayMap();
43   TearDownJNIEnv();
44 }
45 
46 // Setting up a JNIEnv instance.
SetUpJNIEnv()47 void MockJNIEnv::SetUpJNIEnv() {
48   JNINativeInterface *functions = new JNINativeInterface;
49   functions->reserved0 = this;
50   functions->FindClass = MockJNIEnv::FindClassProxy;
51   functions->GetStaticMethodID = MockJNIEnv::GetStaticMethodIDProxy;
52   functions->PushLocalFrame = MockJNIEnv::PushLocalFrameProxy;
53   functions->PopLocalFrame = MockJNIEnv::PopLocalFrameProxy;
54   functions->NewGlobalRef = MockJNIEnv::NewGlobalRefProxy;
55   functions->DeleteGlobalRef = MockJNIEnv::DeleteGlobalRefProxy;
56 
57   functions->CallStaticObjectMethodV =
58       MockJNIEnv::CallStaticObjectMethodVProxy;
59   functions->ExceptionOccurred = MockJNIEnv::ExceptionOccurredProxy;
60 
61   functions->NewByteArray = MockJNIEnv::NewByteArrayProxy;
62   functions->GetArrayLength = MockJNIEnv::GetArrayLengthProxy;
63   functions->GetByteArrayRegion = MockJNIEnv::GetByteArrayRegionProxy;
64   functions->SetByteArrayRegion = MockJNIEnv::SetByteArrayRegionProxy;
65 
66   env_.functions = functions;
67 }
68 
TearDownJNIEnv()69 void MockJNIEnv::TearDownJNIEnv() {
70   delete env_.functions;
71 }
72 
ClearArrayMap()73 void MockJNIEnv::ClearArrayMap() {
74   for (std::map<jbyteArray, std::pair<jsize, jbyte*> >::iterator iter =
75            byte_array_map_.begin();
76        iter != byte_array_map_.end(); ++iter) {
77     delete iter->first;
78     delete [] iter->second.second;
79   }
80   byte_array_map_.clear();
81 }
82 
FindClass(const char * class_path)83 jclass MockJNIEnv::FindClass(const char *class_path) {
84   static const char kHttpClientPath[] =
85       "org/mozc/android/inputmethod/japanese/nativecallback/HttpClient";
86   if (strcmp(class_path, kHttpClientPath) == 0) {
87     return &mock_http_client_class_;
88   }
89   return NULL;
90 }
91 
GetStaticMethodID(jclass cls,const char * name,const char * signature)92 jmethodID MockJNIEnv::GetStaticMethodID(
93     jclass cls, const char *name, const char *signature) {
94   if (cls == &mock_http_client_class_) {
95     if (strcmp(name, "request") == 0 &&
96         strcmp(signature, "([B[B[B)[B") == 0) {
97       return reinterpret_cast<jmethodID>(&mock_request_);
98     }
99     return NULL;
100   }
101   return NULL;
102 }
103 
CallStaticObjectMethodV(jclass cls,jmethodID method,va_list args)104 jobject MockJNIEnv::CallStaticObjectMethodV(
105     jclass cls, jmethodID method, va_list args) {
106   if (cls == &mock_http_client_class_) {
107     CHECK(mock_http_client_.get() != NULL)
108         << "mock_http_client_ is not initialized.";
109     if (method == reinterpret_cast<jmethodID>(&mock_request_)) {
110       jbyteArray method = va_arg(args, jbyteArray);
111       jbyteArray url = va_arg(args, jbyteArray);
112       jbyteArray post_data = va_arg(args, jbyteArray);
113       return mock_http_client_->Request(method, url, post_data);
114     }
115     LOG(FATAL) << "Unexpected call.";
116     return NULL;
117   }
118   LOG(FATAL) << "Unexpected call.";
119   return NULL;
120 }
121 
122 // Utilities to manage jbyteArray instances.
NewByteArray(jsize size)123 jbyteArray MockJNIEnv::NewByteArray(jsize size) {
124   // Use non-public type _jbyteArray, which is the dereferenced type of
125   // jbyteArray. We can use the same technique as MockJMethodId if necessary,
126   // but this hack looks simpler.
127   jbyteArray result = new _jbyteArray;
128   jbyte *buf = new jbyte[size];
129   byte_array_map_[result] = std::make_pair(size, buf);
130   return result;
131 }
132 
GetArrayLength(jarray array)133 jsize MockJNIEnv::GetArrayLength(jarray array) {
134   std::map<jbyteArray, std::pair<jsize, jbyte*> >::iterator iter =
135       byte_array_map_.find(static_cast<jbyteArray>(array));
136   if (iter != byte_array_map_.end()) {
137     return iter->second.first;
138   }
139   return 0;
140 }
141 
GetByteArrayElements(jbyteArray array,jboolean * is_copy)142 jbyte *MockJNIEnv::GetByteArrayElements(jbyteArray array, jboolean *is_copy) {
143   std::map<jbyteArray, std::pair<jsize, jbyte*> >::iterator iter =
144       byte_array_map_.find(array);
145   if (iter != byte_array_map_.end()) {
146     if (is_copy) {
147       *is_copy = JNI_FALSE;
148     }
149     return iter->second.second;
150   }
151   return NULL;
152 }
GetByteArrayRegion(jbyteArray array,jsize start,jsize len,jbyte * buf)153 void MockJNIEnv::GetByteArrayRegion(
154       jbyteArray array, jsize start, jsize len, jbyte *buf) {
155   const jsize size = GetArrayLength(array);
156   CHECK(start <= size);
157   CHECK(start + len <= size);
158   memcpy(buf, GetByteArrayElements(array, NULL) + start, len);
159 }
160 
SetByteArrayRegion(jbyteArray array,jsize start,jsize len,const jbyte * buf)161 void MockJNIEnv::SetByteArrayRegion(
162       jbyteArray array, jsize start, jsize len, const jbyte *buf) {
163   const jsize size = GetArrayLength(array);
164   CHECK(start <= size);
165   CHECK(start + len <= size);
166   memcpy(GetByteArrayElements(array, NULL) + start, buf, len);
167 }
168 
JByteArrayToString(jbyteArray array)169 string MockJNIEnv::JByteArrayToString(jbyteArray array) {
170   jboolean is_copy;
171   jbyte *buffer = GetByteArrayElements(array, &is_copy);
172   CHECK(!is_copy);
173   return string(reinterpret_cast<char*>(buffer), GetArrayLength(array));
174 }
175 
StringToJByteArray(const string & str)176 jbyteArray MockJNIEnv::StringToJByteArray(const string &str) {
177   jbyteArray result = NewByteArray(str.size());
178   jboolean is_copy;
179   jbyte *buffer = GetByteArrayElements(result, &is_copy);
180   CHECK(!is_copy);
181   memcpy(buffer, str.data(), str.size());
182   return result;
183 }
184 
185 // static proxy methods.
FindClassProxy(JNIEnv * env,const char * class_path)186 jclass MockJNIEnv::FindClassProxy(JNIEnv *env, const char *class_path) {
187   return static_cast<MockJNIEnv*>(env->functions->reserved0)
188       ->FindClass(class_path);
189 }
190 
GetStaticMethodIDProxy(JNIEnv * env,jclass cls,const char * name,const char * signature)191 jmethodID MockJNIEnv::GetStaticMethodIDProxy(
192     JNIEnv *env, jclass cls, const char *name, const char *signature) {
193   return static_cast<MockJNIEnv*>(env->functions->reserved0)
194       ->GetStaticMethodID(cls, name, signature);
195 }
196 
PushLocalFrameProxy(JNIEnv *,jint)197 jint MockJNIEnv::PushLocalFrameProxy(JNIEnv *, jint) {
198   return 0;
199 }
200 
PopLocalFrameProxy(JNIEnv *,jobject)201 jobject MockJNIEnv::PopLocalFrameProxy(JNIEnv *, jobject) {
202   return NULL;
203 }
204 
NewGlobalRefProxy(JNIEnv *,jobject obj)205 jobject MockJNIEnv::NewGlobalRefProxy(JNIEnv *, jobject obj) {
206   return obj;
207 }
208 
DeleteGlobalRefProxy(JNIEnv *,jobject)209 void MockJNIEnv::DeleteGlobalRefProxy(JNIEnv *, jobject) {
210 }
211 
CallStaticObjectMethodVProxy(JNIEnv * env,jclass cls,jmethodID method,va_list args)212 jobject MockJNIEnv::CallStaticObjectMethodVProxy(
213     JNIEnv *env, jclass cls, jmethodID method, va_list args) {
214   return static_cast<MockJNIEnv*>(env->functions->reserved0)
215       ->CallStaticObjectMethodV(cls, method, args);
216 }
217 
ExceptionOccurredProxy(JNIEnv *)218 jthrowable MockJNIEnv::ExceptionOccurredProxy(JNIEnv *) {
219   return NULL;
220 }
221 
NewByteArrayProxy(JNIEnv * env,jsize size)222 jbyteArray MockJNIEnv::NewByteArrayProxy(JNIEnv *env, jsize size) {
223   return static_cast<MockJNIEnv*>(env->functions->reserved0)
224       ->NewByteArray(size);
225 }
226 
GetArrayLengthProxy(JNIEnv * env,jarray array)227 jsize MockJNIEnv::GetArrayLengthProxy(JNIEnv *env, jarray array) {
228   return static_cast<MockJNIEnv*>(env->functions->reserved0)
229       ->GetArrayLength(array);
230 }
231 
GetByteArrayRegionProxy(JNIEnv * env,jbyteArray array,jsize start,jsize len,jbyte * buf)232 void MockJNIEnv::GetByteArrayRegionProxy(
233       JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf) {
234   static_cast<MockJNIEnv*>(env->functions->reserved0)
235       ->GetByteArrayRegion(array, start, len, buf);
236 }
237 
SetByteArrayRegionProxy(JNIEnv * env,jbyteArray array,jsize start,jsize len,const jbyte * buf)238 void MockJNIEnv::SetByteArrayRegionProxy(
239       JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf) {
240   static_cast<MockJNIEnv*>(env->functions->reserved0)
241       ->SetByteArrayRegion(array, start, len, buf);
242 }
243 
MockJavaVM()244 MockJavaVM::MockJavaVM() {
245   SetUpJavaVM();
246 }
247 
~MockJavaVM()248 MockJavaVM::~MockJavaVM() {
249   TearDownJavaVM();
250 }
251 
GetEnv(void ** env,jint version)252 jint MockJavaVM::GetEnv(void **env, jint version) {
253   *env = env_.mutable_env();
254   return JNI_OK;
255 }
256 
GetEnvProxy(JavaVM * jvm,void ** env,jint version)257 jint MockJavaVM::GetEnvProxy(JavaVM *jvm, void **env, jint version) {
258   return static_cast<MockJavaVM*>(jvm->functions->reserved0)
259       ->GetEnv(env, version);
260 }
261 
SetUpJavaVM()262 void MockJavaVM::SetUpJavaVM() {
263   JNIInvokeInterface *functions = new JNIInvokeInterface;
264   functions->reserved0 = this;
265   functions->GetEnv = MockJavaVM::GetEnvProxy;
266 
267   jvm_.functions = functions;
268 }
269 
TearDownJavaVM()270 void MockJavaVM::TearDownJavaVM() {
271   delete jvm_.functions;
272 }
273 
274 }  // jni
275 }  // mozc
276