1/*-------------------------------------------------------------------------
2 * drawElements Quality Program Tester Core
3 * ----------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Android utilities.
22 *//*--------------------------------------------------------------------*/
23
24#include "tcuAndroidUtil.hpp"
25
26#include "deSTLUtil.hpp"
27#include "deMath.h"
28
29#include <vector>
30
31namespace tcu
32{
33namespace Android
34{
35
36using std::string;
37using std::vector;
38
39namespace
40{
41
42class ScopedJNIEnv
43{
44public:
45
46					ScopedJNIEnv	(JavaVM* vm);
47					~ScopedJNIEnv	(void);
48
49	JavaVM*			getVM			(void) const { return m_vm;		}
50	JNIEnv*			getEnv			(void) const { return m_env;	}
51
52private:
53	JavaVM* const	m_vm;
54	JNIEnv*			m_env;
55	bool			m_detach;
56};
57
58ScopedJNIEnv::ScopedJNIEnv (JavaVM* vm)
59	: m_vm		(vm)
60	, m_env		(DE_NULL)
61	, m_detach	(false)
62{
63	const int	getEnvRes	= m_vm->GetEnv((void**)&m_env, JNI_VERSION_1_6);
64
65	if (getEnvRes == JNI_EDETACHED)
66	{
67		if (m_vm->AttachCurrentThread(&m_env, DE_NULL) != JNI_OK)
68			throw std::runtime_error("JNI AttachCurrentThread() failed");
69
70		m_detach = true;
71	}
72	else if (getEnvRes != JNI_OK)
73		throw std::runtime_error("JNI GetEnv() failed");
74
75	DE_ASSERT(m_env);
76}
77
78ScopedJNIEnv::~ScopedJNIEnv (void)
79{
80	if (m_detach)
81		m_vm->DetachCurrentThread();
82}
83
84class LocalRef
85{
86public:
87					LocalRef		(JNIEnv* env, jobject ref);
88					~LocalRef		(void);
89
90	jobject			operator*		(void) const { return m_ref;	}
91	operator		bool			(void) const { return !!m_ref;	}
92
93private:
94					LocalRef		(const LocalRef&);
95	LocalRef&		operator=		(const LocalRef&);
96
97	JNIEnv* const	m_env;
98	const jobject	m_ref;
99};
100
101LocalRef::LocalRef (JNIEnv* env, jobject ref)
102	: m_env(env)
103	, m_ref(ref)
104{
105}
106
107LocalRef::~LocalRef (void)
108{
109	if (m_ref)
110		m_env->DeleteLocalRef(m_ref);
111}
112
113void checkException (JNIEnv* env)
114{
115	if (env->ExceptionCheck())
116	{
117		env->ExceptionDescribe();
118		env->ExceptionClear();
119		throw std::runtime_error("Got JNI exception");
120	}
121}
122
123jclass findClass (JNIEnv* env, const char* className)
124{
125	const jclass	cls		= env->FindClass(className);
126
127	checkException(env);
128	TCU_CHECK_INTERNAL(cls);
129
130	return cls;
131}
132
133jclass getObjectClass (JNIEnv* env, jobject object)
134{
135	const jclass	cls		= env->GetObjectClass(object);
136
137	checkException(env);
138	TCU_CHECK_INTERNAL(cls);
139
140	return cls;
141}
142
143jmethodID getMethodID (JNIEnv* env, jclass cls, const char* methodName, const char* signature)
144{
145	const jmethodID		id		= env->GetMethodID(cls, methodName, signature);
146
147	checkException(env);
148	TCU_CHECK_INTERNAL(id);
149
150	return id;
151}
152
153string getStringValue (JNIEnv* env, jstring jniStr)
154{
155	const char*		ptr		= env->GetStringUTFChars(jniStr, DE_NULL);
156	const string	str		= string(ptr);
157
158	env->ReleaseStringUTFChars(jniStr, ptr);
159
160	return str;
161}
162
163string getIntentStringExtra (JNIEnv* env, jobject activity, const char* name)
164{
165	// \todo [2013-05-12 pyry] Clean up references on error.
166
167	const jclass	activityCls		= getObjectClass(env, activity);
168	const LocalRef	intent			(env, env->CallObjectMethod(activity, getMethodID(env, activityCls, "getIntent", "()Landroid/content/Intent;")));
169	TCU_CHECK_INTERNAL(intent);
170
171	const LocalRef	extraName		(env, env->NewStringUTF(name));
172	const jclass	intentCls		= getObjectClass(env, *intent);
173	TCU_CHECK_INTERNAL(extraName && intentCls);
174
175	jvalue getExtraArgs[1];
176	getExtraArgs[0].l = *extraName;
177
178	const LocalRef	extraStr		(env, env->CallObjectMethodA(*intent, getMethodID(env, intentCls, "getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;"), getExtraArgs));
179
180	if (extraStr)
181		return getStringValue(env, (jstring)*extraStr);
182	else
183		return string();
184}
185
186void setRequestedOrientation (JNIEnv* env, jobject activity, ScreenOrientation orientation)
187{
188	const jclass	activityCls			= getObjectClass(env, activity);
189	const jmethodID	setOrientationId	= getMethodID(env, activityCls, "setRequestedOrientation", "(I)V");
190
191	env->CallVoidMethod(activity, setOrientationId, (int)orientation);
192}
193
194template<typename Type>
195const char* getJNITypeStr (void);
196
197template<>
198const char* getJNITypeStr<int> (void)
199{
200	return "I";
201}
202
203template<>
204const char* getJNITypeStr<deInt64> (void)
205{
206	return "J";
207}
208
209template<>
210const char* getJNITypeStr<string> (void)
211{
212	return "Ljava/lang/String;";
213}
214
215template<>
216const char* getJNITypeStr<vector<string> > (void)
217{
218	return "[Ljava/lang/String;";
219}
220
221template<typename FieldType>
222FieldType getStaticFieldValue (JNIEnv* env, jclass cls, jfieldID fieldId);
223
224template<>
225int getStaticFieldValue<int> (JNIEnv* env, jclass cls, jfieldID fieldId)
226{
227	DE_ASSERT(cls && fieldId);
228	return env->GetStaticIntField(cls, fieldId);
229}
230
231template<>
232string getStaticFieldValue<string> (JNIEnv* env, jclass cls, jfieldID fieldId)
233{
234	const jstring	jniStr	= (jstring)env->GetStaticObjectField(cls, fieldId);
235
236	if (jniStr)
237		return getStringValue(env, jniStr);
238	else
239		return string();
240}
241
242template<>
243vector<string> getStaticFieldValue<vector<string> > (JNIEnv* env, jclass cls, jfieldID fieldId)
244{
245	const jobjectArray	array		= (jobjectArray)env->GetStaticObjectField(cls, fieldId);
246	vector<string>		result;
247
248	checkException(env);
249
250	if (array)
251	{
252		const int	numElements		= env->GetArrayLength(array);
253
254		for (int ndx = 0; ndx < numElements; ndx++)
255		{
256			const jstring	jniStr	= (jstring)env->GetObjectArrayElement(array, ndx);
257
258			checkException(env);
259
260			if (jniStr)
261				result.push_back(getStringValue(env, jniStr));
262		}
263	}
264
265	return result;
266}
267
268template<typename FieldType>
269FieldType getStaticField (JNIEnv* env, const char* className, const char* fieldName)
270{
271	const jclass	cls			= findClass(env, className);
272	const jfieldID	fieldId		= env->GetStaticFieldID(cls, fieldName, getJNITypeStr<FieldType>());
273
274	checkException(env);
275
276	if (fieldId)
277		return getStaticFieldValue<FieldType>(env, cls, fieldId);
278	else
279		throw std::runtime_error(string(fieldName) + " not found in " + className);
280}
281
282template<typename FieldType>
283FieldType getFieldValue (JNIEnv* env, jobject obj, jfieldID fieldId);
284
285template<>
286deInt64 getFieldValue<deInt64> (JNIEnv* env, jobject obj, jfieldID fieldId)
287{
288	DE_ASSERT(obj && fieldId);
289	return env->GetLongField(obj, fieldId);
290}
291
292template<typename FieldType>
293FieldType getField (JNIEnv* env, jobject obj, const char* fieldName)
294{
295	const jclass	cls			= getObjectClass(env, obj);
296	const jfieldID	fieldId		= env->GetFieldID(cls, fieldName, getJNITypeStr<FieldType>());
297
298	checkException(env);
299
300	if (fieldId)
301		return getFieldValue<FieldType>(env, obj, fieldId);
302	else
303		throw std::runtime_error(string(fieldName) + " not found in object");
304}
305
306void describePlatform (JNIEnv* env, std::ostream& dst)
307{
308	const char* const	buildClass		= "android/os/Build";
309	const char* const	versionClass	= "android/os/Build$VERSION";
310
311	static const struct
312	{
313		const char*		classPath;
314		const char*		className;
315		const char*		fieldName;
316	} s_stringFields[] =
317	{
318		{ buildClass,	"Build",			"BOARD"			},
319		{ buildClass,	"Build",			"BRAND"			},
320		{ buildClass,	"Build",			"DEVICE"		},
321		{ buildClass,	"Build",			"DISPLAY"		},
322		{ buildClass,	"Build",			"FINGERPRINT"	},
323		{ buildClass,	"Build",			"HARDWARE"		},
324		{ buildClass,	"Build",			"MANUFACTURER"	},
325		{ buildClass,	"Build",			"MODEL"			},
326		{ buildClass,	"Build",			"PRODUCT"		},
327		{ buildClass,	"Build",			"TAGS"			},
328		{ buildClass,	"Build",			"TYPE"			},
329		{ versionClass,	"Build.VERSION",	"RELEASE"		},
330	};
331
332	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_stringFields); ndx++)
333		dst << s_stringFields[ndx].className << "." << s_stringFields[ndx].fieldName
334			<< ": " << getStaticField<string>(env, s_stringFields[ndx].classPath, s_stringFields[ndx].fieldName)
335			<< "\n";
336
337	dst << "Build.VERSION.SDK_INT: " << getStaticField<int>(env, versionClass, "SDK_INT") << "\n";
338
339	{
340		const vector<string>	supportedAbis	= getStaticField<vector<string> >(env, buildClass, "SUPPORTED_ABIS");
341
342		dst << "Build.SUPPORTED_ABIS: ";
343
344		for (size_t ndx = 0; ndx < supportedAbis.size(); ndx++)
345			dst << (ndx != 0 ? ", " : "") << supportedAbis[ndx];
346
347		dst << "\n";
348	}
349}
350
351} // anonymous
352
353ScreenOrientation mapScreenRotation (ScreenRotation rotation)
354{
355	switch (rotation)
356	{
357		case SCREENROTATION_UNSPECIFIED:	return SCREEN_ORIENTATION_UNSPECIFIED;
358		case SCREENROTATION_0:				return SCREEN_ORIENTATION_PORTRAIT;
359		case SCREENROTATION_90:				return SCREEN_ORIENTATION_LANDSCAPE;
360		case SCREENROTATION_180:			return SCREEN_ORIENTATION_REVERSE_PORTRAIT;
361		case SCREENROTATION_270:			return SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
362		default:
363			print("Warning: Unsupported rotation");
364			return SCREEN_ORIENTATION_PORTRAIT;
365	}
366}
367
368string getIntentStringExtra (ANativeActivity* activity, const char* name)
369{
370	const ScopedJNIEnv	env(activity->vm);
371
372	return getIntentStringExtra(env.getEnv(), activity->clazz, name);
373}
374
375void setRequestedOrientation (ANativeActivity* activity, ScreenOrientation orientation)
376{
377	const ScopedJNIEnv	env(activity->vm);
378
379	setRequestedOrientation(env.getEnv(), activity->clazz, orientation);
380}
381
382void describePlatform (ANativeActivity* activity, std::ostream& dst)
383{
384	const ScopedJNIEnv	env(activity->vm);
385
386	describePlatform(env.getEnv(), dst);
387}
388
389size_t				getTotalAndroidSystemMemory		(ANativeActivity* activity)
390{
391	const ScopedJNIEnv	scopedJniEnv				(activity->vm);
392	JNIEnv* env = scopedJniEnv.getEnv();
393
394	// Get activity manager instance:
395	// ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
396	const jclass	activityManagerClass	= findClass(env, "android/app/ActivityManager");
397	const LocalRef	activityString			(env, env->NewStringUTF("activity")); // Context.ACTIVITY_SERVICE == "activity"
398	const jclass	activityClass			= getObjectClass(env, activity->clazz);
399	const jmethodID	getServiceID			= getMethodID(env, activityClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
400	LocalRef		activityManager			(env, env->CallObjectMethod(activity->clazz, getServiceID, *activityString));
401	checkException(env);
402	TCU_CHECK_INTERNAL(activityManager);
403
404	// Crete memory info instance:
405	// ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
406	const jclass	memoryInfoClass			= findClass(env, "android/app/ActivityManager$MemoryInfo");
407	const jmethodID	memoryInfoCtor			= getMethodID(env, memoryInfoClass, "<init>", "()V");
408	LocalRef		memoryInfo				(env, env->NewObject(memoryInfoClass, memoryInfoCtor));
409	checkException(env);
410	TCU_CHECK_INTERNAL(memoryInfo);
411
412	// Get memory info from activity manager:
413	// activityManager.getMemoryInfo(memoryInfo);
414	const jmethodID	getMemoryInfoID			= getMethodID(env, activityManagerClass, "getMemoryInfo", "(Landroid/app/ActivityManager$MemoryInfo;)V");
415	checkException(env);
416	env->CallVoidMethod(*activityManager, getMemoryInfoID, *memoryInfo);
417
418	// Return 'totalMem' field from the memory info instance.
419	return static_cast<size_t>(getField<deInt64>(env, *memoryInfo, "totalMem"));
420}
421
422} // Android
423} // tcu
424