Android: Add content provider support to File::IOFile

Taking the hit now to prepare us for when Google Play will
force us to use scoped storage...
This commit is contained in:
JosJuice 2020-06-26 17:52:31 +02:00
parent d9f3e382fe
commit 6e1e6d2311
10 changed files with 114 additions and 5 deletions

View File

@ -0,0 +1,23 @@
package org.dolphinemu.dolphinemu.utils;
import android.net.Uri;
import org.dolphinemu.dolphinemu.DolphinApplication;
import java.io.FileNotFoundException;
public class ContentHandler
{
public static int openFd(String uri, String mode)
{
try
{
return DolphinApplication.getAppContext().getContentResolver()
.openFileDescriptor(Uri.parse(uri), mode).detachFd();
}
catch (FileNotFoundException | NullPointerException e)
{
return -1;
}
}
}

View File

@ -11,6 +11,7 @@
#include <jni.h>
#include "Common/StringUtil.h"
#include "jni/AndroidCommon/IDCache.h"
std::string GetJString(JNIEnv* env, jstring jstr)
{
@ -40,3 +41,20 @@ std::vector<std::string> JStringArrayToVector(JNIEnv* env, jobjectArray array)
return result;
}
int OpenAndroidContent(const std::string& uri, const std::string& mode)
{
JNIEnv* env = IDCache::GetEnvForThread();
const jint fd = env->CallStaticIntMethod(IDCache::GetContentHandlerClass(),
IDCache::GetContentHandlerOpenFd(), ToJString(env, uri),
ToJString(env, mode));
// We can get an IllegalArgumentException when passing an invalid mode
if (env->ExceptionCheck())
{
env->ExceptionDescribe();
abort();
}
return fd;
}

View File

@ -11,3 +11,5 @@
std::string GetJString(JNIEnv* env, jstring jstr);
jstring ToJString(JNIEnv* env, const std::string& str);
std::vector<std::string> JStringArrayToVector(JNIEnv* env, jobjectArray array);
int OpenAndroidContent(const std::string& uri, const std::string& mode);

View File

@ -0,0 +1,15 @@
add_library(androidcommon STATIC
AndroidCommon.cpp
AndroidCommon.h
IDCache.cpp
IDCache.h
)
target_link_libraries(androidcommon
PRIVATE
android
log
"-Wl,--no-warn-mismatch"
"-Wl,--whole-archive"
"-Wl,--no-whole-archive"
)

View File

@ -39,6 +39,9 @@ static jmethodID s_ini_file_section_constructor;
static jclass s_compress_cb_class;
static jmethodID s_compress_cb_run;
static jclass s_content_handler_class;
static jmethodID s_content_handler_open_fd;
namespace IDCache
{
JNIEnv* GetEnvForThread()
@ -174,6 +177,16 @@ jmethodID GetCompressCallbackRun()
return s_compress_cb_run;
}
jclass GetContentHandlerClass()
{
return s_content_handler_class;
}
jmethodID GetContentHandlerOpenFd()
{
return s_content_handler_open_fd;
}
} // namespace IDCache
#ifdef __cplusplus
@ -241,6 +254,12 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved)
s_compress_cb_class = reinterpret_cast<jclass>(env->NewGlobalRef(compress_cb_class));
s_compress_cb_run = env->GetMethodID(s_compress_cb_class, "run", "(Ljava/lang/String;F)Z");
const jclass content_handler_class =
env->FindClass("org/dolphinemu/dolphinemu/utils/ContentHandler");
s_content_handler_class = reinterpret_cast<jclass>(env->NewGlobalRef(content_handler_class));
s_content_handler_open_fd = env->GetStaticMethodID(s_content_handler_class, "openFd",
"(Ljava/lang/String;Ljava/lang/String;)I");
return JNI_VERSION;
}
@ -258,6 +277,7 @@ void JNI_OnUnload(JavaVM* vm, void* reserved)
env->DeleteGlobalRef(s_ini_file_class);
env->DeleteGlobalRef(s_ini_file_section_class);
env->DeleteGlobalRef(s_compress_cb_class);
env->DeleteGlobalRef(s_content_handler_class);
}
#ifdef __cplusplus

View File

@ -41,4 +41,7 @@ jmethodID GetIniFileSectionConstructor();
jclass GetCompressCallbackClass();
jmethodID GetCompressCallbackRun();
jclass GetContentHandlerClass();
jmethodID GetContentHandlerOpenFd();
} // namespace IDCache

View File

@ -1,7 +1,6 @@
add_library(main SHARED
AndroidCommon/AndroidCommon.cpp
AndroidCommon/IDCache.cpp
GameList/GameFile.cpp
GameList/GameFile.h
GameList/GameFileCache.cpp
IniFile.cpp
MainAndroid.cpp
@ -10,6 +9,7 @@ add_library(main SHARED
target_link_libraries(main
PRIVATE
androidcommon
core
uicommon
)
@ -32,3 +32,5 @@ file(REMOVE_RECURSE ${CMAKE_SOURCE_DIR}/Source/Android/app/src/main/assets/Sys/R
file(REMOVE_RECURSE ${CMAKE_SOURCE_DIR}/Source/Android/app/src/main/assets/Sys/Themes/)
set(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_EXECUTABLES} main)
add_subdirectory(AndroidCommon)

View File

@ -64,8 +64,8 @@ if (MSVC)
add_definitions(/I${PCH_DIRECTORY})
add_definitions(/Yu${PCH_PATH})
# Don't include timestamps in binaries
add_link_options(/Brepro)
# Don't include timestamps in binaries
add_link_options(/Brepro)
endif()
# These aren't actually needed for C11/C++11

View File

@ -164,6 +164,11 @@ elseif(WIN32)
if (_M_X86_64)
target_link_libraries(common PRIVATE opengl32.lib)
endif()
elseif (ANDROID)
target_link_libraries(common
PRIVATE
androidcommon
)
endif()
if(ANDROID)

View File

@ -15,6 +15,13 @@
#include <unistd.h>
#endif
#ifdef ANDROID
#include <algorithm>
#include "Common/StringUtil.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#endif
#include "Common/CommonTypes.h"
#include "Common/File.h"
#include "Common/FileUtil.h"
@ -62,7 +69,21 @@ bool IOFile::Open(const std::string& filename, const char openmode[])
#ifdef _WIN32
m_good = _tfopen_s(&m_file, UTF8ToTStr(filename).c_str(), UTF8ToTStr(openmode).c_str()) == 0;
#else
m_file = std::fopen(filename.c_str(), openmode);
#ifdef ANDROID
if (StringBeginsWith(filename, "content://"))
{
// The Java method which OpenAndroidContent passes the mode to does not support the b specifier.
// Since we're on POSIX, it's fine to just remove the b.
std::string mode_without_b(openmode);
mode_without_b.erase(std::remove(mode_without_b.begin(), mode_without_b.end(), 'b'),
mode_without_b.end());
m_file = fdopen(OpenAndroidContent(filename, mode_without_b), mode_without_b.c_str());
}
else
#endif
{
m_file = std::fopen(filename.c_str(), openmode);
}
m_good = m_file != nullptr;
#endif