1 /*
2 * Copyright 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include <EGL/egl.h>
17 #include <GLES2/gl2.h>
18 #include <fstream>
19 #include <iostream>
20
21 #include "JNIHelper.h"
22
23 namespace ndk_helper
24 {
25
26 #define CLASS_NAME "android/app/NativeActivity"
27
28 //---------------------------------------------------------------------------
29 //JNI Helper functions
30 //---------------------------------------------------------------------------
31
32 //---------------------------------------------------------------------------
33 //Singleton
34 //---------------------------------------------------------------------------
GetInstance()35 JNIHelper* JNIHelper::GetInstance()
36 {
37 static JNIHelper helper;
38 return &helper;
39 }
40
41 //---------------------------------------------------------------------------
42 //Ctor
43 //---------------------------------------------------------------------------
JNIHelper()44 JNIHelper::JNIHelper()
45 {
46 pthread_mutex_init( &mutex_, NULL );
47 }
48
49 //---------------------------------------------------------------------------
50 //Dtor
51 //---------------------------------------------------------------------------
~JNIHelper()52 JNIHelper::~JNIHelper()
53 {
54 pthread_mutex_lock( &mutex_ );
55
56 JNIEnv *env;
57 activity_->vm->AttachCurrentThread( &env, NULL );
58
59 env->DeleteGlobalRef( jni_helper_java_ref_ );
60 env->DeleteGlobalRef( jni_helper_java_class_ );
61
62 activity_->vm->DetachCurrentThread();
63
64 pthread_mutex_destroy( &mutex_ );
65 }
66
67 //---------------------------------------------------------------------------
68 //Init
69 //---------------------------------------------------------------------------
Init(ANativeActivity * activity,const char * helper_class_name)70 void JNIHelper::Init( ANativeActivity* activity,
71 const char* helper_class_name )
72 {
73 JNIHelper& helper = *GetInstance();
74 pthread_mutex_lock( &helper.mutex_ );
75
76 helper.activity_ = activity;
77
78 JNIEnv *env;
79 helper.activity_->vm->AttachCurrentThread( &env, NULL );
80
81 //Retrieve app name
82 jclass android_content_Context = env->GetObjectClass( helper.activity_->clazz );
83 jmethodID midGetPackageName = env->GetMethodID( android_content_Context, "getPackageName",
84 "()Ljava/lang/String;" );
85
86 jstring packageName = (jstring) env->CallObjectMethod( helper.activity_->clazz,
87 midGetPackageName );
88 const char* appname = env->GetStringUTFChars( packageName, NULL );
89 helper.app_name_ = std::string( appname );
90
91 jclass cls = helper.RetrieveClass( env, helper_class_name );
92 helper.jni_helper_java_class_ = (jclass) env->NewGlobalRef( cls );
93
94 jmethodID constructor = env->GetMethodID( helper.jni_helper_java_class_, "<init>", "()V" );
95 helper.jni_helper_java_ref_ = env->NewObject( helper.jni_helper_java_class_, constructor );
96 helper.jni_helper_java_ref_ = env->NewGlobalRef( helper.jni_helper_java_ref_ );
97
98 env->ReleaseStringUTFChars( packageName, appname );
99 helper.activity_->vm->DetachCurrentThread();
100
101 pthread_mutex_unlock( &helper.mutex_ );
102 }
103
104 //---------------------------------------------------------------------------
105 //readFile
106 //---------------------------------------------------------------------------
ReadFile(const char * fileName,std::vector<uint8_t> * buffer_ref)107 bool JNIHelper::ReadFile( const char* fileName,
108 std::vector<uint8_t>* buffer_ref )
109 {
110 if( activity_ == NULL )
111 {
112 LOGI( "JNIHelper has not been initialized.Call init() to initialize the helper" );
113 return false;
114 }
115
116 //First, try reading from externalFileDir;
117 JNIEnv *env;
118 jmethodID mid;
119
120 pthread_mutex_lock( &mutex_ );
121 activity_->vm->AttachCurrentThread( &env, NULL );
122
123 jstring str_path = GetExternalFilesDirJString( env );
124 const char* path = env->GetStringUTFChars( str_path, NULL );
125 std::string s( path );
126
127 if( fileName[0] != '/' )
128 {
129 s.append( "/" );
130 }
131 s.append( fileName );
132 std::ifstream f( s.c_str(), std::ios::binary );
133
134 env->ReleaseStringUTFChars( str_path, path );
135 env->DeleteLocalRef( str_path );
136 activity_->vm->DetachCurrentThread();
137
138 if( f )
139 {
140 LOGI( "reading:%s", s.c_str() );
141 f.seekg( 0, std::ifstream::end );
142 int32_t fileSize = f.tellg();
143 f.seekg( 0, std::ifstream::beg );
144 buffer_ref->reserve( fileSize );
145 buffer_ref->assign( std::istreambuf_iterator<char>( f ), std::istreambuf_iterator<char>() );
146 f.close();
147 pthread_mutex_unlock( &mutex_ );
148 return true;
149 }
150 else
151 {
152 //Fallback to assetManager
153 AAssetManager* assetManager = activity_->assetManager;
154 AAsset* assetFile = AAssetManager_open( assetManager, fileName, AASSET_MODE_BUFFER );
155 if( !assetFile )
156 {
157 pthread_mutex_unlock( &mutex_ );
158 return false;
159 }
160 uint8_t* data = (uint8_t*) AAsset_getBuffer( assetFile );
161 int32_t size = AAsset_getLength( assetFile );
162 if( data == NULL )
163 {
164 AAsset_close( assetFile );
165
166 LOGI( "Failed to load:%s", fileName );
167 pthread_mutex_unlock( &mutex_ );
168 return false;
169 }
170
171 buffer_ref->reserve( size );
172 buffer_ref->assign( data, data + size );
173
174 AAsset_close( assetFile );
175 pthread_mutex_unlock( &mutex_ );
176 return true;
177 }
178 }
179
GetExternalFilesDir()180 std::string JNIHelper::GetExternalFilesDir()
181 {
182 if( activity_ == NULL )
183 {
184 LOGI( "JNIHelper has not been initialized. Call init() to initialize the helper" );
185 return std::string( "" );
186 }
187
188 pthread_mutex_lock( &mutex_ );
189
190 //First, try reading from externalFileDir;
191 JNIEnv *env;
192 jmethodID mid;
193
194 activity_->vm->AttachCurrentThread( &env, NULL );
195
196 jstring strPath = GetExternalFilesDirJString( env );
197 const char* path = env->GetStringUTFChars( strPath, NULL );
198 std::string s( path );
199
200 env->ReleaseStringUTFChars( strPath, path );
201 env->DeleteLocalRef( strPath );
202 activity_->vm->DetachCurrentThread();
203
204 pthread_mutex_unlock( &mutex_ );
205 return s;
206 }
207
LoadTexture(const char * file_name)208 uint32_t JNIHelper::LoadTexture( const char* file_name )
209 {
210 if( activity_ == NULL )
211 {
212 LOGI( "JNIHelper has not been initialized. Call init() to initialize the helper" );
213 return 0;
214 }
215
216 JNIEnv *env;
217 jmethodID mid;
218
219 pthread_mutex_lock( &mutex_ );
220 activity_->vm->AttachCurrentThread( &env, NULL );
221
222 jstring name = env->NewStringUTF( file_name );
223
224 GLuint tex;
225 glGenTextures( 1, &tex );
226 glBindTexture( GL_TEXTURE_2D, tex );
227
228 glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
229 glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
230
231 mid = env->GetMethodID( jni_helper_java_class_, "loadTexture", "(Ljava/lang/String;)Z" );
232 jboolean ret = env->CallBooleanMethod( jni_helper_java_ref_, mid, name );
233 if( !ret )
234 {
235 glDeleteTextures( 1, &tex );
236 tex = -1;
237 LOGI( "Texture load failed %s", file_name );
238 }
239
240 //Generate mipmap
241 glGenerateMipmap( GL_TEXTURE_2D );
242
243 env->DeleteLocalRef( name );
244 activity_->vm->DetachCurrentThread();
245 pthread_mutex_unlock( &mutex_ );
246
247 return tex;
248
249 }
250
ConvertString(const char * str,const char * encode)251 std::string JNIHelper::ConvertString( const char* str,
252 const char* encode )
253 {
254 if( activity_ == NULL )
255 {
256 LOGI( "JNIHelper has not been initialized. Call init() to initialize the helper" );
257 return std::string( "" );
258 }
259
260 JNIEnv *env;
261
262 pthread_mutex_lock( &mutex_ );
263 activity_->vm->AttachCurrentThread( &env, NULL );
264
265 int32_t iLength = strlen( (const char*) str );
266
267 jbyteArray array = env->NewByteArray( iLength );
268 env->SetByteArrayRegion( array, 0, iLength, (const signed char*) str );
269
270 jstring strEncode = env->NewStringUTF( encode );
271
272 jclass cls = env->FindClass( "java/lang/String" );
273 jmethodID ctor = env->GetMethodID( cls, "<init>", "([BLjava/lang/String;)V" );
274 jstring object = (jstring) env->NewObject( cls, ctor, array, strEncode );
275
276 const char *cparam = env->GetStringUTFChars( object, NULL );
277
278 std::string s = std::string( cparam );
279
280 env->ReleaseStringUTFChars( object, cparam );
281 env->DeleteLocalRef( strEncode );
282 env->DeleteLocalRef( object );
283 activity_->vm->DetachCurrentThread();
284 pthread_mutex_unlock( &mutex_ );
285
286 return s;
287 }
288
289 //---------------------------------------------------------------------------
290 //Audio helpers
291 //---------------------------------------------------------------------------
GetNativeAudioBufferSize()292 int32_t JNIHelper::GetNativeAudioBufferSize()
293 {
294 if( activity_ == NULL )
295 {
296 LOGI( "JNIHelper has not been initialized. Call init() to initialize the helper" );
297 return 0;
298 }
299
300 JNIEnv *env;
301 jmethodID mid;
302
303 pthread_mutex_lock( &mutex_ );
304 activity_->vm->AttachCurrentThread( &env, NULL );
305
306 mid = env->GetMethodID( jni_helper_java_class_, "getNativeAudioBufferSize", "()I" );
307 int32_t i = env->CallIntMethod( jni_helper_java_ref_, mid );
308 activity_->vm->DetachCurrentThread();
309 pthread_mutex_unlock( &mutex_ );
310
311 return i;
312 }
313
GetNativeAudioSampleRate()314 int32_t JNIHelper::GetNativeAudioSampleRate()
315 {
316 if( activity_ == NULL )
317 {
318 LOGI( "JNIHelper has not been initialized. Call init() to initialize the helper" );
319 return 0;
320 }
321
322 JNIEnv *env;
323 jmethodID mid;
324
325 pthread_mutex_lock( &mutex_ );
326 activity_->vm->AttachCurrentThread( &env, NULL );
327
328 mid = env->GetMethodID( jni_helper_java_class_, "getNativeAudioSampleRate", "()I" );
329 int32_t i = env->CallIntMethod( jni_helper_java_ref_, mid );
330 activity_->vm->DetachCurrentThread();
331 pthread_mutex_unlock( &mutex_ );
332
333 return i;
334 }
335
336 //---------------------------------------------------------------------------
337 //Misc implementations
338 //---------------------------------------------------------------------------
RetrieveClass(JNIEnv * jni,const char * class_name)339 jclass JNIHelper::RetrieveClass( JNIEnv *jni,
340 const char* class_name )
341 {
342 jclass activity_class = jni->FindClass( CLASS_NAME );
343 jmethodID get_class_loader = jni->GetMethodID( activity_class, "getClassLoader",
344 "()Ljava/lang/ClassLoader;" );
345 jobject cls = jni->CallObjectMethod( activity_->clazz, get_class_loader );
346 jclass class_loader = jni->FindClass( "java/lang/ClassLoader" );
347 jmethodID find_class = jni->GetMethodID( class_loader, "loadClass",
348 "(Ljava/lang/String;)Ljava/lang/Class;" );
349
350 jstring str_class_name = jni->NewStringUTF( class_name );
351 jclass class_retrieved = (jclass) jni->CallObjectMethod( cls, find_class, str_class_name );
352 jni->DeleteLocalRef( str_class_name );
353 return class_retrieved;
354 }
355
GetExternalFilesDirJString(JNIEnv * env)356 jstring JNIHelper::GetExternalFilesDirJString( JNIEnv *env )
357 {
358 if( activity_ == NULL )
359 {
360 LOGI( "JNIHelper has not been initialized. Call init() to initialize the helper" );
361 return NULL;
362 }
363
364 // Invoking getExternalFilesDir() java API
365 jclass cls_Env = env->FindClass( CLASS_NAME );
366 jmethodID mid = env->GetMethodID( cls_Env, "getExternalFilesDir",
367 "(Ljava/lang/String;)Ljava/io/File;" );
368 jobject obj_File = env->CallObjectMethod( activity_->clazz, mid, NULL );
369 jclass cls_File = env->FindClass( "java/io/File" );
370 jmethodID mid_getPath = env->GetMethodID( cls_File, "getPath", "()Ljava/lang/String;" );
371 jstring obj_Path = (jstring) env->CallObjectMethod( obj_File, mid_getPath );
372
373 return obj_Path;
374 }
375
376 } //namespace ndkHelper
377