From a09f59910b129ccd112320e0e64b1e230e2cd9eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 17 Oct 2026 18:13:49 +0200 Subject: [PATCH] Android: Fix JNI edge cases, read Java strings as real UTF-8 - Scoped storage no longer flips on while no activity is registered: whether legacy storage was preserved is fixed per install, so it's asked once. - Free space reads as unknown (-1), not 0 bytes, without an activity. - Strings from Java (file listings and info, crash history, the camera list, the input debug string) go through GetJavaString, so names with characters outside the BMP aren't mangled, and a null string can't crash the crash history. - Requests (inputbox, browse_*) whose command can't be delivered are failed instead of left waiting forever. - A font that fails to load uses the default typeface (-1) instead of font 0, and is retried later if there was no activity yet. Missing exception checks added in the text renderer. - Method IDs and the storage callbacks are looked up once in JNI_OnLoad. - Unknown permissions are logged correctly. Also removes what was left of Moga support, the unused backbuffer format and a stray MessageBox macro, and tidies System_GetCameraDeviceList. Co-Authored-By: Claude Opus 5.5 (1M context) --- Common/File/AndroidStorage.cpp | 131 +++------ Common/File/AndroidStorage.h | 5 +- Common/Render/Text/draw_text_android.cpp | 55 ++-- Common/System/System.h | 1 - UI/SystemInfoScreen.cpp | 8 - android/jni/app-android.cpp | 270 +++++++----------- android/jni/app-android.h | 72 +++++ android/src/org/ppsspp/ppsspp/NativeApp.java | 2 +- .../ppsspp/ppsspp/NativeSurfaceManager.java | 10 +- 9 files changed, 251 insertions(+), 303 deletions(-) diff --git a/Common/File/AndroidStorage.cpp b/Common/File/AndroidStorage.cpp index 6ef29f656b..5c01ebac8e 100644 --- a/Common/File/AndroidStorage.cpp +++ b/Common/File/AndroidStorage.cpp @@ -25,7 +25,6 @@ static jmethodID contentUriGetFileInfo; static jmethodID contentUriFileExists; static jmethodID contentUriGetFreeStorageSpace; static jmethodID filePathGetFreeStorageSpace; -static jmethodID isExternalStoragePreservedLegacy; static jmethodID computeRecursiveDirectorySize; // A global ref owned by whoever calls Android_StorageSetActivity, who may replace it and delete the old @@ -39,13 +38,8 @@ void Android_StorageSetActivity(jobject nativeActivity) { g_nativeActivity = nativeActivity; } -// Only for the one call that doesn't pass the activity to Java. Anything that does must use -// AcquireActivity and check the ref it got - between a check here and the call, the UI thread -// can swap the activity out from under us. -static bool HasActivity() { - std::lock_guard guard(g_nativeActivityLock); - return g_nativeActivity != nullptr; -} +// Fixed for the life of the process (it's decided at install), so asked once at registration. +static bool g_externalStoragePreservedLegacy; // Returns a local ref, to be freed by the caller's JNILocalFrame. Can be null if we just lost the activity. static jobject AcquireActivity(JNIEnv *env) { @@ -53,18 +47,8 @@ static jobject AcquireActivity(JNIEnv *env) { return g_nativeActivity ? env->NewLocalRef(g_nativeActivity) : nullptr; } -// Returns true (and clears it) if the last Java call threw. ContentUri catches Exception, but an Error -// (NoSuchMethodError, OutOfMemoryError) gets through, and a pending exception makes the next JNI call abort. -// The call's return value is garbage then - for the int calls, 0 would read as SUCCESS. -static bool CheckException(JNIEnv *env, const char *func) { - if (!env->ExceptionCheck()) { - return false; - } - ERROR_LOG(Log::IO, "%s: Java exception", func); - env->ExceptionDescribe(); - env->ExceptionClear(); - return true; -} +// ContentUri catches Exception, but an Error (NoSuchMethodError, OutOfMemoryError) gets through, so every +// call is followed by CheckJavaException. For the int calls, the garbage result would read as SUCCESS. // For names that aren't URIs (those are percent-encoded already), which can contain anything. static jstring NewJavaString(JNIEnv *env, const std::string &str) { @@ -73,12 +57,8 @@ static jstring NewJavaString(JNIEnv *env, const std::string &str) { return env->NewStringUTF(modified.c_str()); } -void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj) { - if (g_classContentUri) { - // Already done. This gets called for each new activity, but the class and the method IDs stay - // valid, and other threads may be using them right now. - return; - } +void Android_RegisterStorageCallbacks(JNIEnv *env) { + _dbg_assert_(!g_classContentUri); jclass localClass = env->FindClass("org/ppsspp/ppsspp/ContentUri"); _dbg_assert_(localClass); @@ -106,35 +86,18 @@ void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj) { _dbg_assert_(contentUriGetFreeStorageSpace); filePathGetFreeStorageSpace = env->GetStaticMethodID(localClass, "filePathGetFreeStorageSpace", "(Landroid/app/Activity;Ljava/lang/String;)J"); _dbg_assert_(filePathGetFreeStorageSpace); - isExternalStoragePreservedLegacy = env->GetStaticMethodID(localClass, "isExternalStoragePreservedLegacy", "()Z"); // doesn't need an activity + jmethodID isExternalStoragePreservedLegacy = env->GetStaticMethodID(localClass, "isExternalStoragePreservedLegacy", "()Z"); // doesn't need an activity _dbg_assert_(isExternalStoragePreservedLegacy); computeRecursiveDirectorySize = env->GetStaticMethodID(localClass, "computeRecursiveDirectorySize", "(Landroid/app/Activity;Ljava/lang/String;)J"); _dbg_assert_(computeRecursiveDirectorySize); g_classContentUri = reinterpret_cast(env->NewGlobalRef(localClass)); env->DeleteLocalRef(localClass); // cleanup local ref -} -void Android_UnregisterStorageCallbacks(JNIEnv * env) { - if (g_classContentUri) { - env->DeleteGlobalRef(g_classContentUri); - g_classContentUri = nullptr; + g_externalStoragePreservedLegacy = env->CallStaticBooleanMethod(g_classContentUri, isExternalStoragePreservedLegacy); + if (CheckJavaException(env, __FUNCTION__)) { + g_externalStoragePreservedLegacy = false; } - Android_StorageSetActivity(nullptr); - openContentUri = nullptr; - listContentUriDir = nullptr; - contentUriCreateFile = nullptr; - contentUriCreateDirectory = nullptr; - contentUriCopyFile = nullptr; - contentUriMoveFile = nullptr; - contentUriRemoveFile = nullptr; - contentUriRenameFileTo = nullptr; - contentUriGetFileInfo = nullptr; - contentUriFileExists = nullptr; - contentUriGetFreeStorageSpace = nullptr; - filePathGetFreeStorageSpace = nullptr; - isExternalStoragePreservedLegacy = nullptr; - computeRecursiveDirectorySize = nullptr; } bool Android_IsContentUri(std::string_view filename) { @@ -172,7 +135,7 @@ int Android_OpenContentUriFd(std::string_view filename, Android_OpenContentUriMo jstring j_filename = env->NewStringUTF(fname.c_str()); jstring j_mode = env->NewStringUTF(modeStr); int fd = env->CallStaticIntMethod(g_classContentUri, openContentUri, activity, j_filename, j_mode); - if (CheckException(env, __FUNCTION__)) { + if (CheckJavaException(env, __FUNCTION__)) { return -1; } return fd; @@ -188,7 +151,7 @@ StorageError Android_CreateDirectory(const std::string &rootTreeUri, const std:: jstring paramRoot = env->NewStringUTF(rootTreeUri.c_str()); jstring paramDirName = NewJavaString(env, dirName); int result = env->CallStaticIntMethod(g_classContentUri, contentUriCreateDirectory, activity, paramRoot, paramDirName); - if (CheckException(env, __FUNCTION__)) { + if (CheckJavaException(env, __FUNCTION__)) { return StorageError::UNKNOWN; } return StorageErrorFromInt(result); @@ -204,7 +167,7 @@ StorageError Android_CreateFile(const std::string &parentTreeUri, const std::str jstring paramRoot = env->NewStringUTF(parentTreeUri.c_str()); jstring paramFileName = NewJavaString(env, fileName); int result = env->CallStaticIntMethod(g_classContentUri, contentUriCreateFile, activity, paramRoot, paramFileName); - if (CheckException(env, __FUNCTION__)) { + if (CheckJavaException(env, __FUNCTION__)) { return StorageError::UNKNOWN; } return StorageErrorFromInt(result); @@ -220,7 +183,7 @@ StorageError Android_CopyFile(const std::string &fileUri, const std::string &des jstring paramFileName = env->NewStringUTF(fileUri.c_str()); jstring paramDestParentUri = env->NewStringUTF(destParentUri.c_str()); int result = env->CallStaticIntMethod(g_classContentUri, contentUriCopyFile, activity, paramFileName, paramDestParentUri); - if (CheckException(env, __FUNCTION__)) { + if (CheckJavaException(env, __FUNCTION__)) { return StorageError::UNKNOWN; } return StorageErrorFromInt(result); @@ -237,7 +200,7 @@ StorageError Android_MoveFile(const std::string &fileUri, const std::string &src jstring paramSrcParentUri = env->NewStringUTF(srcParentUri.c_str()); jstring paramDestParentUri = env->NewStringUTF(destParentUri.c_str()); int result = env->CallStaticIntMethod(g_classContentUri, contentUriMoveFile, activity, paramFileName, paramSrcParentUri, paramDestParentUri); - if (CheckException(env, __FUNCTION__)) { + if (CheckJavaException(env, __FUNCTION__)) { return StorageError::UNKNOWN; } return StorageErrorFromInt(result); @@ -252,7 +215,7 @@ StorageError Android_RemoveFile(const std::string &fileUri) { } jstring paramFileName = env->NewStringUTF(fileUri.c_str()); int result = env->CallStaticIntMethod(g_classContentUri, contentUriRemoveFile, activity, paramFileName); - if (CheckException(env, __FUNCTION__)) { + if (CheckJavaException(env, __FUNCTION__)) { return StorageError::UNKNOWN; } return StorageErrorFromInt(result); @@ -268,7 +231,7 @@ StorageError Android_RenameFileTo(const std::string &fileUri, const std::string jstring paramFileUri = env->NewStringUTF(fileUri.c_str()); jstring paramNewName = NewJavaString(env, newName); int result = env->CallStaticIntMethod(g_classContentUri, contentUriRenameFileTo, activity, paramFileUri, paramNewName); - if (CheckException(env, __FUNCTION__)) { + if (CheckJavaException(env, __FUNCTION__)) { return StorageError::UNKNOWN; } return StorageErrorFromInt(result); @@ -320,15 +283,10 @@ bool Android_GetFileInfo(const std::string &fileUri, File::FileInfo *fileInfo) { jstring paramFileUri = env->NewStringUTF(fileUri.c_str()); jstring str = (jstring)env->CallStaticObjectMethod(g_classContentUri, contentUriGetFileInfo, activity, paramFileUri); - if (CheckException(env, __FUNCTION__) || !str) { + if (CheckJavaException(env, __FUNCTION__) || !str) { return false; } - const char *charArray = env->GetStringUTFChars(str, 0); - if (!charArray) { - return false; - } - bool retval = ParseFileInfo(charArray, fileInfo); - env->ReleaseStringUTFChars(str, charArray); + bool retval = ParseFileInfo(GetJavaString(env, str), fileInfo); fileInfo->fullName = Path(fileUri); return retval && fileInfo->exists; } @@ -342,7 +300,7 @@ bool Android_FileExists(const std::string &fileUri) { } jstring paramFileUri = env->NewStringUTF(fileUri.c_str()); bool exists = env->CallStaticBooleanMethod(g_classContentUri, contentUriFileExists, activity, paramFileUri); - if (CheckException(env, __FUNCTION__)) { + if (CheckJavaException(env, __FUNCTION__)) { return false; } return exists; @@ -363,7 +321,7 @@ std::vector Android_ListContentUri(const std::string &uri, const jstring param = env->NewStringUTF(uri.c_str()); jstring filenamePrefix = prefix.empty() ? nullptr : NewJavaString(env, prefix); jobject retval = env->CallStaticObjectMethod(g_classContentUri, listContentUriDir, activity, param, filenamePrefix); - if (CheckException(env, __FUNCTION__)) { + if (CheckJavaException(env, __FUNCTION__)) { *exists = false; return {}; } @@ -380,20 +338,15 @@ std::vector Android_ListContentUri(const std::string &uri, const if (!str) { continue; } - const char *charArray = env->GetStringUTFChars(str, 0); - if (charArray) { // paranoia - std::string line = charArray; - File::FileInfo info{}; - if (line == "X") { - // Indicates an exception thrown, uri doesn't exist. - *exists = false; - } else if (ParseFileInfo(line, &info)) { - // We can just reconstruct the URI. - info.fullName = Path(uri) / info.name; - // INFO_LOG(Log::IO, "%s", info.name.c_str()); - items.push_back(info); - } - env->ReleaseStringUTFChars(str, charArray); + std::string line = GetJavaString(env, str); + File::FileInfo info{}; + if (line == "X") { + // Indicates an exception thrown, uri doesn't exist. + *exists = false; + } else if (ParseFileInfo(line, &info)) { + // We can just reconstruct the URI. + info.fullName = Path(uri) / info.name; + items.push_back(info); } // Don't leave these to the frame, there can be thousands. env->DeleteLocalRef(str); @@ -412,12 +365,12 @@ int64_t Android_GetFreeSpaceByContentUri(const std::string &uri) { JNILocalFrame frame(env); jobject activity = AcquireActivity(env); if (!activity) { - return false; + return -1; } jstring param = env->NewStringUTF(uri.c_str()); int64_t space = env->CallStaticLongMethod(g_classContentUri, contentUriGetFreeStorageSpace, activity, param); - if (CheckException(env, __FUNCTION__)) { + if (CheckJavaException(env, __FUNCTION__)) { return -1; } return space; @@ -429,7 +382,7 @@ int64_t Android_GetFreeSpaceByFilePath(const std::string &filePath) { JNILocalFrame frame(env); jobject activity = AcquireActivity(env); if (!activity) { - return false; + return -1; } if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) < 26) { @@ -439,7 +392,7 @@ int64_t Android_GetFreeSpaceByFilePath(const std::string &filePath) { jstring param = env->NewStringUTF(filePath.c_str()); int64_t space = env->CallStaticLongMethod(g_classContentUri, filePathGetFreeStorageSpace, activity, param); - if (CheckException(env, __FUNCTION__)) { + if (CheckJavaException(env, __FUNCTION__)) { return -1; } return space; @@ -450,14 +403,14 @@ int64_t Android_ComputeRecursiveDirectorySize(const std::string &uri) { JNILocalFrame frame(env); jobject activity = AcquireActivity(env); if (!activity) { - return false; + return 0; } jstring param = env->NewStringUTF(uri.c_str()); double start = time_now_d(); int64_t size = env->CallStaticLongMethod(g_classContentUri, computeRecursiveDirectorySize, activity, param); - if (CheckException(env, __FUNCTION__)) { + if (CheckJavaException(env, __FUNCTION__)) { return 0; } double elapsed = time_now_d() - start; @@ -467,17 +420,7 @@ int64_t Android_ComputeRecursiveDirectorySize(const std::string &uri) { } bool Android_IsExternalStoragePreservedLegacy() { - if (!HasActivity()) { - return false; - } - auto env = getEnv(); - JNILocalFrame frame(env); - // Note: No activity param - bool preserved = env->CallStaticBooleanMethod(g_classContentUri, isExternalStoragePreservedLegacy); - if (CheckException(env, __FUNCTION__)) { - return false; - } - return preserved; + return g_externalStoragePreservedLegacy; } const char *Android_ErrorToString(StorageError error) { diff --git a/Common/File/AndroidStorage.h b/Common/File/AndroidStorage.h index 54ac51d37b..8f6fea820c 100644 --- a/Common/File/AndroidStorage.h +++ b/Common/File/AndroidStorage.h @@ -64,9 +64,8 @@ const char *Android_ErrorToString(StorageError error); // TODO: prefix doesn't do anything yet. std::vector Android_ListContentUri(const std::string &uri, const std::string &prefix, bool *exists); -// Don't need to add these below, they're only used from app-android.cpp. -void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj); -void Android_UnregisterStorageCallbacks(JNIEnv * env); +// Don't need to add this below, it's only used from app-android.cpp. Call once, from JNI_OnLoad. +void Android_RegisterStorageCallbacks(JNIEnv *env); #else diff --git a/Common/Render/Text/draw_text_android.cpp b/Common/Render/Text/draw_text_android.cpp index 5cbb6f8d4e..6623e0bd19 100644 --- a/Common/Render/Text/draw_text_android.cpp +++ b/Common/Render/Text/draw_text_android.cpp @@ -68,21 +68,32 @@ void TextDrawerAndroid::SetOrCreateFont(const FontStyle &style) { } std::string filename = GetFilenameForFontStyle(style) + ".ttf"; + // TextRenderer has no font -1, so it draws that with the default typeface. + int fontId = -1; auto fontIter = allocatedFonts_.find(filename); - if (fontIter == allocatedFonts_.end()) { + if (fontIter != allocatedFonts_.end()) { + fontId = fontIter->second; + } else { auto env = getEnv(); JNILocalFrame frame(env); jobject activity = Android_GetActivity(env); - jstring jstr = env->NewStringUTF(filename.c_str()); - int fontId = env->CallStaticIntMethod(cls_textRenderer, method_allocFont, activity, jstr); - - if (fontId >= 0) { + if (activity) { + jstring jstr = env->NewStringUTF(filename.c_str()); + fontId = env->CallStaticIntMethod(cls_textRenderer, method_allocFont, activity, jstr); + if (CheckJavaException(env, __FUNCTION__) || fontId < 0) { + ERROR_LOG(Log::G3D, "Failed to load font '%s', using the default", filename.c_str()); + fontId = -1; + } + // Remembered even if it failed, that won't change. allocatedFonts_[filename] = fontId; + } else { + // Not remembered, so it's tried again when the fonts are next recreated. + WARN_LOG(Log::G3D, "No activity to load font '%s' with, using the default", filename.c_str()); } } AndroidFontEntry entry{}; - entry.font = allocatedFonts_[filename]; + entry.font = fontId; entry.size = 1.25f * style.sizePts / dpiScale_; // Not sure why this formula works. // Just chose a factor that looks good, don't know what unit size is in anyway. @@ -106,6 +117,11 @@ void TextDrawerAndroid::MeasureStringInternal(std::string_view str, float *w, fl jstring jstr = env->NewStringUTF(text.c_str()); uint32_t size = env->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, iter->second.font, iter->second.size); env->DeleteLocalRef(jstr); + if (CheckJavaException(env, __FUNCTION__)) { + *w = 1.0f; + *h = 1.0f; + return; + } *w = (float)(size >> 16); *h = (float)(size & 0xFFFF); @@ -130,6 +146,10 @@ bool TextDrawerAndroid::DrawStringBitmap(std::vector &bitmapData, TextS jstring jstr = env->NewStringUTF(text.c_str()); uint32_t textSize = env->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, iter->second.font, iter->second.size); + if (CheckJavaException(env, __FUNCTION__)) { + env->DeleteLocalRef(jstr); + return false; + } int imageWidth = (short)(textSize >> 16); int imageHeight = (short)(textSize & 0xFFFF); if (imageWidth <= 0) @@ -141,12 +161,22 @@ bool TextDrawerAndroid::DrawStringBitmap(std::vector &bitmapData, TextS // TODO: Handle bold/italic jintArray imageData = (jintArray)env->CallStaticObjectMethod(cls_textRenderer, method_renderText, jstr, iter->second.font, iter->second.size, imageWidth, imageHeight); env->DeleteLocalRef(jstr); - if (!imageData) { - // Most likely an OutOfMemoryError allocating the bitmap. - env->ExceptionClear(); + // Most likely an OutOfMemoryError allocating the bitmap. + if (CheckJavaException(env, __FUNCTION__) || !imageData) { ERROR_LOG(Log::G3D, "TextRenderer failed to render text"); return false; } + if (env->GetArrayLength(imageData) != imageWidth * imageHeight) { + ERROR_LOG(Log::G3D, "TextRenderer returned bad image size"); + env->DeleteLocalRef(imageData); + return false; + } + jint *jimage = env->GetIntArrayElements(imageData, nullptr); + if (!jimage) { + env->ExceptionClear(); // OutOfMemoryError + env->DeleteLocalRef(imageData); + return false; + } entry.texture = nullptr; entry.bmWidth = imageWidth; @@ -155,13 +185,6 @@ bool TextDrawerAndroid::DrawStringBitmap(std::vector &bitmapData, TextS entry.height = imageHeight; entry.lastUsedFrame = frameCount_; - jint *jimage = env->GetIntArrayElements(imageData, nullptr); - if (env->GetArrayLength(imageData) != imageWidth * imageHeight) { - ERROR_LOG(Log::G3D, "TextRenderer returned bad image size"); - env->ReleaseIntArrayElements(imageData, jimage, JNI_ABORT); - env->DeleteLocalRef(imageData); - return false; - } if (texFormat == Draw::DataFormat::B4G4R4A4_UNORM_PACK16 || texFormat == Draw::DataFormat::R4G4B4A4_UNORM_PACK16) { bitmapData.resize(entry.bmWidth * entry.bmHeight * sizeof(uint16_t)); uint16_t *bitmapData16 = (uint16_t *)&bitmapData[0]; diff --git a/Common/System/System.h b/Common/System/System.h index 1a9804457a..4609088e75 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -185,7 +185,6 @@ enum SystemProperty { SYSPROP_DISPLAY_LOGICAL_DPI, SYSPROP_DISPLAY_DPI, SYSPROP_DISPLAY_COUNT, - SYSPROP_MOGA_VERSION, // Float only: SYSPROP_DISPLAY_SAFE_INSET_LEFT, diff --git a/UI/SystemInfoScreen.cpp b/UI/SystemInfoScreen.cpp index cd0264322c..4114832bf8 100644 --- a/UI/SystemInfoScreen.cpp +++ b/UI/SystemInfoScreen.cpp @@ -302,14 +302,6 @@ void SystemInfoScreen::CreateDeviceInfoTab(UI::LinearLayout *deviceSpecs) { } versionInfo->Add(new InfoItem(si->T("Shading Language"), draw->GetInfoString(InfoField::SHADELANGVERSION))); -#if PPSSPP_PLATFORM(ANDROID) - std::string moga = System_GetProperty(SYSPROP_MOGA_VERSION); - if (moga.empty()) { - moga = si->T("(none detected)"); - } - versionInfo->Add(new InfoItem("Moga", moga)); -#endif - if (gstate_c.GetUseFlags()) { // We're in-game, and can determine these. // TODO: Call a static version of GPUCommon::CheckGPUFeatures() and derive them here directly. diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 8bd62c41ea..3bb6f7f1a0 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -111,9 +111,11 @@ AndroidAudioState *g_audioState; struct FrameCommand { FrameCommand() = default; - FrameCommand(std::string_view cmd, std::string_view param) : command(cmd), params(param) {} + FrameCommand(std::string_view cmd, std::string_view param, int reqId) : command(cmd), params(param), requestId(reqId) {} std::string command; std::string params; + // The request waiting for an answer to this command, or -1. Failed if the command can't be delivered. + int requestId = -1; }; static std::mutex frameCommandLock; @@ -121,7 +123,6 @@ static std::vector g_frameCommands; static std::string systemName; static std::string langRegion; -static std::string mogaVersion; static std::string boardName; std::string g_externalDir; // Original external dir (root of Android storage). @@ -143,8 +144,6 @@ static int display_dpi; static float display_scale_x; // Scale factor due to backbuffer scaling static float display_scale_y; -static int backbuffer_format; // Android PixelFormat enum - static int desiredBackbufferSizeX; static int desiredBackbufferSizeY; @@ -154,16 +153,17 @@ extern JavaVM *gJvm; static jobject gClassLoader; static jmethodID gFindClassMethod; +// PpssppActivity methods, looked up in JNI_OnLoad. +static jmethodID postCommand; +static jmethodID getDebugString; +static jmethodID getNativeCrashHistory; + static float g_safeInsetLeft = 0.0; static float g_safeInsetRight = 0.0; static float g_safeInsetTop = 0.0; static float g_safeInsetBottom = 0.0; static bool g_hasCameraCutout = false; -static jmethodID postCommand; -static jmethodID getDebugString; -static jmethodID getNativeCrashHistory; - // Global ref. Written on the UI thread under g_activityLock. Other threads go through Android_GetActivity. static jobject ppssppActivity; static std::mutex g_activityLock; @@ -181,15 +181,24 @@ static std::string g_installerName; static std::mutex permissionsLock; static std::map permissions; -static void SetPermissionStatus(SystemPermission permission, PermissionStatus status) { +// The name is the one System_AskForPermission sends. +static void SetPermissionStatusFromJava(std::string_view name, PermissionStatus status) { + SystemPermission permission; + if (name == "storage") { + permission = SYSTEM_PERMISSION_STORAGE; + } else if (name == "local_network") { + permission = SYSTEM_PERMISSION_LOCAL_NETWORK; + } else { + WARN_LOG(Log::System, "Status %d for unknown permission '%.*s'", (int)status, STR_VIEW(name)); + return; + } + INFO_LOG(Log::System, "Permission '%.*s': status %d", STR_VIEW(name), (int)status); std::lock_guard guard(permissionsLock); permissions[permission] = status; } static GraphicsContext *graphicsContext; -#define MessageBox(a, b, c, d) __android_log_print(ANDROID_LOG_INFO, APP_NAME, "%s %s", (b), (c)); - #if PPSSPP_ARCH(ARMV7) // Old Android workaround extern "C" { @@ -248,15 +257,20 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *pjvm, void *reserved) { INFO_LOG(Log::System, "JNI_OnLoad"); gJvm = pjvm; // cache the JavaVM pointer auto env = getEnv(); - //replace with one of your classes in the line below - auto randomClass = env->FindClass("org/ppsspp/ppsspp/PpssppActivity"); - jclass classClass = env->GetObjectClass(randomClass); - auto classLoaderClass = env->FindClass("java/lang/ClassLoader"); - auto getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader", - "()Ljava/lang/ClassLoader;"); - gClassLoader = env->NewGlobalRef(env->CallObjectMethod(randomClass, getClassLoaderMethod)); - gFindClassMethod = env->GetMethodID(classLoaderClass, "findClass", - "(Ljava/lang/String;)Ljava/lang/Class;"); + // Method IDs stay valid for as long as the class is loaded, which is the life of the process. + jclass activityClass = env->FindClass("org/ppsspp/ppsspp/PpssppActivity"); + jclass classClass = env->GetObjectClass(activityClass); + jclass classLoaderClass = env->FindClass("java/lang/ClassLoader"); + jmethodID getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader", "()Ljava/lang/ClassLoader;"); + gClassLoader = env->NewGlobalRef(env->CallObjectMethod(activityClass, getClassLoaderMethod)); + gFindClassMethod = env->GetMethodID(classLoaderClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + + postCommand = env->GetMethodID(activityClass, "postCommand", "(Ljava/lang/String;Ljava/lang/String;)V"); + getDebugString = env->GetMethodID(activityClass, "getDebugString", "(Ljava/lang/String;)Ljava/lang/String;"); + getNativeCrashHistory = env->GetMethodID(activityClass, "getNativeCrashHistory", "(I)Ljava/util/ArrayList;"); + _dbg_assert_(postCommand && getDebugString && getNativeCrashHistory); + + Android_RegisterStorageCallbacks(env); RegisterAttachDetach(&Android_AttachThreadToJNI, &Android_DetachThreadFromJNI); @@ -269,9 +283,9 @@ jobject Android_GetActivity(JNIEnv *env) { return ppssppActivity ? env->NewLocalRef(ppssppActivity) : nullptr; } -static void PushCommand(std::string_view cmd, std::string_view param) { +static void PushCommand(std::string_view cmd, std::string_view param, int requestId = -1) { std::lock_guard guard(frameCommandLock); - g_frameCommands.emplace_back(std::string(cmd), std::string(param)); + g_frameCommands.emplace_back(cmd, param, requestId); } // Android implementation of callbacks to the Java part of the app @@ -305,8 +319,6 @@ std::string System_GetProperty(SystemProperty prop) { return systemName; case SYSPROP_LANGREGION: // "en_US" return langRegion; - case SYSPROP_MOGA_VERSION: - return mogaVersion; case SYSPROP_BOARDNAME: return boardName; case SYSPROP_BUILD_VERSION: @@ -479,76 +491,27 @@ std::string Android_GetInputDeviceDebugString() { jstring jparam = env->NewStringUTF("InputDevice"); jstring jstr = (jstring)env->CallObjectMethod(activity, getDebugString, jparam); - if (!jstr) { + if (CheckJavaException(env, __FUNCTION__) || !jstr) { return "(N/A)"; } - - const char *charArray = env->GetStringUTFChars(jstr, nullptr); - std::string retVal = charArray ? charArray : "(N/A)"; - if (charArray) { - env->ReleaseStringUTFChars(jstr, charArray); - } - return retVal; + return GetJavaString(env, jstr); } std::vector Android_GetNativeCrashHistory(int maxEntries) { - std::vector crashHistory; if (!getNativeCrashHistory) { - return crashHistory; + return {}; } auto env = getEnv(); JNILocalFrame frame(env); jobject activity = Android_GetActivity(env); if (!activity) { - return crashHistory; + return {}; } jobject jlist = env->CallObjectMethod(activity, getNativeCrashHistory, maxEntries); - if (!jlist) { - return crashHistory; + if (CheckJavaException(env, __FUNCTION__)) { + return {}; } - jclass arrayListClass = env->GetObjectClass(jlist); - jmethodID sizeMethod = env->GetMethodID(arrayListClass, "size", "()I"); - jmethodID getMethod = env->GetMethodID(arrayListClass, "get", "(I)Ljava/lang/Object;"); - jint listSize = env->CallIntMethod(jlist, sizeMethod); - for (jint i = 0; i < listSize; i++) { - jstring jstr = (jstring)env->CallObjectMethod(jlist, getMethod, i); - if (jstr) { - const char *charArray = env->GetStringUTFChars(jstr, nullptr); - crashHistory.emplace_back(charArray); - env->ReleaseStringUTFChars(jstr, charArray); - env->DeleteLocalRef(jstr); - } - } - env->DeleteLocalRef(arrayListClass); - env->DeleteLocalRef(jlist); - return crashHistory; -} - -// GetStringUTFChars returns Java's modified UTF-8, where characters outside the BMP (emoji) become two 3-byte -// surrogates, so we go through UTF-16. -std::string GetJavaString(JNIEnv *env, jstring jstr) { - if (!jstr) - return ""; - const jsize len = env->GetStringLength(jstr); - const jchar *chars = env->GetStringChars(jstr, nullptr); - if (!chars) - return ""; - std::string cpp_string; - cpp_string.reserve(len); - char buf[4]; - for (jsize i = 0; i < len; i++) { - uint32_t c = chars[i]; - if (c >= 0xD800 && c < 0xDC00 && i + 1 < len && chars[i + 1] >= 0xDC00 && chars[i + 1] < 0xE000) { - c = 0x10000 + ((c - 0xD800) << 10) + (chars[i + 1] - 0xDC00); - i++; - } else if (c >= 0xD800 && c < 0xE000) { - // Unpaired surrogate. - c = 0xFFFD; - } - cpp_string.append(buf, UTF8::encode(buf, c)); - } - env->ReleaseStringChars(jstr, chars); - return cpp_string; + return GetJavaStringList(env, jlist); } extern "C" void Java_org_ppsspp_ppsspp_PpssppActivity_registerCallbacks(JNIEnv *env, jobject obj) { @@ -559,15 +522,6 @@ extern "C" void Java_org_ppsspp_ppsspp_PpssppActivity_registerCallbacks(JNIEnv * oldActivity = ppssppActivity; ppssppActivity = newActivity; } - postCommand = env->GetMethodID(env->GetObjectClass(obj), "postCommand", "(Ljava/lang/String;Ljava/lang/String;)V"); - getDebugString = env->GetMethodID(env->GetObjectClass(obj), "getDebugString", "(Ljava/lang/String;)Ljava/lang/String;"); - getNativeCrashHistory = env->GetMethodID(env->GetObjectClass(obj), "getNativeCrashHistory", "(I)Ljava/util/ArrayList;"); - - _dbg_assert_(postCommand); - _dbg_assert_(getDebugString); - // It's OK if getNativeCrashHistory is missing. - - Android_RegisterStorageCallbacks(env, obj); Android_StorageSetActivity(newActivity); // An activity that gets recreated (rotation, resize) never unregisters, so this is where we let go @@ -902,7 +856,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { INFO_LOG(Log::System, "NativeApp.shutdown() -- end"); } -extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_backbufferResize(JNIEnv *, jclass, jint pixel_xres, jint pixel_yres, jint format, jint rotation) { +extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_backbufferResize(JNIEnv *, jclass, jint pixel_xres, jint pixel_yres, jint rotation) { INFO_LOG(Log::System, "NativeApp.backbufferResize(%d x %d, rotation %d)", pixel_xres, pixel_yres, rotation); // The display's Surface.ROTATION_*. A 180 degree flip changes this but not the size, and the swapchain @@ -917,8 +871,6 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_backbufferResize(JNIEnv int old_dp_h = g_display.dp_yres; // pixel_*res is the backbuffer resolution. - backbuffer_format = format; - if (IsVREnabled()) { GetVRResolutionPerEye(&pixel_xres, &pixel_yres); } @@ -989,11 +941,11 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string { // The flags go before the strings, so the free text comes last. std::string serialized = StringFromFormat("%d:@:%d:@:%s:@:%s", requestId, param3 ? 1 : 0, param1.c_str(), param2.c_str()); - PushCommand("inputbox", serialized); + PushCommand("inputbox", serialized, requestId); return true; } case SystemRequestType::BROWSE_FOR_IMAGE: - PushCommand("browse_image", StringFromFormat("%d", requestId)); + PushCommand("browse_image", StringFromFormat("%d", requestId), requestId); return true; case SystemRequestType::BROWSE_FOR_FILE: { @@ -1001,19 +953,19 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string std::string params = StringFromFormat("%d", requestId); switch (fileType) { case BrowseFileType::SOUND_EFFECT: - PushCommand("browse_file_audio", params); + PushCommand("browse_file_audio", params, requestId); break; case BrowseFileType::ZIP: - PushCommand("browse_file_zip", params); + PushCommand("browse_file_zip", params, requestId); break; default: - PushCommand("browse_file", params); + PushCommand("browse_file", params, requestId); break; } return true; } case SystemRequestType::BROWSE_FOR_FOLDER: - PushCommand("browse_folder", StringFromFormat("%d", requestId)); + PushCommand("browse_folder", StringFromFormat("%d", requestId), requestId); return true; case SystemRequestType::CAMERA_COMMAND: @@ -1280,36 +1232,12 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_sendMessageFromJava(JNI static InputDeviceID nextInputDeviceID = DEVICE_ID_ANY; // Some messages are caught by app-android. TODO: Should be all. - if (msg == "moga") { - mogaVersion = prm; - } else if (msg == "permission_pending") { - if (prm == "storage") { - INFO_LOG(Log::System, "STORAGE PERMISSION: PENDING"); - SetPermissionStatus(SYSTEM_PERMISSION_STORAGE, PERMISSION_STATUS_PENDING); - } else if (prm == "local_network") { - INFO_LOG(Log::System, "LOCAL NETWORK PERMISSION: PENDING"); - SetPermissionStatus(SYSTEM_PERMISSION_LOCAL_NETWORK, PERMISSION_STATUS_PENDING); - } + if (msg == "permission_pending") { + SetPermissionStatusFromJava(prm, PERMISSION_STATUS_PENDING); } else if (msg == "permission_denied") { - if (prm == "storage") { - INFO_LOG(Log::System, "STORAGE PERMISSION: DENIED"); - SetPermissionStatus(SYSTEM_PERMISSION_STORAGE, PERMISSION_STATUS_DENIED); - } else if (prm == "local_network") { - INFO_LOG(Log::System, "LOCAL NETWORK PERMISSION: DENIED"); - SetPermissionStatus(SYSTEM_PERMISSION_LOCAL_NETWORK, PERMISSION_STATUS_DENIED); - } else { - WARN_LOG(Log::System, "UNKNOWN PERMISSION GRANTED: %s", prm.c_str()); - } + SetPermissionStatusFromJava(prm, PERMISSION_STATUS_DENIED); } else if (msg == "permission_granted") { - if (prm == "storage") { - INFO_LOG(Log::System, "STORAGE PERMISSION: GRANTED"); - SetPermissionStatus(SYSTEM_PERMISSION_STORAGE, PERMISSION_STATUS_GRANTED); - } else if (prm == "local_network") { - INFO_LOG(Log::System, "LOCAL NETWORK PERMISSION: GRANTED"); - SetPermissionStatus(SYSTEM_PERMISSION_LOCAL_NETWORK, PERMISSION_STATUS_GRANTED); - } else { - WARN_LOG(Log::System, "UNKNOWN PERMISSION GRANTED: %s", prm.c_str()); - } + SetPermissionStatusFromJava(prm, PERMISSION_STATUS_GRANTED); System_PostUIMessage(UIMessage::PERMISSION_GRANTED, prm); } else if (msg == "sustained_perf_supported") { sustainedPerfSupported = true; @@ -1468,29 +1396,18 @@ extern "C" jint JNICALL Java_org_ppsspp_ppsspp_NativeApp_getDisplayFramerateMode } std::vector System_GetCameraDeviceList() { - JNILocalFrame frame(getEnv()); + JNIEnv *env = getEnv(); + JNILocalFrame frame(env); jclass cameraClass = findClass("org/ppsspp/ppsspp/CameraHelper"); - jmethodID deviceListMethod = getEnv()->GetStaticMethodID(cameraClass, "getDeviceList", "()Ljava/util/ArrayList;"); - jobject deviceListObject = getEnv()->CallStaticObjectMethod(cameraClass, deviceListMethod); - jclass arrayListClass = getEnv()->FindClass("java/util/ArrayList"); - jmethodID arrayListSize = getEnv()->GetMethodID(arrayListClass, "size", "()I"); - jmethodID arrayListGet = getEnv()->GetMethodID(arrayListClass, "get", "(I)Ljava/lang/Object;"); - - jint arrayListObjectLen = getEnv()->CallIntMethod(deviceListObject, arrayListSize); - std::vector deviceListVector; - - for (int i = 0; i < arrayListObjectLen; i++) { - jstring dev = static_cast(getEnv()->CallObjectMethod(deviceListObject, arrayListGet, i)); - const char *cdev = getEnv()->GetStringUTFChars(dev, nullptr); - if (!cdev) { - getEnv()->DeleteLocalRef(dev); - continue; - } - deviceListVector.emplace_back(cdev); - getEnv()->ReleaseStringUTFChars(dev, cdev); - getEnv()->DeleteLocalRef(dev); + if (CheckJavaException(env, __FUNCTION__) || !cameraClass) { + return {}; } - return deviceListVector; + jmethodID deviceListMethod = env->GetStaticMethodID(cameraClass, "getDeviceList", "()Ljava/util/ArrayList;"); + jobject deviceList = env->CallStaticObjectMethod(cameraClass, deviceListMethod); + if (CheckJavaException(env, __FUNCTION__)) { + return {}; + } + return GetJavaStringList(env, deviceList); } extern "C" jint Java_org_ppsspp_ppsspp_NativeApp_getSelectedCamera(JNIEnv *, jclass) { @@ -1520,38 +1437,45 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_pushCameraImageAndroid( static void ProcessFrameCommands() { JNIEnv *env = getEnv(); + JNILocalFrame frame(env); jobject activity = Android_GetActivity(env); std::vector frameCommands; { std::lock_guard guard(frameCommandLock); - if (!activity) { - if (!g_frameCommands.empty()) { - ERROR_LOG(Log::System, "No activity, clearing commands"); - } - } else { - frameCommands = std::move(g_frameCommands); - } + frameCommands = std::move(g_frameCommands); g_frameCommands.clear(); } - - if (!frameCommands.empty()) { - INFO_LOG(Log::System, "Processing %zu frame commands", frameCommands.size()); - for (const FrameCommand &frameCmd : frameCommands) { - // Not logging the params, an inputbox one can contain a password. - DEBUG_LOG(Log::System, "frameCommand '%s'", frameCmd.command.c_str()); - - // The params can contain any text, like a title the game gave its OSK. - std::string params; - ConvertUTF8ToJavaModifiedUTF8(¶ms, frameCmd.params); - jstring cmd = env->NewStringUTF(frameCmd.command.c_str()); - jstring param = env->NewStringUTF(params.c_str()); - env->CallVoidMethod(activity, postCommand, cmd, param); - env->DeleteLocalRef(cmd); - env->DeleteLocalRef(param); - } + if (frameCommands.empty()) { + return; } - if (activity) { - env->DeleteLocalRef(activity); + + if (!activity) { + ERROR_LOG(Log::System, "No activity, dropping %zu commands", frameCommands.size()); + for (const FrameCommand &frameCmd : frameCommands) { + // Whoever made the request is waiting for an answer. + if (frameCmd.requestId >= 0) { + g_requestManager.PostSystemFailure(frameCmd.requestId); + } + } + return; + } + + INFO_LOG(Log::System, "Processing %zu frame commands", frameCommands.size()); + for (const FrameCommand &frameCmd : frameCommands) { + // Not logging the params, an inputbox one can contain a password. + DEBUG_LOG(Log::System, "frameCommand '%s'", frameCmd.command.c_str()); + + // The params can contain any text, like a title the game gave its OSK. + std::string params; + ConvertUTF8ToJavaModifiedUTF8(¶ms, frameCmd.params); + jstring cmd = env->NewStringUTF(frameCmd.command.c_str()); + jstring param = env->NewStringUTF(params.c_str()); + env->CallVoidMethod(activity, postCommand, cmd, param); + if (CheckJavaException(env, __FUNCTION__) && frameCmd.requestId >= 0) { + g_requestManager.PostSystemFailure(frameCmd.requestId); + } + env->DeleteLocalRef(cmd); + env->DeleteLocalRef(param); } } @@ -1680,7 +1604,6 @@ Java_org_ppsspp_ppsspp_ShortcutActivity_queryGameInfo(JNIEnv * env, jclass, jobj g_logManager.SetAllLogLevels(LogLevel::LDEBUG); activityRef = env->NewGlobalRef(activity); Android_StorageSetActivity(activityRef); - Android_RegisterStorageCallbacks(env, activityRef); INFO_LOG(Log::System, "No thread manager - initializing one"); } @@ -1728,7 +1651,6 @@ Java_org_ppsspp_ppsspp_ShortcutActivity_queryGameInfo(JNIEnv * env, jclass, jobj if (teardownThreadManager) { g_logManager.SetOutputsEnabled((LogOutput)0); - Android_UnregisterStorageCallbacks(env); Android_StorageSetActivity(nullptr); g_threadManager.Teardown(); } diff --git a/android/jni/app-android.h b/android/jni/app-android.h index e1af557b13..5236849a32 100644 --- a/android/jni/app-android.h +++ b/android/jni/app-android.h @@ -18,10 +18,82 @@ std::vector Android_GetNativeCrashHistory(int maxEntries); #if !defined(__LIBRETRO__) #include +#include + +#include "Common/Data/Encoding/Utf8.h" +#include "Common/Log.h" jclass findClass(const char* name); JNIEnv* getEnv(); +// GetStringUTFChars returns Java's modified UTF-8, where characters outside the BMP (emoji) become two 3-byte +// surrogates, so we go through UTF-16. +inline std::string GetJavaString(JNIEnv *env, jstring jstr) { + if (!jstr) { + return ""; + } + const jsize len = env->GetStringLength(jstr); + const jchar *chars = env->GetStringChars(jstr, nullptr); + if (!chars) { + return ""; + } + std::string cpp_string; + cpp_string.reserve(len); + char buf[4]; + for (jsize i = 0; i < len; i++) { + uint32_t c = chars[i]; + if (c >= 0xD800 && c < 0xDC00 && i + 1 < len && chars[i + 1] >= 0xDC00 && chars[i + 1] < 0xE000) { + c = 0x10000 + ((c - 0xD800) << 10) + (chars[i + 1] - 0xDC00); + i++; + } else if (c >= 0xD800 && c < 0xE000) { + // Unpaired surrogate. + c = 0xFFFD; + } + cpp_string.append(buf, UTF8::encode(buf, c)); + } + env->ReleaseStringChars(jstr, chars); + return cpp_string; +} + +// Returns true (and clears it) if the last Java call threw. A pending exception makes the next JNI call abort, +// and the call's return value is garbage. +inline bool CheckJavaException(JNIEnv *env, const char *func) { + if (!env->ExceptionCheck()) { + return false; + } + ERROR_LOG(Log::System, "%s: Java exception", func); + env->ExceptionDescribe(); + env->ExceptionClear(); + return true; +} + +// Reads a java.util.List of strings. Null entries are skipped. +inline std::vector GetJavaStringList(JNIEnv *env, jobject jlist) { + std::vector result; + if (!jlist) { + return result; + } + jclass listClass = env->GetObjectClass(jlist); + jmethodID sizeMethod = env->GetMethodID(listClass, "size", "()I"); + jmethodID getMethod = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;"); + env->DeleteLocalRef(listClass); + jint size = env->CallIntMethod(jlist, sizeMethod); + if (CheckJavaException(env, __FUNCTION__)) { + return result; + } + for (jint i = 0; i < size; i++) { + jstring jstr = (jstring)env->CallObjectMethod(jlist, getMethod, i); + if (CheckJavaException(env, __FUNCTION__)) { + break; + } + if (jstr) { + result.push_back(GetJavaString(env, jstr)); + env->DeleteLocalRef(jstr); + } + } + return result; +} + // Returns a new local reference to the current activity, or null if there is none. The activity gets // replaced whenever Android recreates it (rotation, resize), so don't hang on to anything longer-lived. jobject Android_GetActivity(JNIEnv *env); diff --git a/android/src/org/ppsspp/ppsspp/NativeApp.java b/android/src/org/ppsspp/ppsspp/NativeApp.java index f330718753..f76bb2dfe1 100644 --- a/android/src/org/ppsspp/ppsspp/NativeApp.java +++ b/android/src/org/ppsspp/ppsspp/NativeApp.java @@ -43,7 +43,7 @@ public class NativeApp { public static native void setDisplayParameters(int display_xres, int display_yres, int dpi, float refreshRate); // rotation is the Surface.ROTATION_* of the display. A change in it alone (a 180 degree flip) keeps the size // but the swapchain still has to be recreated, for the pre-rotation. - public static native void backbufferResize(int bufferWidth, int bufferHeight, int format, int rotation); + public static native void backbufferResize(int bufferWidth, int bufferHeight, int rotation); public static native boolean isLandscape(); public static native boolean isAtTopLevel(); diff --git a/android/src/org/ppsspp/ppsspp/NativeSurfaceManager.java b/android/src/org/ppsspp/ppsspp/NativeSurfaceManager.java index 5227de6e7a..008bc81752 100644 --- a/android/src/org/ppsspp/ppsspp/NativeSurfaceManager.java +++ b/android/src/org/ppsspp/ppsspp/NativeSurfaceManager.java @@ -24,7 +24,6 @@ public class NativeSurfaceManager implements SurfaceHolder.Callback { // A density change has to re-report it, see densityChanged(). private int reportedWidth; private int reportedHeight; - private int reportedFormat; private float reportedDensityDpi; public NativeSurfaceManager(final PpssppActivity a) { @@ -93,7 +92,7 @@ public class NativeSurfaceManager implements SurfaceHolder.Callback { } } - reportSurfaceSize(width, height, format); + reportSurfaceSize(width, height); activity.notifySurface(holder.getSurface()); } @@ -107,12 +106,11 @@ public class NativeSurfaceManager implements SurfaceHolder.Callback { holder.setSizeFromLayout(); } - private void reportSurfaceSize(int width, int height, int format) { + private void reportSurfaceSize(int width, int height) { reportedWidth = width; reportedHeight = height; - reportedFormat = format; reportedDensityDpi = densityDpi; - NativeApp.backbufferResize(width, height, format, activity.getWindowManager().getDefaultDisplay().getRotation()); + NativeApp.backbufferResize(width, height, activity.getWindowManager().getDefaultDisplay().getRotation()); } // A density change rescales the UI but leaves the surface size alone, so no surfaceChanged @@ -124,7 +122,7 @@ public class NativeSurfaceManager implements SurfaceHolder.Callback { return; } Log.i(TAG, "Density is now " + densityDpi + ", re-reporting surface " + reportedWidth + "x" + reportedHeight); - reportSurfaceSize(reportedWidth, reportedHeight, reportedFormat); + reportSurfaceSize(reportedWidth, reportedHeight); } public void updateDisplayMeasurements() {