Derive available system memory from Android CDD
authorPyry Haulos <phaulos@google.com>
Wed, 20 Apr 2016 21:39:18 +0000 (14:39 -0700)
committerPyry Haulos <phaulos@google.com>
Fri, 22 Apr 2016 18:07:52 +0000 (11:07 -0700)
Android CDD defines minimum available system memory based on screen size
and density.

Includes various cleanups and robustness improvements to JNI code in
tcuAndroidUtil.cpp.

Bug: 28275297
Change-Id: I727cdf47db8e8f5d765017ed348a316234fe4f8e

framework/platform/android/tcuAndroidPlatform.cpp
framework/platform/android/tcuAndroidPlatform.hpp
framework/platform/android/tcuAndroidUtil.cpp
framework/platform/android/tcuAndroidUtil.hpp

index 40057e2..6d3fc48 100644 (file)
@@ -261,10 +261,35 @@ private:
        WindowRegistry&         m_windowRegistry;
 };
 
+static size_t getTotalSystemMemory (ANativeActivity* activity)
+{
+       const size_t    MiB             = (size_t)(1<<20);
+
+       try
+       {
+               const size_t    cddRequiredSize = getCDDRequiredSystemMemory(activity);
+
+               print("Device has at least %.2f MiB total system memory per Android CDD\n", double(cddRequiredSize) / double(MiB));
+
+               return cddRequiredSize;
+       }
+       catch (const std::exception& e)
+       {
+               // Use relatively high fallback size to encourage CDD-compliant behavior
+               const size_t    fallbackSize    = (sizeof(void*) == sizeof(deUint64)) ? 2048*MiB : 1024*MiB;
+
+               print("WARNING: Failed to determine system memory size required by CDD: %s\n", e.what());
+               print("WARNING: Using fall-back size of %.2f MiB\n", double(fallbackSize) / double(MiB));
+
+               return fallbackSize;
+       }
+}
+
 // Platform
 
 Platform::Platform (NativeActivity& activity)
-       : m_activity(activity)
+       : m_activity                    (activity)
+       , m_totalSystemMemory   (getTotalSystemMemory(activity.getNativeActivity()))
 {
        m_nativeDisplayFactoryRegistry.registerFactory(new NativeDisplayFactory(m_windowRegistry));
        m_contextFactoryRegistry.registerFactory(new eglu::GLContextFactory(m_nativeDisplayFactoryRegistry));
@@ -292,8 +317,17 @@ void Platform::describePlatform (std::ostream& dst) const
 
 void Platform::getMemoryLimits (vk::PlatformMemoryLimits& limits) const
 {
-       limits.totalSystemMemory                                        = 128*1024*1024;
+       // Worst-case estimates
+       const size_t    MiB                             = (size_t)(1<<20);
+       const size_t    baseMemUsage    = 400*MiB;
+       const double    safeUsageRatio  = 0.25;
+
+       limits.totalSystemMemory                                        = de::max((size_t)(double(deInt64(m_totalSystemMemory)-deInt64(baseMemUsage)) * safeUsageRatio), 16*MiB);
+
+       // Assume UMA architecture
        limits.totalDeviceLocalMemory                           = 0;
+
+       // Reasonable worst-case estimates
        limits.deviceMemoryAllocationGranularity        = 64*1024;
        limits.devicePageSize                                           = 4096;
        limits.devicePageTableEntrySize                         = 8;
index b5df4b2..1798777 100644 (file)
@@ -59,6 +59,7 @@ public:
 private:
        NativeActivity&                                 m_activity;
        WindowRegistry                                  m_windowRegistry;
+       const size_t                                    m_totalSystemMemory;
 };
 
 } // Android
index 3c09ce9..c62ee05 100644 (file)
@@ -23,6 +23,9 @@
 
 #include "tcuAndroidUtil.hpp"
 
+#include "deSTLUtil.hpp"
+#include "deMath.h"
+
 #include <vector>
 
 namespace tcu
@@ -33,7 +36,81 @@ namespace Android
 using std::string;
 using std::vector;
 
-void checkJNIException (JNIEnv* env)
+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())
        {
@@ -43,7 +120,37 @@ void checkJNIException (JNIEnv* env)
        }
 }
 
-string getJNIStringValue (JNIEnv* env, jstring jniStr)
+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);
@@ -53,63 +160,37 @@ string getJNIStringValue (JNIEnv* env, jstring jniStr)
        return str;
 }
 
