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 //--------------------------------------------------------------------------- 35 JNIHelper* JNIHelper::GetInstance() 36 { 37 static JNIHelper helper; 38 return &helper; 39 } 40 41 //--------------------------------------------------------------------------- 42 //Ctor 43 //--------------------------------------------------------------------------- 44 JNIHelper::JNIHelper() 45 { 46 pthread_mutex_init( &mutex_, NULL ); 47 } 48 49 //--------------------------------------------------------------------------- 50 //Dtor 51 //--------------------------------------------------------------------------- 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 //--------------------------------------------------------------------------- 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 //--------------------------------------------------------------------------- 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 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 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 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 //--------------------------------------------------------------------------- 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 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 //--------------------------------------------------------------------------- 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 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