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 
31 namespace tcu
32 {
33 namespace Android
34 {
35 
36 using std::string;
37 using std::vector;
38 
39 namespace
40 {
41 
42 class ScopedJNIEnv
43 {
44 public:
45 
46 					ScopedJNIEnv	(JavaVM* vm);
47 					~ScopedJNIEnv	(void);
48 
getVM(void) const49 	JavaVM*			getVM			(void) const { return m_vm;		}
getEnv(void) const50 	JNIEnv*			getEnv			(void) const { return m_env;	}
51 
52 private:
53 	JavaVM* const	m_vm;
54 	JNIEnv*			m_env;
55 	bool			m_detach;
56 };
57 
ScopedJNIEnv(JavaVM * vm)58 ScopedJNIEnv::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 
~ScopedJNIEnv(void)78 ScopedJNIEnv::~ScopedJNIEnv (void)
79 {
80 	if (m_detach)
81 		m_vm->DetachCurrentThread();
82 }
83 
84 class LocalRef
85 {
86 public:
87 					LocalRef		(JNIEnv* env, jobject ref);
88 					~LocalRef		(void);
89 
operator *(void) const90 	jobject			operator*		(void) const { return m_ref;	}
operator bool(void) const91 	operator		bool			(void) const { return !!m_ref;	}
92 
93 private:
94 					LocalRef		(const LocalRef&);
95 	LocalRef&		operator=		(const LocalRef&);
96 
97 	JNIEnv* const	m_env;
98 	const jobject	m_ref;
99 };
100 
LocalRef(JNIEnv * env,jobject ref)101 LocalRef::LocalRef (JNIEnv* env, jobject ref)
102 	: m_env(env)
103 	, m_ref(ref)
104 {
105 }
106 
~LocalRef(void)107 LocalRef::~LocalRef (void)
108 {
109 	if (m_ref)
110 		m_env->DeleteLocalRef(m_ref);
111 }
112 
checkException(JNIEnv * env)113 void 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 
findClass(JNIEnv * env,const char * className)123 jclass 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 
getObjectClass(JNIEnv * env,jobject object)133 jclass 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 
getMethodID(JNIEnv * env,jclass cls,const char * methodName,const char * signature)143 jmethodID 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 
getStringValue(JNIEnv * env,jstring jniStr)153 string 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 
getIntentStringExtra(JNIEnv * env,jobject activity,const char * name)163 string 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 
setRequestedOrientation(JNIEnv * env,jobject activity,ScreenOrientation orientation)186 void 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 
194 template<typename Type>
195 const char* getJNITypeStr (void);
196 
197 template<>
getJNITypeStr(void)198 const char* getJNITypeStr<int> (void)
199 {
200 	return "I";
201 }
202 
203 template<>
getJNITypeStr(void)204 const char* getJNITypeStr<float> (void)
205 {
206 	return "F";
207 }
208 
209 template<>
getJNITypeStr(void)210 const char* getJNITypeStr<string> (void)
211 {
212 	return "Ljava/lang/String;";
213 }
214 
215 template<>
getJNITypeStr(void)216 const char* getJNITypeStr<vector<string> > (void)
217 {
218 	return "[Ljava/lang/String;";
219 }
220 
221 template<typename FieldType>
222 FieldType getStaticFieldValue (JNIEnv* env, jclass cls, jfieldID fieldId);
223 
224 template<>
getStaticFieldValue(JNIEnv * env,jclass cls,jfieldID fieldId)225 int getStaticFieldValue<int> (JNIEnv* env, jclass cls, jfieldID fieldId)
226 {
227 	DE_ASSERT(cls && fieldId);
228 	return env->GetStaticIntField(cls, fieldId);
229 }
230 
231 template<>
getStaticFieldValue(JNIEnv * env,jclass cls,jfieldID fieldId)232 string 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 
242 template<>
getStaticFieldValue(JNIEnv * env,jclass cls,jfieldID fieldId)243 vector<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 
268 template<typename FieldType>
getStaticField(JNIEnv * env,const char * className,const char * fieldName)269 FieldType 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 
282 template<typename FieldType>
283 FieldType getFieldValue (JNIEnv* env, jobject obj, jfieldID fieldId);
284 
285 template<>
getFieldValue(JNIEnv * env,jobject obj,jfieldID fieldId)286 int getFieldValue<int> (JNIEnv* env, jobject obj, jfieldID fieldId)
287 {
288 	DE_ASSERT(obj && fieldId);
289 	return env->GetIntField(obj, fieldId);
290 }
291 
292 template<>
getFieldValue(JNIEnv * env,jobject obj,jfieldID fieldId)293 float getFieldValue<float> (JNIEnv* env, jobject obj, jfieldID fieldId)
294 {
295 	DE_ASSERT(obj && fieldId);
296 	return env->GetFloatField(obj, fieldId);
297 }
298 
299 template<typename FieldType>
getField(JNIEnv * env,jobject obj,const char * fieldName)300 FieldType getField (JNIEnv* env, jobject obj, const char* fieldName)
301 {
302 	const jclass	cls			= getObjectClass(env, obj);
303 	const jfieldID	fieldId		= env->GetFieldID(cls, fieldName, getJNITypeStr<FieldType>());
304 
305 	checkException(env);
306 
307 	if (fieldId)
308 		return getFieldValue<FieldType>(env, obj, fieldId);
309 	else
310 		throw std::runtime_error(string(fieldName) + " not found in object");
311 }
312 
describePlatform(JNIEnv * env,std::ostream & dst)313 void describePlatform (JNIEnv* env, std::ostream& dst)
314 {
315 	const char* const	buildClass		= "android/os/Build";
316 	const char* const	versionClass	= "android/os/Build$VERSION";
317 
318 	static const struct
319 	{
320 		const char*		classPath;
321 		const char*		className;
322 		const char*		fieldName;
323 	} s_stringFields[] =
324 	{
325 		{ buildClass,	"Build",			"BOARD"			},
326 		{ buildClass,	"Build",			"BRAND"			},
327 		{ buildClass,	"Build",			"DEVICE"		},
328 		{ buildClass,	"Build",			"DISPLAY"		},
329 		{ buildClass,	"Build",			"FINGERPRINT"	},
330 		{ buildClass,	"Build",			"HARDWARE"		},
331 		{ buildClass,	"Build",			"MANUFACTURER"	},
332 		{ buildClass,	"Build",			"MODEL"			},
333 		{ buildClass,	"Build",			"PRODUCT"		},
334 		{ buildClass,	"Build",			"TAGS"			},
335 		{ buildClass,	"Build",			"TYPE"			},
336 		{ versionClass,	"Build.VERSION",	"RELEASE"		},
337 	};
338 
339 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_stringFields); ndx++)
340 		dst << s_stringFields[ndx].className << "." << s_stringFields[ndx].fieldName
341 			<< ": " << getStaticField<string>(env, s_stringFields[ndx].classPath, s_stringFields[ndx].fieldName)
342 			<< "\n";
343 
344 	dst << "Build.VERSION.SDK_INT: " << getStaticField<int>(env, versionClass, "SDK_INT") << "\n";
345 
346 	{
347 		const vector<string>	supportedAbis	= getStaticField<vector<string> >(env, buildClass, "SUPPORTED_ABIS");
348 
349 		dst << "Build.SUPPORTED_ABIS: ";
350 
351 		for (size_t ndx = 0; ndx < supportedAbis.size(); ndx++)
352 			dst << (ndx != 0 ? ", " : "") << supportedAbis[ndx];
353 
354 		dst << "\n";
355 	}
356 }
357 
getSupportedABIs(JNIEnv * env)358 vector<string> getSupportedABIs (JNIEnv* env)
359 {
360 	return getStaticField<vector<string> >(env, "android/os/Build", "SUPPORTED_ABIS");
361 }
362 
supportsAny64BitABI(JNIEnv * env)363 bool supportsAny64BitABI (JNIEnv* env)
364 {
365 	const vector<string>	supportedAbis		= getSupportedABIs(env);
366 	const char*				known64BitAbis[]	= { "arm64-v8a", "x86_64", "mips64" };
367 
368 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(known64BitAbis); ++ndx)
369 	{
370 		if (de::contains(supportedAbis.begin(), supportedAbis.end(), string(known64BitAbis[ndx])))
371 			return true;
372 	}
373 
374 	return false;
375 }
376 
supportsAny64BitABI(ANativeActivity * activity)377 bool supportsAny64BitABI (ANativeActivity* activity)
378 {
379 	const ScopedJNIEnv	env(activity->vm);
380 
381 	return supportsAny64BitABI(env.getEnv());
382 }
383 
getPackageManager(JNIEnv * env,jobject activity)384 jobject getPackageManager (JNIEnv* env, jobject activity)
385 {
386 	const jclass		activityCls		= getObjectClass(env, activity);
387 	const jmethodID		getPMID			= getMethodID(env, activityCls, "getPackageManager", "()Landroid/content/pm/PackageManager;");
388 	const jobject		packageManager	= env->CallObjectMethod(activity, getPMID);
389 
390 	return packageManager;
391 }
392 
hasSystemFeature(JNIEnv * env,jobject activity,const char * name)393 bool hasSystemFeature (JNIEnv* env, jobject activity, const char* name)
394 {
395 	const LocalRef		packageManager	(env, getPackageManager(env, activity));
396 	const jclass		pmCls			= getObjectClass(env, *packageManager);
397 	const jmethodID		hasFeatureID	= getMethodID(env, pmCls, "hasSystemFeature", "(Ljava/lang/String;)Z");
398 	const LocalRef		nameStr			(env, env->NewStringUTF(name));
399 	jvalue				callArgs[1];
400 
401 	callArgs[0].l = *nameStr;
402 
403 	return env->CallBooleanMethodA(*packageManager, hasFeatureID, callArgs) == JNI_TRUE;
404 }
405 
getWindowManager(JNIEnv * env,jobject activity)406 jobject getWindowManager (JNIEnv* env, jobject activity)
407 {
408 	const jclass		activityCls		= getObjectClass(env, activity);
409 	const jmethodID		getWMID			= getMethodID(env, activityCls, "getWindowManager", "()Landroid/view/WindowManager;");
410 	const jobject		windowManager	= env->CallObjectMethod(activity, getWMID);
411 
412 	return windowManager;
413 }
414 
getDefaultDisplay(JNIEnv * env,jobject windowManager)415 jobject getDefaultDisplay (JNIEnv* env, jobject windowManager)
416 {
417 	const jclass		wmClass			= getObjectClass(env, windowManager);
418 	const jmethodID		getDisplayID	= getMethodID(env, wmClass, "getDefaultDisplay", "()Landroid/view/Display;");
419 	const jobject		display			= env->CallObjectMethod(windowManager, getDisplayID);
420 
421 	return display;
422 }
423 
createDisplayMetrics(JNIEnv * env)424 jobject createDisplayMetrics (JNIEnv* env)
425 {
426 	const jclass		displayMetricsCls	= findClass(env, "android/util/DisplayMetrics");
427 	const jmethodID		ctorId				= getMethodID(env, displayMetricsCls, "<init>", "()V");
428 
429 	return env->NewObject(displayMetricsCls, ctorId);
430 }
431 
getDisplayMetrics(JNIEnv * env,jobject activity)432 DisplayMetrics getDisplayMetrics (JNIEnv* env, jobject activity)
433 {
434 	const LocalRef		windowManager		(env, getWindowManager(env, activity));
435 	const LocalRef		defaultDisplay		(env, getDefaultDisplay(env, *windowManager));
436 	const LocalRef		nativeMetrics		(env, createDisplayMetrics(env));
437 	const jclass		displayCls			= getObjectClass(env, *defaultDisplay);
438 	const jmethodID		getMetricsID		= getMethodID(env, displayCls, "getMetrics", "(Landroid/util/DisplayMetrics;)V");
439 	DisplayMetrics		metrics;
440 
441 	{
442 		jvalue callArgs[1];
443 		callArgs[0].l = *nativeMetrics;
444 
445 		env->CallVoidMethodA(*defaultDisplay, getMetricsID, callArgs);
446 	}
447 
448 	metrics.density			= getField<float>	(env, *nativeMetrics, "density");
449 	metrics.densityDpi		= getField<int>		(env, *nativeMetrics, "densityDpi");
450 	metrics.scaledDensity	= getField<float>	(env, *nativeMetrics, "scaledDensity");
451 	metrics.widthPixels		= getField<int>		(env, *nativeMetrics, "widthPixels");
452 	metrics.heightPixels	= getField<int>		(env, *nativeMetrics, "heightPixels");
453 	metrics.xdpi			= getField<float>	(env, *nativeMetrics, "xdpi");
454 	metrics.ydpi			= getField<float>	(env, *nativeMetrics, "ydpi");
455 
456 	return metrics;
457 }
458 
459 enum ScreenClass
460 {
461 	SCREEN_CLASS_WEAR	= 0,
462 	SCREEN_CLASS_SMALL,
463 	SCREEN_CLASS_NORMAL,
464 	SCREEN_CLASS_LARGE,
465 	SCREEN_CLASS_EXTRA_LARGE,
466 
467 	SCREEN_CLASS_LAST
468 };
469 
470 enum DensityClass
471 {
472 	DENSITY_CLASS_LDPI		= 120,
473 	DENSITY_CLASS_MDPI		= 160,
474 	DENSITY_CLASS_TVDPI		= 213,
475 	DENSITY_CLASS_HDPI		= 240,
476 	DENSITY_CLASS_280DPI	= 280,
477 	DENSITY_CLASS_XHDPI		= 320,
478 	DENSITY_CLASS_360DPI	= 360,
479 	DENSITY_CLASS_400DPI	= 400,
480 	DENSITY_CLASS_420DPI	= 420,
481 	DENSITY_CLASS_XXHDPI	= 480,
482 	DENSITY_CLASS_560DPI	= 560,
483 	DENSITY_CLASS_XXXHDPI	= 640,
484 
485 	DENSITY_CLASS_INVALID	= -1,
486 };
487 
getScreenClass(const DisplayMetrics & displayMetrics)488 ScreenClass getScreenClass (const DisplayMetrics& displayMetrics)
489 {
490 	static const struct
491 	{
492 		int			minWidthDp;
493 		int			minHeightDp;
494 		ScreenClass	screenClass;
495 	} s_screenClasses[] =
496 	{
497 		// Must be ordered from largest to smallest
498 		{ 960, 720,		SCREEN_CLASS_EXTRA_LARGE	},
499 		{ 640, 480,		SCREEN_CLASS_LARGE			},
500 		{ 480, 320,		SCREEN_CLASS_NORMAL			},
501 		{ 426, 320,		SCREEN_CLASS_SMALL			},
502 	};
503 
504 	const float		dpScale		= float(displayMetrics.densityDpi) / 160.f;
505 
506 	// \note Assume landscape orientation for comparison
507 	const int		widthP		= de::max(displayMetrics.widthPixels, displayMetrics.heightPixels);
508 	const int		heightP		= de::min(displayMetrics.widthPixels, displayMetrics.heightPixels);
509 
510 	const int		widthDp		= deFloorFloatToInt32(float(widthP) / dpScale);
511 	const int		heightDp	= deFloorFloatToInt32(float(heightP) / dpScale);
512 
513 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_screenClasses); ++ndx)
514 	{
515 		if ((s_screenClasses[ndx].minWidthDp <= widthDp) &&
516 			(s_screenClasses[ndx].minHeightDp <= heightDp))
517 			return s_screenClasses[ndx].screenClass;
518 	}
519 
520 	return SCREEN_CLASS_WEAR;
521 }
522 
isValidDensityClass(int dpi)523 bool isValidDensityClass (int dpi)
524 {
525 	switch (dpi)
526 	{
527 		case DENSITY_CLASS_LDPI:
528 		case DENSITY_CLASS_MDPI:
529 		case DENSITY_CLASS_TVDPI:
530 		case DENSITY_CLASS_HDPI:
531 		case DENSITY_CLASS_280DPI:
532 		case DENSITY_CLASS_XHDPI:
533 		case DENSITY_CLASS_360DPI:
534 		case DENSITY_CLASS_400DPI:
535 		case DENSITY_CLASS_420DPI:
536 		case DENSITY_CLASS_XXHDPI:
537 		case DENSITY_CLASS_560DPI:
538 		case DENSITY_CLASS_XXXHDPI:
539 			return true;
540 
541 		default:
542 			return false;
543 	}
544 }
545 
getDensityClass(const DisplayMetrics & displayMetrics)546 DensityClass getDensityClass (const DisplayMetrics& displayMetrics)
547 {
548 	if (isValidDensityClass(displayMetrics.densityDpi))
549 		return (DensityClass)displayMetrics.densityDpi;
550 	else
551 		return DENSITY_CLASS_INVALID;
552 }
553 
554 } // anonymous
555 
mapScreenRotation(ScreenRotation rotation)556 ScreenOrientation mapScreenRotation (ScreenRotation rotation)
557 {
558 	switch (rotation)
559 	{
560 		case SCREENROTATION_UNSPECIFIED:	return SCREEN_ORIENTATION_UNSPECIFIED;
561 		case SCREENROTATION_0:				return SCREEN_ORIENTATION_PORTRAIT;
562 		case SCREENROTATION_90:				return SCREEN_ORIENTATION_LANDSCAPE;
563 		case SCREENROTATION_180:			return SCREEN_ORIENTATION_REVERSE_PORTRAIT;
564 		case SCREENROTATION_270:			return SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
565 		default:
566 			print("Warning: Unsupported rotation");
567 			return SCREEN_ORIENTATION_PORTRAIT;
568 	}
569 }
570 
getIntentStringExtra(ANativeActivity * activity,const char * name)571 string getIntentStringExtra (ANativeActivity* activity, const char* name)
572 {
573 	const ScopedJNIEnv	env(activity->vm);
574 
575 	return getIntentStringExtra(env.getEnv(), activity->clazz, name);
576 }
577 
setRequestedOrientation(ANativeActivity * activity,ScreenOrientation orientation)578 void setRequestedOrientation (ANativeActivity* activity, ScreenOrientation orientation)
579 {
580 	const ScopedJNIEnv	env(activity->vm);
581 
582 	setRequestedOrientation(env.getEnv(), activity->clazz, orientation);
583 }
584 
describePlatform(ANativeActivity * activity,std::ostream & dst)585 void describePlatform (ANativeActivity* activity, std::ostream& dst)
586 {
587 	const ScopedJNIEnv	env(activity->vm);
588 
589 	describePlatform(env.getEnv(), dst);
590 }
591 
hasSystemFeature(ANativeActivity * activity,const char * name)592 bool hasSystemFeature (ANativeActivity* activity, const char* name)
593 {
594 	const ScopedJNIEnv	env(activity->vm);
595 
596 	return hasSystemFeature(env.getEnv(), activity->clazz, name);
597 }
598 
getDisplayMetrics(ANativeActivity * activity)599 DisplayMetrics getDisplayMetrics (ANativeActivity* activity)
600 {
601 	const ScopedJNIEnv	env(activity->vm);
602 
603 	return getDisplayMetrics(env.getEnv(), activity->clazz);
604 }
605 
getCDDRequiredSystemMemory(ANativeActivity * activity)606 size_t getCDDRequiredSystemMemory (ANativeActivity* activity)
607 {
608 	const DisplayMetrics	displayMetrics	= getDisplayMetrics(activity);
609 	const ScreenClass		screenClass		= getScreenClass(displayMetrics);
610 	const bool				isWearDevice	= hasSystemFeature(activity, "android.hardware.type.watch");
611 	const bool				is64BitDevice	= supportsAny64BitABI(activity);
612 	const size_t			MiB				= (size_t)(1<<20);
613 
614 	if (!is64BitDevice)
615 		TCU_CHECK_INTERNAL(sizeof(void*) != sizeof(deUint64));
616 
617 	if (isWearDevice)
618 	{
619 		TCU_CHECK_INTERNAL(!is64BitDevice);
620 		return 416*MiB;
621 	}
622 	else
623 	{
624 		const DensityClass	densityClass	= getDensityClass(displayMetrics);
625 
626 		TCU_CHECK_INTERNAL(de::inRange(screenClass, SCREEN_CLASS_SMALL, SCREEN_CLASS_EXTRA_LARGE));
627 		TCU_CHECK_INTERNAL(densityClass != DENSITY_CLASS_INVALID);
628 
629 		static const struct
630 		{
631 			DensityClass	smallNormalScreenDensity;
632 			DensityClass	largeScreenDensity;
633 			DensityClass	extraLargeScreenDensity;
634 			size_t			requiredMem32bit;
635 			size_t			requiredMem64bit;
636 		} s_classes[] =
637 		{
638 			// Must be ordered from largest to smallest
639 			{ DENSITY_CLASS_560DPI,		DENSITY_CLASS_400DPI,	DENSITY_CLASS_XHDPI,	1344*MiB,	1824*MiB	},
640 			{ DENSITY_CLASS_400DPI,		DENSITY_CLASS_XHDPI,	DENSITY_CLASS_TVDPI,	896*MiB,	1280*MiB	},
641 			{ DENSITY_CLASS_XHDPI,		DENSITY_CLASS_HDPI,		DENSITY_CLASS_MDPI,		512*MiB,	832*MiB		},
642 
643 			// \note Last is default, and density values are maximum allowed
644 			{ DENSITY_CLASS_280DPI,		DENSITY_CLASS_MDPI,		DENSITY_CLASS_LDPI,		424*MiB,	704*MiB		},
645 		};
646 
647 		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_classes); ++ndx)
648 		{
649 			const DensityClass	minClass	= screenClass == SCREEN_CLASS_EXTRA_LARGE	? s_classes[ndx].extraLargeScreenDensity
650 											: screenClass == SCREEN_CLASS_LARGE			? s_classes[ndx].largeScreenDensity
651 											: /* small/normal */						  s_classes[ndx].smallNormalScreenDensity;
652 			const size_t		reqMem		= is64BitDevice ? s_classes[ndx].requiredMem64bit : s_classes[ndx].requiredMem32bit;
653 			const bool			isLast		= ndx == DE_LENGTH_OF_ARRAY(s_classes)-1;
654 
655 			if ((isLast && minClass >= densityClass) || (!isLast && minClass <= densityClass))
656 				return reqMem;
657 		}
658 
659 		TCU_THROW(InternalError, "Invalid combination of density and screen size");
660 	}
661 }
662 
663 } // Android
664 } // tcu
665