/*------------------------------------------------------------------------- * drawElements Quality Program Tester Core * ---------------------------------------- * * Copyright 2014 The Android Open Source Project * * 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. * *//*! * \file * \brief Android utilities. *//*--------------------------------------------------------------------*/ #include "tcuAndroidUtil.hpp" #include "deSTLUtil.hpp" #include "deMath.h" #include namespace tcu { namespace Android { using std::string; using std::vector; namespace { class ScopedJNIEnv { public: ScopedJNIEnv (JavaVM* vm); ~ScopedJNIEnv (void); JavaVM* getVM (void) const { return m_vm; } JNIEnv* getEnv (void) const { return m_env; } private: JavaVM* const m_vm; JNIEnv* m_env; bool m_detach; }; ScopedJNIEnv::ScopedJNIEnv (JavaVM* vm) : m_vm (vm) , m_env (DE_NULL) , m_detach (false) { const int getEnvRes = m_vm->GetEnv((void**)&m_env, JNI_VERSION_1_6); if (getEnvRes == JNI_EDETACHED) { if (m_vm->AttachCurrentThread(&m_env, DE_NULL) != JNI_OK) throw std::runtime_error("JNI AttachCurrentThread() failed"); m_detach = true; } else if (getEnvRes != JNI_OK) throw std::runtime_error("JNI GetEnv() failed"); DE_ASSERT(m_env); } ScopedJNIEnv::~ScopedJNIEnv (void) { if (m_detach) m_vm->DetachCurrentThread(); } class LocalRef { public: LocalRef (JNIEnv* env, jobject ref); ~LocalRef (void); jobject operator* (void) const { return m_ref; } operator bool (void) const { return !!m_ref; } private: LocalRef (const LocalRef&); LocalRef& operator= (const LocalRef&); JNIEnv* const m_env; const jobject m_ref; }; LocalRef::LocalRef (JNIEnv* env, jobject ref) : m_env(env) , m_ref(ref) { } LocalRef::~LocalRef (void) { if (m_ref) m_env->DeleteLocalRef(m_ref); } void checkException (JNIEnv* env) { if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); throw std::runtime_error("Got JNI exception"); } } jclass findClass (JNIEnv* env, const char* className) { const jclass cls = env->FindClass(className); checkException(env); TCU_CHECK_INTERNAL(cls); return cls; } jclass getObjectClass (JNIEnv* env, jobject object) { const jclass cls = env->GetObjectClass(object); checkException(env); TCU_CHECK_INTERNAL(cls); return cls; } jmethodID getMethodID (JNIEnv* env, jclass cls, const char* methodName, const char* signature) { const jmethodID id = env->GetMethodID(cls, methodName, signature); checkException(env); TCU_CHECK_INTERNAL(id); return id; } string getStringValue (JNIEnv* env, jstring jniStr) { const char* ptr = env->GetStringUTFChars(jniStr, DE_NULL); const string str = string(ptr); env->ReleaseStringUTFChars(jniStr, ptr); return str; } string getIntentStringExtra (JNIEnv* env, jobject activity, const char* name) { // \todo [2013-05-12 pyry] Clean up references on error. const jclass activityCls = getObjectClass(env, activity); const LocalRef intent (env, env->CallObjectMethod(activity, getMethodID(env, activityCls, "getIntent", "()Landroid/content/Intent;"))); TCU_CHECK_INTERNAL(intent); const LocalRef extraName (env, env->NewStringUTF(name)); const jclass intentCls = getObjectClass(env, *intent); TCU_CHECK_INTERNAL(extraName && intentCls); jvalue getExtraArgs[1]; getExtraArgs[0].l = *extraName; const LocalRef extraStr (env, env->CallObjectMethodA(*intent, getMethodID(env, intentCls, "getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;"), getExtraArgs)); if (extraStr) return getStringValue(env, (jstring)*extraStr); else return string(); } void setRequestedOrientation (JNIEnv* env, jobject activity, ScreenOrientation orientation) { const jclass activityCls = getObjectClass(env, activity); const jmethodID setOrientationId = getMethodID(env, activityCls, "setRequestedOrientation", "(I)V"); env->CallVoidMethod(activity, setOrientationId, (int)orientation); } template const char* getJNITypeStr (void); template<> const char* getJNITypeStr (void) { return "I"; } template<> const char* getJNITypeStr (void) { return "J"; } template<> const char* getJNITypeStr (void) { return "Ljava/lang/String;"; } template<> const char* getJNITypeStr > (void) { return "[Ljava/lang/String;"; } template FieldType getStaticFieldValue (JNIEnv* env, jclass cls, jfieldID fieldId); template<> int getStaticFieldValue (JNIEnv* env, jclass cls, jfieldID fieldId) { DE_ASSERT(cls && fieldId); return env->GetStaticIntField(cls, fieldId); } template<> string getStaticFieldValue (JNIEnv* env, jclass cls, jfieldID fieldId) { const jstring jniStr = (jstring)env->GetStaticObjectField(cls, fieldId); if (jniStr) return getStringValue(env, jniStr); else return string(); } template<> vector getStaticFieldValue > (JNIEnv* env, jclass cls, jfieldID fieldId) { const jobjectArray array = (jobjectArray)env->GetStaticObjectField(cls, fieldId); vector result; checkException(env); if (array) { const int numElements = env->GetArrayLength(array); for (int ndx = 0; ndx < numElements; ndx++) { const jstring jniStr = (jstring)env->GetObjectArrayElement(array, ndx); checkException(env); if (jniStr) result.push_back(getStringValue(env, jniStr)); } } return result; } template FieldType getStaticField (JNIEnv* env, const char* className, const char* fieldName) { const jclass cls = findClass(env, className); const jfieldID fieldId = env->GetStaticFieldID(cls, fieldName, getJNITypeStr()); checkException(env); if (fieldId) return getStaticFieldValue(env, cls, fieldId); else throw std::runtime_error(string(fieldName) + " not found in " + className); } template FieldType getFieldValue (JNIEnv* env, jobject obj, jfieldID fieldId); template<> deInt64 getFieldValue (JNIEnv* env, jobject obj, jfieldID fieldId) { DE_ASSERT(obj && fieldId); return env->GetLongField(obj, fieldId); } template FieldType getField (JNIEnv* env, jobject obj, const char* fieldName) { const jclass cls = getObjectClass(env, obj); const jfieldID fieldId = env->GetFieldID(cls, fieldName, getJNITypeStr()); checkException(env); if (fieldId) return getFieldValue(env, obj, fieldId); else throw std::runtime_error(string(fieldName) + " not found in object"); } void describePlatform (JNIEnv* env, std::ostream& dst) { const char* const buildClass = "android/os/Build"; const char* const versionClass = "android/os/Build$VERSION"; static const struct { const char* classPath; const char* className; const char* fieldName; } s_stringFields[] = { { buildClass, "Build", "BOARD" }, { buildClass, "Build", "BRAND" }, { buildClass, "Build", "DEVICE" }, { buildClass, "Build", "DISPLAY" }, { buildClass, "Build", "FINGERPRINT" }, { buildClass, "Build", "HARDWARE" }, { buildClass, "Build", "MANUFACTURER" }, { buildClass, "Build", "MODEL" }, { buildClass, "Build", "PRODUCT" }, { buildClass, "Build", "TAGS" }, { buildClass, "Build", "TYPE" }, { versionClass, "Build.VERSION", "RELEASE" }, }; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_stringFields); ndx++) dst << s_stringFields[ndx].className << "." << s_stringFields[ndx].fieldName << ": " << getStaticField(env, s_stringFields[ndx].classPath, s_stringFields[ndx].fieldName) << "\n"; dst << "Build.VERSION.SDK_INT: " << getStaticField(env, versionClass, "SDK_INT") << "\n"; { const vector supportedAbis = getStaticField >(env, buildClass, "SUPPORTED_ABIS"); dst << "Build.SUPPORTED_ABIS: "; for (size_t ndx = 0; ndx < supportedAbis.size(); ndx++) dst << (ndx != 0 ? ", " : "") << supportedAbis[ndx]; dst << "\n"; } } } // anonymous ScreenOrientation mapScreenRotation (ScreenRotation rotation) { switch (rotation) { case SCREENROTATION_UNSPECIFIED: return SCREEN_ORIENTATION_UNSPECIFIED; case SCREENROTATION_0: return SCREEN_ORIENTATION_PORTRAIT; case SCREENROTATION_90: return SCREEN_ORIENTATION_LANDSCAPE; case SCREENROTATION_180: return SCREEN_ORIENTATION_REVERSE_PORTRAIT; case SCREENROTATION_270: return SCREEN_ORIENTATION_REVERSE_LANDSCAPE; default: print("Warning: Unsupported rotation"); return SCREEN_ORIENTATION_PORTRAIT; } } string getIntentStringExtra (ANativeActivity* activity, const char* name) { const ScopedJNIEnv env(activity->vm); return getIntentStringExtra(env.getEnv(), activity->clazz, name); } void setRequestedOrientation (ANativeActivity* activity, ScreenOrientation orientation) { const ScopedJNIEnv env(activity->vm); setRequestedOrientation(env.getEnv(), activity->clazz, orientation); } void describePlatform (ANativeActivity* activity, std::ostream& dst) { const ScopedJNIEnv env(activity->vm); describePlatform(env.getEnv(), dst); } size_t getTotalAndroidSystemMemory (ANativeActivity* activity) { const ScopedJNIEnv scopedJniEnv (activity->vm); JNIEnv* env = scopedJniEnv.getEnv(); // Get activity manager instance: // ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); const jclass activityManagerClass = findClass(env, "android/app/ActivityManager"); const LocalRef activityString (env, env->NewStringUTF("activity")); // Context.ACTIVITY_SERVICE == "activity" const jclass activityClass = getObjectClass(env, activity->clazz); const jmethodID getServiceID = getMethodID(env, activityClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); LocalRef activityManager (env, env->CallObjectMethod(activity->clazz, getServiceID, *activityString)); checkException(env); TCU_CHECK_INTERNAL(activityManager); // Crete memory info instance: // ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); const jclass memoryInfoClass = findClass(env, "android/app/ActivityManager$MemoryInfo"); const jmethodID memoryInfoCtor = getMethodID(env, memoryInfoClass, "", "()V"); LocalRef memoryInfo (env, env->NewObject(memoryInfoClass, memoryInfoCtor)); checkException(env); TCU_CHECK_INTERNAL(memoryInfo); // Get memory info from activity manager: // activityManager.getMemoryInfo(memoryInfo); const jmethodID getMemoryInfoID = getMethodID(env, activityManagerClass, "getMemoryInfo", "(Landroid/app/ActivityManager$MemoryInfo;)V"); checkException(env); env->CallVoidMethod(*activityManager, getMemoryInfoID, *memoryInfo); // Return 'totalMem' field from the memory info instance. return static_cast(getField(env, *memoryInfo, "totalMem")); } } // Android } // tcu