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