-static string getIntentStringExtra (JNIEnv* env, jobject activity, const char* name)
+string getIntentStringExtra (JNIEnv* env, jobject activity, const char* name)
 {
        // \todo [2013-05-12 pyry] Clean up references on error.
 
-       jclass  activityCls             = env->GetObjectClass(activity);
-       jobject intent                  = env->CallObjectMethod(activity, env->GetMethodID(activityCls, "getIntent", "()Landroid/content/Intent;"));
-       TCU_CHECK(intent);
+       const jclass    activityCls             = getObjectClass(env, activity);
+       const LocalRef  intent                  (env, env->CallObjectMethod(activity, getMethodID(env, activityCls, "getIntent", "()Landroid/content/Intent;")));
+       TCU_CHECK_INTERNAL(intent);
 
-       jstring extraName               = env->NewStringUTF(name);
-       jclass  intentCls               = env->GetObjectClass(intent);
-       TCU_CHECK(extraName && intentCls);
+       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;
-       jstring extraStr = (jstring)env->CallObjectMethodA(intent, env->GetMethodID(intentCls, "getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;"), getExtraArgs);
+       getExtraArgs[0].l = *extraName;
 
-       env->DeleteLocalRef(extraName);
+       const LocalRef  extraStr                (env, env->CallObjectMethodA(*intent, getMethodID(env, intentCls, "getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;"), getExtraArgs));
 
        if (extraStr)
-               return getJNIStringValue(env, extraStr);
+               return getStringValue(env, (jstring)*extraStr);
        else
                return string();
 }
 
-string getIntentStringExtra (ANativeActivity* activity, const char* name)
+void setRequestedOrientation (JNIEnv* env, jobject activity, ScreenOrientation orientation)
 {
-       return getIntentStringExtra(activity->env, activity->clazz, name);
-}
-
-static void setRequestedOrientation (JNIEnv* env, jobject activity, ScreenOrientation orientation)
-{
-       jclass          activityCls                     = env->GetObjectClass(activity);
-       jmethodID       setOrientationId        = env->GetMethodID(activityCls, "setRequestedOrientation", "(I)V");
+       const jclass    activityCls                     = getObjectClass(env, activity);
+       const jmethodID setOrientationId        = getMethodID(env, activityCls, "setRequestedOrientation", "(I)V");
 
        env->CallVoidMethod(activity, setOrientationId, (int)orientation);
 }
 
-void setRequestedOrientation (ANativeActivity* activity, ScreenOrientation orientation)
-{
-       setRequestedOrientation(activity->env, activity->clazz, orientation);
-}
-
-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;
-       }
-}
-
 template<typename Type>
 const char* getJNITypeStr (void);
 
@@ -120,6 +201,12 @@ const char* getJNITypeStr<int> (void)
 }
 
 template<>
+const char* getJNITypeStr<float> (void)
+{
+       return "F";
+}
+
+template<>
 const char* getJNITypeStr<string> (void)
 {
        return "Ljava/lang/String;";
@@ -147,7 +234,7 @@ string getStaticFieldValue<string> (JNIEnv* env, jclass cls, jfieldID fieldId)
        const jstring   jniStr  = (jstring)env->GetStaticObjectField(cls, fieldId);
 
        if (jniStr)
-               return getJNIStringValue(env, jniStr);
+               return getStringValue(env, jniStr);
        else
                return string();
 }
