android_storage.cpp
1 // Copyright 2023 Citra Emulator Project 2 // Licensed under GPLv2 or any later version 3 // Refer to the license.txt file included. 4 5 #ifdef ANDROID 6 #include "common/android_storage.h" 7 8 namespace AndroidStorage { 9 JNIEnv* GetEnvForThread() { 10 thread_local static struct OwnedEnv { 11 OwnedEnv() { 12 status = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6); 13 if (status == JNI_EDETACHED) 14 g_jvm->AttachCurrentThread(&env, nullptr); 15 } 16 17 ~OwnedEnv() { 18 if (status == JNI_EDETACHED) 19 g_jvm->DetachCurrentThread(); 20 } 21 22 int status; 23 JNIEnv* env = nullptr; 24 } owned; 25 return owned.env; 26 } 27 28 AndroidOpenMode ParseOpenmode(const std::string_view openmode) { 29 AndroidOpenMode android_open_mode = AndroidOpenMode::NEVER; 30 const char* mode = openmode.data(); 31 int o = 0; 32 switch (*mode++) { 33 case 'r': 34 android_open_mode = AndroidStorage::AndroidOpenMode::READ; 35 break; 36 case 'w': 37 android_open_mode = AndroidStorage::AndroidOpenMode::WRITE; 38 o = O_TRUNC; 39 break; 40 case 'a': 41 android_open_mode = AndroidStorage::AndroidOpenMode::WRITE; 42 o = O_APPEND; 43 break; 44 } 45 46 // [rwa]\+ or [rwa]b\+ means read and write 47 if (*mode == '+' || (*mode == 'b' && mode[1] == '+')) { 48 android_open_mode = AndroidStorage::AndroidOpenMode::READ_WRITE; 49 } 50 51 return android_open_mode | o; 52 } 53 54 void InitJNI(JNIEnv* env, jclass clazz) { 55 env->GetJavaVM(&g_jvm); 56 native_library = clazz; 57 58 #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ 59 F(JMethodID, JMethodName, Signature) 60 #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \ 61 F(JMethodID, JMethodName, Signature) 62 #define F(JMethodID, JMethodName, Signature) \ 63 JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature); 64 ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) 65 ANDROID_STORAGE_FUNCTIONS(FS) 66 #undef F 67 #undef FS 68 #undef FR 69 } 70 71 void CleanupJNI() { 72 #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) 73 #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID) 74 #define F(JMethodID) JMethodID = nullptr; 75 ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) 76 ANDROID_STORAGE_FUNCTIONS(FS) 77 #undef F 78 #undef FS 79 #undef FR 80 } 81 82 bool CreateFile(const std::string& directory, const std::string& filename) { 83 if (create_file == nullptr) 84 return false; 85 auto env = GetEnvForThread(); 86 jstring j_directory = env->NewStringUTF(directory.c_str()); 87 jstring j_filename = env->NewStringUTF(filename.c_str()); 88 return env->CallStaticBooleanMethod(native_library, create_file, j_directory, j_filename); 89 } 90 91 bool CreateDir(const std::string& directory, const std::string& filename) { 92 if (create_dir == nullptr) 93 return false; 94 auto env = GetEnvForThread(); 95 jstring j_directory = env->NewStringUTF(directory.c_str()); 96 jstring j_directory_name = env->NewStringUTF(filename.c_str()); 97 return env->CallStaticBooleanMethod(native_library, create_dir, j_directory, j_directory_name); 98 } 99 100 int OpenContentUri(const std::string& filepath, AndroidOpenMode openmode) { 101 if (open_content_uri == nullptr) 102 return -1; 103 104 const char* mode = ""; 105 switch (openmode) { 106 case AndroidOpenMode::READ: 107 mode = "r"; 108 break; 109 case AndroidOpenMode::WRITE: 110 mode = "w"; 111 break; 112 case AndroidOpenMode::READ_WRITE: 113 mode = "rw"; 114 break; 115 case AndroidOpenMode::WRITE_TRUNCATE: 116 mode = "wt"; 117 break; 118 case AndroidOpenMode::WRITE_APPEND: 119 mode = "wa"; 120 break; 121 case AndroidOpenMode::READ_WRITE_APPEND: 122 mode = "rwa"; 123 break; 124 case AndroidOpenMode::READ_WRITE_TRUNCATE: 125 mode = "rwt"; 126 break; 127 case AndroidOpenMode::NEVER: 128 return -1; 129 } 130 auto env = GetEnvForThread(); 131 jstring j_filepath = env->NewStringUTF(filepath.c_str()); 132 jstring j_mode = env->NewStringUTF(mode); 133 return env->CallStaticIntMethod(native_library, open_content_uri, j_filepath, j_mode); 134 } 135 136 std::vector<std::string> GetFilesName(const std::string& filepath) { 137 auto vector = std::vector<std::string>(); 138 if (get_files_name == nullptr) 139 return vector; 140 auto env = GetEnvForThread(); 141 jstring j_filepath = env->NewStringUTF(filepath.c_str()); 142 auto j_object = 143 (jobjectArray)env->CallStaticObjectMethod(native_library, get_files_name, j_filepath); 144 const jsize j_size = env->GetArrayLength(j_object); 145 vector.reserve(j_size); 146 for (int i = 0; i < j_size; i++) { 147 auto string = (jstring)(env->GetObjectArrayElement(j_object, i)); 148 vector.emplace_back(env->GetStringUTFChars(string, nullptr)); 149 } 150 return vector; 151 } 152 153 bool CopyFile(const std::string& source, const std::string& destination_path, 154 const std::string& destination_filename) { 155 if (copy_file == nullptr) 156 return false; 157 auto env = GetEnvForThread(); 158 jstring j_source_path = env->NewStringUTF(source.c_str()); 159 jstring j_destination_path = env->NewStringUTF(destination_path.c_str()); 160 jstring j_destination_filename = env->NewStringUTF(destination_filename.c_str()); 161 return env->CallStaticBooleanMethod(native_library, copy_file, j_source_path, 162 j_destination_path, j_destination_filename); 163 } 164 165 bool RenameFile(const std::string& source, const std::string& filename) { 166 if (rename_file == nullptr) 167 return false; 168 auto env = GetEnvForThread(); 169 jstring j_source_path = env->NewStringUTF(source.c_str()); 170 jstring j_destination_path = env->NewStringUTF(filename.c_str()); 171 return env->CallStaticBooleanMethod(native_library, rename_file, j_source_path, 172 j_destination_path); 173 } 174 175 #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ 176 F(FunctionName, ReturnValue, JMethodID, Caller) 177 #define F(FunctionName, ReturnValue, JMethodID, Caller) \ 178 ReturnValue FunctionName(const std::string& filepath) { \ 179 if (JMethodID == nullptr) { \ 180 return 0; \ 181 } \ 182 auto env = GetEnvForThread(); \ 183 jstring j_filepath = env->NewStringUTF(filepath.c_str()); \ 184 return env->Caller(native_library, JMethodID, j_filepath); \ 185 } 186 ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) 187 #undef F 188 #undef FR 189 190 } // namespace AndroidStorage 191 #endif