Home | History | Annotate | Download | only in android
      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 
     49 	JavaVM*			getVM			(void) const { return m_vm;		}
     50 	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 
     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 
     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 
     90 	jobject			operator*		(void) const { return m_ref;	}
     91 	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 
    101 LocalRef::LocalRef (JNIEnv* env, jobject ref)
    102 	: m_env(env)
    103 	, m_ref(ref)
    104 {
    105 }
    106 
    107 LocalRef::~LocalRef (void)
    108 {
    109 	if (m_ref)
    110 		m_env->DeleteLocalRef(m_ref);
    111 }
    112 
    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 
    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 
    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 
    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 
    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 
    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 
    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<>
    198 const char* getJNITypeStr<int> (void)
    199 {
    200 	return "I";
    201 }
    202 
    203 template<>
    204 const char* getJNITypeStr<float> (void)
    205 {
    206 	return "F";
    207 }
    208 
    209 template<>
    210 const char* getJNITypeStr<string> (void)
    211 {
    212 	return "Ljava/lang/String;";
    213 }
    214 
    215 template<>
    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<>
    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<>
    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<>
    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>
    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<>
    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<>
    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>
    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 
    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 
    358 vector<string> getSupportedABIs (JNIEnv* env)
    359 {
    360 	return getStaticField<vector<string> >(env, "android/os/Build", "SUPPORTED_ABIS");
    361 }
    362 
    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 
    377 bool supportsAny64BitABI (ANativeActivity* activity)
    378 {
    379 	const ScopedJNIEnv	env(activity->vm);
    380 
    381 	return supportsAny64BitABI(env.getEnv());
    382 }
    383 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    578 void setRequestedOrientation (ANativeActivity* activity, ScreenOrientation orientation)
    579 {
    580 	const ScopedJNIEnv	env(activity->vm);
    581 
    582 	setRequestedOrientation(env.getEnv(), activity->clazz, orientation);
    583 }
    584 
    585 void describePlatform (ANativeActivity* activity, std::ostream& dst)
    586 {
    587 	const ScopedJNIEnv	env(activity->vm);
    588 
    589 	describePlatform(env.getEnv(), dst);
    590 }
    591 
    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 
    599 DisplayMetrics getDisplayMetrics (ANativeActivity* activity)
    600 {
    601 	const ScopedJNIEnv	env(activity->vm);
    602 
    603 	return getDisplayMetrics(env.getEnv(), activity->clazz);
    604 }
    605 
    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