@@ -158,7 +245,7 @@ vector<string> getStaticFieldValue<vector<string> > (JNIEnv* env, jclass cls, jf
        const jobjectArray      array           = (jobjectArray)env->GetStaticObjectField(cls, fieldId);
        vector<string>          result;
 
-       checkJNIException(env);
+       checkException(env);
 
        if (array)
        {
@@ -168,10 +255,10 @@ vector<string> getStaticFieldValue<vector<string> > (JNIEnv* env, jclass cls, jf
                {
                        const jstring   jniStr  = (jstring)env->GetObjectArrayElement(array, ndx);
 
-                       checkJNIException(env);
+                       checkException(env);
 
                        if (jniStr)
-                               result.push_back(getJNIStringValue(env, jniStr));
+                               result.push_back(getStringValue(env, jniStr));
                }
        }
 
@@ -181,62 +268,50 @@ vector<string> getStaticFieldValue<vector<string> > (JNIEnv* env, jclass cls, jf
 template<typename FieldType>
 FieldType getStaticField (JNIEnv* env, const char* className, const char* fieldName)
 {
-       const jclass    cls                     = env->FindClass(className);
-       const jfieldID  fieldId         = cls ? env->GetStaticFieldID(cls, fieldName, getJNITypeStr<FieldType>()) : (jfieldID)0;
+       const jclass    cls                     = findClass(env, className);
+       const jfieldID  fieldId         = env->GetStaticFieldID(cls, fieldName, getJNITypeStr<FieldType>());
 
-       checkJNIException(env);
+       checkException(env);
 
-       if (cls && fieldId)
+       if (fieldId)
                return getStaticFieldValue<FieldType>(env, cls, fieldId);
        else
-               return FieldType();
+               throw std::runtime_error(string(fieldName) + " not found in " + className);
 }
 
-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;
-};
+template<typename FieldType>
+FieldType getFieldValue (JNIEnv* env, jobject obj, jfieldID fieldId);
 
-ScopedJNIEnv::ScopedJNIEnv (JavaVM* vm)
-       : m_vm          (vm)
-       , m_env         (DE_NULL)
-       , m_detach      (false)
+template<>
+int getFieldValue<int> (JNIEnv* env, jobject obj, jfieldID fieldId)
 {
-       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(obj && fieldId);
+       return env->GetIntField(obj, fieldId);
+}
 
-       DE_ASSERT(m_env);
+template<>
+float getFieldValue<float> (JNIEnv* env, jobject obj, jfieldID fieldId)
+{
+       DE_ASSERT(obj && fieldId);
+       return env->GetFloatField(obj, fieldId);
 }
 
-ScopedJNIEnv::~ScopedJNIEnv (void)
+template<typename FieldType>
+FieldType getField (JNIEnv* env, jobject obj, const char* fieldName)
 {
-       if (m_detach)
-               m_vm->DetachCurrentThread();
+       const jclass    cls                     = getObjectClass(env, obj);
+       const jfieldID  fieldId         = env->GetFieldID(cls, fieldName, getJNITypeStr<FieldType>());
+
+       checkException(env);
+
+       if (fieldId)
+               return getFieldValue<FieldType>(env, obj, fieldId);
+       else
+               throw std::runtime_error(string(fieldName) + " not found in object");
 }
 
-void describePlatform (ANativeActivity* activity, std::ostream& dst)
+void describePlatform (JNIEnv* env, std::ostream& dst)
 {
-       const ScopedJNIEnv      env                             (activity->vm);
        const char* const       buildClass              = "android/os/Build";
        const char* const       versionClass    = "android/os/Build$VERSION";
 
@@ -263,13 +338,13 @@ void describePlatform (ANativeActivity* activity, std::ostream& dst)
 
        for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_stringFields); ndx++)
                dst << s_stringFields[ndx].className << "." << s_stringFields[ndx].fieldName
-                       << ": " << getStaticField<string>(env.getEnv(), s_stringFields[ndx].classPath, s_stringFields[ndx].fieldName)
+                       << ": " << getStaticField<string>(env, s_stringFields[ndx].classPath, s_stringFields[ndx].fieldName)
                        << "\n";
 
-       dst << "Build.VERSION.SDK_INT: " << getStaticField<int>(env.getEnv(), versionClass, "SDK_INT") << "\n";
+       dst << "Build.VERSION.SDK_INT: " << getStaticField<int>(env, versionClass, "SDK_INT") << "\n";
 
        {
-               const vector<string>    supportedAbis   = getStaticField<vector<string> >(env.getEnv(), buildClass, "SUPPORTED_ABIS");
+               const vector<string>    supportedAbis   = getStaticField<vector<string> >(env, buildClass, "SUPPORTED_ABIS");
 
                dst << "Build.SUPPORTED_ABIS: ";
 
@@ -280,5 +355,310 @@ void describePlatform (ANativeActivity* activity, std::ostream& dst)
        }
 }
 
