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