+vector<string> getSupportedABIs (JNIEnv* env)
+{
+       return getStaticField<vector<string> >(env, "android/os/Build", "SUPPORTED_ABIS");
+}
+
+bool supportsAny64BitABI (JNIEnv* env)
+{
+       const vector<string>    supportedAbis           = getSupportedABIs(env);
+       const char*                             known64BitAbis[]        = { "arm64-v8a", "x86_64", "mips64" };
+
+       for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(known64BitAbis); ++ndx)
+       {
+               if (de::contains(supportedAbis.begin(), supportedAbis.end(), string(known64BitAbis[ndx])))
+                       return true;
+       }
+
+       return false;
+}
+
+bool supportsAny64BitABI (ANativeActivity* activity)
+{
+       const ScopedJNIEnv      env(activity->vm);
+
+       return supportsAny64BitABI(env.getEnv());
+}
+
+jobject getPackageManager (JNIEnv* env, jobject activity)
+{
+       const jclass            activityCls             = getObjectClass(env, activity);
+       const jmethodID         getPMID                 = getMethodID(env, activityCls, "getPackageManager", "()Landroid/content/pm/PackageManager;");
+       const jobject           packageManager  = env->CallObjectMethod(activity, getPMID);
+
+       return packageManager;
+}
+
+bool hasSystemFeature (JNIEnv* env, jobject activity, const char* name)
+{
+       const LocalRef          packageManager  (env, getPackageManager(env, activity));
+       const jclass            pmCls                   = getObjectClass(env, *packageManager);
+       const jmethodID         hasFeatureID    = getMethodID(env, pmCls, "hasSystemFeature", "(Ljava/lang/String;)Z");
+       const LocalRef          nameStr                 (env, env->NewStringUTF(name));
+       jvalue                          callArgs[1];
+
+       callArgs[0].l = *nameStr;
+
+       return env->CallBooleanMethodA(*packageManager, hasFeatureID, callArgs) == JNI_TRUE;
+}
+
+jobject getWindowManager (JNIEnv* env, jobject activity)
+{
+       const jclass            activityCls             = getObjectClass(env, activity);
+       const jmethodID         getWMID                 = getMethodID(env, activityCls, "getWindowManager", "()Landroid/view/WindowManager;");
+       const jobject           windowManager   = env->CallObjectMethod(activity, getWMID);
+
+       return windowManager;
+}
+
+jobject getDefaultDisplay (JNIEnv* env, jobject windowManager)
+{
+       const jclass            wmClass                 = getObjectClass(env, windowManager);
+       const jmethodID         getDisplayID    = getMethodID(env, wmClass, "getDefaultDisplay", "()Landroid/view/Display;");
+       const jobject           display                 = env->CallObjectMethod(windowManager, getDisplayID);
+
+       return display;
+}
+
+jobject createDisplayMetrics (JNIEnv* env)
+{
+       const jclass            displayMetricsCls       = findClass(env, "android/util/DisplayMetrics");
+       const jmethodID         ctorId                          = getMethodID(env, displayMetricsCls, "<init>", "()V");
+
+       return env->NewObject(displayMetricsCls, ctorId);
+}
+
+DisplayMetrics getDisplayMetrics (JNIEnv* env, jobject activity)
+{
+       const LocalRef          windowManager           (env, getWindowManager(env, activity));
+       const LocalRef          defaultDisplay          (env, getDefaultDisplay(env, *windowManager));
+       const LocalRef          nativeMetrics           (env, createDisplayMetrics(env));
+       const jclass            displayCls                      = getObjectClass(env, *defaultDisplay);
+       const jmethodID         getMetricsID            = getMethodID(env, displayCls, "getMetrics", "(Landroid/util/DisplayMetrics;)V");
+       DisplayMetrics          metrics;
+
+       {
+               jvalue callArgs[1];
+               callArgs[0].l = *nativeMetrics;
+
+               env->CallVoidMethodA(*defaultDisplay, getMetricsID, callArgs);
+       }
+
+       metrics.density                 = getField<float>       (env, *nativeMetrics, "density");
+       metrics.densityDpi              = getField<int>         (env, *nativeMetrics, "densityDpi");
+       metrics.scaledDensity   = getField<float>       (env, *nativeMetrics, "scaledDensity");
+       metrics.widthPixels             = getField<int>         (env, *nativeMetrics, "widthPixels");
+       metrics.heightPixels    = getField<int>         (env, *nativeMetrics, "heightPixels");
+       metrics.xdpi                    = getField<float>       (env, *nativeMetrics, "xdpi");
+       metrics.ydpi                    = getField<float>       (env, *nativeMetrics, "ydpi");
+
+       return metrics;
+}
+
+enum ScreenClass
+{
+       SCREEN_CLASS_WEAR       = 0,
+       SCREEN_CLASS_SMALL,
+       SCREEN_CLASS_NORMAL,
+       SCREEN_CLASS_LARGE,
+       SCREEN_CLASS_EXTRA_LARGE,
+
+       SCREEN_CLASS_LAST
+};
+
+enum DensityClass
+{
+       DENSITY_CLASS_LDPI              = 120,
+       DENSITY_CLASS_MDPI              = 160,
+       DENSITY_CLASS_TVDPI             = 213,
+       DENSITY_CLASS_HDPI              = 240,
+       DENSITY_CLASS_280DPI    = 280,
+       DENSITY_CLASS_XHDPI             = 320,
+       DENSITY_CLASS_360DPI    = 360,
+       DENSITY_CLASS_400DPI    = 400,
+       DENSITY_CLASS_420DPI    = 420,
+       DENSITY_CLASS_XXHDPI    = 480,
+       DENSITY_CLASS_560DPI    = 560,
+       DENSITY_CLASS_XXXHDPI   = 640,
+
+       DENSITY_CLASS_INVALID   = -1,
+};
+
+ScreenClass getScreenClass (const DisplayMetrics& displayMetrics)
+{
+       static const struct
+       {
+               int                     minWidthDp;
+               int                     minHeightDp;
+               ScreenClass     screenClass;
+       } s_screenClasses[] =
+       {
+               // Must be ordered from largest to smallest
+               { 960, 720,             SCREEN_CLASS_EXTRA_LARGE        },
+               { 640, 480,             SCREEN_CLASS_LARGE                      },
+               { 480, 320,             SCREEN_CLASS_NORMAL                     },
+               { 426, 320,             SCREEN_CLASS_SMALL                      },
+       };
+
+       const float             dpScale         = float(displayMetrics.densityDpi) / 160.f;
+
+       // \note Assume landscape orientation for comparison
+       const int               widthP          = de::max(displayMetrics.widthPixels, displayMetrics.heightPixels);
+       const int               heightP         = de::min(displayMetrics.widthPixels, displayMetrics.heightPixels);
+
+       const int               widthDp         = deFloorFloatToInt32(float(widthP) / dpScale);
+       const int               heightDp        = deFloorFloatToInt32(float(heightP) / dpScale);
+
+       for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_screenClasses); ++ndx)
+       {
+               if ((s_screenClasses[ndx].minWidthDp <= widthDp) &&
+                       (s_screenClasses[ndx].minHeightDp <= heightDp))
+                       return s_screenClasses[ndx].screenClass;
+       }
+
+       return SCREEN_CLASS_WEAR;
+}
+
+bool isValidDensityClass (int dpi)
+{
+       switch (dpi)
+       {
+               case DENSITY_CLASS_LDPI:
+               case DENSITY_CLASS_MDPI:
+               case DENSITY_CLASS_TVDPI:
+               case DENSITY_CLASS_HDPI:
+               case DENSITY_CLASS_280DPI:
+               case DENSITY_CLASS_XHDPI:
+               case DENSITY_CLASS_360DPI:
+               case DENSITY_CLASS_400DPI:
+               case DENSITY_CLASS_420DPI:
+               case DENSITY_CLASS_XXHDPI:
+               case DENSITY_CLASS_560DPI:
+               case DENSITY_CLASS_XXXHDPI:
+                       return true;
+
+               default:
+                       return false;
+       }
+}
+
+DensityClass getDensityClass (const DisplayMetrics& displayMetrics)
+{
+       if (isValidDensityClass(displayMetrics.densityDpi))
+               return (DensityClass)displayMetrics.densityDpi;
+       else
+               return DENSITY_CLASS_INVALID;
+}
+
+} // 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);
+}
+
+bool hasSystemFeature (ANativeActivity* activity, const char* name)
+{
+       const ScopedJNIEnv      env(activity->vm);
+
+       return hasSystemFeature(env.getEnv(), activity->clazz, name);
+}
+
+DisplayMetrics getDisplayMetrics (ANativeActivity* activity)
+{
+       const ScopedJNIEnv      env(activity->vm);
+
+       return getDisplayMetrics(env.getEnv(), activity->clazz);
+}
+
+size_t getCDDRequiredSystemMemory (ANativeActivity* activity)
+{
+       const DisplayMetrics    displayMetrics  = getDisplayMetrics(activity);
+       const ScreenClass               screenClass             = getScreenClass(displayMetrics);
+       const bool                              isWearDevice    = hasSystemFeature(activity, "android.hardware.type.watch");
+       const bool                              is64BitDevice   = supportsAny64BitABI(activity);
+       const size_t                    MiB                             = (size_t)(1<<20);
+
+       if (!is64BitDevice)
+               TCU_CHECK_INTERNAL(sizeof(void*) != sizeof(deUint64));
+
+       if (isWearDevice)
+       {
+               TCU_CHECK_INTERNAL(!is64BitDevice);
+               return 416*MiB;
+       }
+       else
+       {
+               const DensityClass      densityClass    = getDensityClass(displayMetrics);
+
+               TCU_CHECK_INTERNAL(de::inRange(screenClass, SCREEN_CLASS_SMALL, SCREEN_CLASS_EXTRA_LARGE));
+               TCU_CHECK_INTERNAL(densityClass != DENSITY_CLASS_INVALID);
+
+               static const struct
+               {
+                       DensityClass    smallNormalScreenDensity;
+                       DensityClass    largeScreenDensity;
+                       DensityClass    extraLargeScreenDensity;
+                       size_t                  requiredMem32bit;
+                       size_t                  requiredMem64bit;
+               } s_classes[] =
+               {
+                       // Must be ordered from largest to smallest
+                       { DENSITY_CLASS_560DPI,         DENSITY_CLASS_400DPI,   DENSITY_CLASS_XHDPI,    1344*MiB,       1824*MiB        },
+                       { DENSITY_CLASS_400DPI,         DENSITY_CLASS_XHDPI,    DENSITY_CLASS_TVDPI,    896*MiB,        1280*MiB        },
+                       { DENSITY_CLASS_XHDPI,          DENSITY_CLASS_HDPI,             DENSITY_CLASS_MDPI,             512*MiB,        832*MiB         },
+
+                       // \note Last is default, and density values are maximum allowed
+                       { DENSITY_CLASS_280DPI,         DENSITY_CLASS_MDPI,             DENSITY_CLASS_LDPI,             424*MiB,        704*MiB         },
+               };
+
+               for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_classes); ++ndx)
+               {
+                       const DensityClass      minClass        = screenClass == SCREEN_CLASS_EXTRA_LARGE       ? s_classes[ndx].extraLargeScreenDensity
+                                                                                       : screenClass == SCREEN_CLASS_LARGE                     ? s_classes[ndx].largeScreenDensity
+                                                                                       : /* small/normal */                                              s_classes[ndx].smallNormalScreenDensity;
+                       const size_t            reqMem          = is64BitDevice ? s_classes[ndx].requiredMem64bit : s_classes[ndx].requiredMem32bit;
+                       const bool                      isLast          = ndx == DE_LENGTH_OF_ARRAY(s_classes)-1;
+
+                       if ((isLast && minClass >= densityClass) || (!isLast && minClass <= densityClass))
+                               return reqMem;
+               }
+
+               TCU_THROW(InternalError, "Invalid combination of density and screen size");
+       }
+}
+
 } // Android
 } // tcu
index 254e890..78601a6 100644 (file)
@@ -52,6 +52,39 @@ ScreenOrientation    mapScreenRotation                       (ScreenRotation rotation);
 
 void                           describePlatform                        (ANativeActivity* activity, std::ostream& dst);
 
+bool                           hasSystemFeature                        (ANativeActivity* activity, const char* name);
+
+//! android.util.DisplayMetrics
+struct DisplayMetrics
+{
+       float           density;
+       float           scaledDensity;
+       int                     densityDpi;
+
+       int                     widthPixels;
+       int                     heightPixels;
+
+       float           xdpi;
+       float           ydpi;
+
+       DisplayMetrics (void)
+               : density               (0.0f)
+               , scaledDensity (0.0f)
+               , densityDpi    (0)
+               , widthPixels   (0)
+               , heightPixels  (0)
+               , xdpi                  (0.0f)
+               , ydpi                  (0.0f)
+       {
+       }
+};
+
+DisplayMetrics         getDisplayMetrics                       (ANativeActivity* activity);
+
+//! Get minimum required system memory that must be available to kernel and
+//! userspace according to Android CDD.
+size_t                         getCDDRequiredSystemMemory      (ANativeActivity* activity);
+
 } // Android
 } // tcu