MemoryFile_Android.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /*
  2. * Tencent is pleased to support the open source community by making
  3. * MMKV available.
  4. *
  5. * Copyright (C) 2019 THL A29 Limited, a Tencent company.
  6. * All rights reserved.
  7. *
  8. * Licensed under the BSD 3-Clause License (the "License"); you may not use
  9. * this file except in compliance with the License. You may obtain a copy of
  10. * the License at
  11. *
  12. * https://opensource.org/licenses/BSD-3-Clause
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. #include "MemoryFile.h"
  21. #ifdef MMKV_ANDROID
  22. # include "MMBuffer.h"
  23. # include "MMKVLog.h"
  24. # include <cerrno>
  25. # include <fcntl.h>
  26. # include <sys/mman.h>
  27. # include <sys/stat.h>
  28. # include <unistd.h>
  29. using namespace std;
  30. constexpr char ASHMEM_NAME_DEF[] = "/dev/ashmem";
  31. namespace mmkv {
  32. // for Android Q limiting ashmem access
  33. extern int ASharedMemory_create(const char *name, size_t size);
  34. extern size_t ASharedMemory_getSize(int fd);
  35. extern string ASharedMemory_getName(int fd);
  36. File::File(MMKVPath_t path, OpenFlag flag, size_t size, FileType fileType)
  37. : m_path(std::move(path)), m_fd(-1), m_flag(flag), m_size(0), m_fileType(fileType) {
  38. if (m_fileType == MMFILE_TYPE_FILE) {
  39. open();
  40. } else {
  41. // round up to (n * pagesize)
  42. if (size < DEFAULT_MMAP_SIZE || (size % DEFAULT_MMAP_SIZE != 0)) {
  43. size = ((size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;
  44. }
  45. auto filename = m_path.c_str();
  46. auto ptr = strstr(filename, ASHMEM_NAME_DEF);
  47. if (ptr && ptr[sizeof(ASHMEM_NAME_DEF) - 1] == '/') {
  48. filename = ptr + sizeof(ASHMEM_NAME_DEF);
  49. }
  50. m_fd = ASharedMemory_create(filename, size);
  51. if (isFileValid()) {
  52. m_size = size;
  53. }
  54. }
  55. }
  56. File::File(MMKVFileHandle_t ashmemFD)
  57. : m_path(), m_fd(ashmemFD), m_flag(OpenFlag::ReadWrite), m_size(0), m_fileType(MMFILE_TYPE_ASHMEM) {
  58. if (isFileValid()) {
  59. m_path = ASharedMemory_getName(m_fd);
  60. m_size = ASharedMemory_getSize(m_fd);
  61. }
  62. }
  63. MemoryFile::MemoryFile(string path, size_t size, FileType fileType, size_t expectedCapacity, bool isReadOnly, bool mayflyFD)
  64. : m_diskFile(std::move(path), isReadOnly ? OpenFlag::ReadOnly : (OpenFlag::ReadWrite | OpenFlag::Create), size, fileType),
  65. m_ptr(nullptr), m_size(0), m_fileType(fileType), m_readOnly(isReadOnly), m_isMayflyFD(mayflyFD) {
  66. if (m_fileType == MMFILE_TYPE_FILE) {
  67. reloadFromFile(expectedCapacity);
  68. } else {
  69. if (m_diskFile.isFileValid()) {
  70. m_size = m_diskFile.m_size;
  71. mmapOrCleanup(nullptr);
  72. }
  73. }
  74. }
  75. MemoryFile::MemoryFile(int ashmemFD)
  76. : m_diskFile(ashmemFD), m_ptr(nullptr), m_size(0), m_fileType(MMFILE_TYPE_ASHMEM), m_readOnly(false),
  77. m_isMayflyFD(false) {
  78. if (!m_diskFile.isFileValid()) {
  79. MMKVError("fd %d invalid", ashmemFD);
  80. } else {
  81. m_size = m_diskFile.m_size;
  82. MMKVInfo("ashmem name:%s, size:%zu", m_diskFile.m_path.c_str(), m_size);
  83. mmapOrCleanup(nullptr);
  84. }
  85. }
  86. } // namespace mmkv
  87. # pragma mark - ashmem
  88. # include <dlfcn.h>
  89. # include <sys/ioctl.h>
  90. namespace mmkv {
  91. constexpr auto ASHMEM_NAME_LEN = 256;
  92. constexpr auto ASHMEM_IOC = 0x77;
  93. # define ASHMEM_SET_NAME _IOW(ASHMEM_IOC, 1, char[ASHMEM_NAME_LEN])
  94. # define ASHMEM_GET_NAME _IOR(ASHMEM_IOC, 2, char[ASHMEM_NAME_LEN])
  95. # define ASHMEM_SET_SIZE _IOW(ASHMEM_IOC, 3, size_t)
  96. # define ASHMEM_GET_SIZE _IO(ASHMEM_IOC, 4)
  97. #ifndef MMKV_OHOS
  98. int g_android_api = __ANDROID_API_L__;
  99. #endif
  100. std::string g_android_tmpDir = "/data/local/tmp/";
  101. #ifndef MMKV_OHOS
  102. void *loadLibrary() {
  103. auto name = "libandroid.so";
  104. static auto handle = dlopen(name, RTLD_LAZY | RTLD_LOCAL);
  105. if (handle == RTLD_DEFAULT) {
  106. MMKVError("unable to load library %s", name);
  107. }
  108. return handle;
  109. }
  110. typedef int (*AShmem_create_t)(const char *name, size_t size);
  111. typedef size_t (*AShmem_getSize_t)(int fd);
  112. #endif
  113. int ASharedMemory_create(const char *name, size_t size) {
  114. #ifndef MMKV_OHOS
  115. if (g_android_api >= __ANDROID_API_O__ || g_android_api >= __ANDROID_API_M__) {
  116. static auto handle = loadLibrary();
  117. static AShmem_create_t funcPtr =
  118. (handle != nullptr) ? reinterpret_cast<AShmem_create_t>(dlsym(handle, "ASharedMemory_create")) : nullptr;
  119. if (funcPtr) {
  120. int fd = funcPtr(name, size);
  121. if (fd < 0) {
  122. MMKVError("fail to ASharedMemory_create %s with size %zu, errno:%s", name, size, strerror(errno));
  123. } else {
  124. MMKVInfo("ASharedMemory_create %s with size %zu, fd:%d", name, size, fd);
  125. return fd;
  126. }
  127. } else if (g_android_api >= __ANDROID_API_O__) {
  128. MMKVWarning("fail to locate ASharedMemory_create() from loading libandroid.so");
  129. }
  130. static AShmem_create_t regionFuncPtr =
  131. (handle != nullptr) ? reinterpret_cast<AShmem_create_t>(dlsym(handle, "ashmem_create_region")) : nullptr;
  132. if (regionFuncPtr) {
  133. int fd = regionFuncPtr(name, size);
  134. if (fd < 0) {
  135. MMKVError("fail to ashmem_create_region %s with size %zu, errno:%s", name, size, strerror(errno));
  136. } else {
  137. MMKVInfo("ashmem_create_region %s with size %zu, fd:%d", name, size, fd);
  138. return fd;
  139. }
  140. } else {
  141. MMKVWarning("fail to locate ashmem_create_region() from loading libandroid.so");
  142. }
  143. }
  144. #endif
  145. int fd = open(ASHMEM_NAME_DEF, O_RDWR | O_CLOEXEC);
  146. if (fd < 0) {
  147. MMKVError("fail to open ashmem:%s, %s", name, strerror(errno));
  148. } else {
  149. if (ioctl(fd, ASHMEM_SET_NAME, name) != 0) {
  150. MMKVError("fail to set ashmem name:%s, %s", name, strerror(errno));
  151. } else if (ioctl(fd, ASHMEM_SET_SIZE, size) != 0) {
  152. MMKVError("fail to set ashmem:%s, size %zu, %s", name, size, strerror(errno));
  153. }
  154. }
  155. return fd;
  156. }
  157. size_t ASharedMemory_getSize(int fd) {
  158. size_t size = 0;
  159. #ifndef MMKV_OHOS
  160. if (g_android_api >= __ANDROID_API_O__) {
  161. static auto handle = loadLibrary();
  162. static AShmem_getSize_t funcPtr =
  163. (handle != nullptr) ? reinterpret_cast<AShmem_getSize_t>(dlsym(handle, "ASharedMemory_getSize")) : nullptr;
  164. if (funcPtr) {
  165. size = funcPtr(fd);
  166. if (size == 0) {
  167. MMKVError("fail to ASharedMemory_getSize:%d, %s", fd, strerror(errno));
  168. }
  169. } else {
  170. MMKVWarning("fail to locate ASharedMemory_create() from loading libandroid.so");
  171. }
  172. }
  173. #endif
  174. if (size == 0) {
  175. int tmp = ioctl(fd, ASHMEM_GET_SIZE, nullptr);
  176. if (tmp < 0) {
  177. MMKVError("fail to get ashmem size:%d, %s", fd, strerror(errno));
  178. } else {
  179. size = static_cast<size_t>(tmp);
  180. }
  181. }
  182. return size;
  183. }
  184. string ASharedMemory_getName(int fd) {
  185. // Android Q doesn't have ASharedMemory_getName()
  186. // I've make a request to Google, https://issuetracker.google.com/issues/130741665
  187. // There's nothing we can do before it's supported officially by Google
  188. #ifndef MMKV_OHOS
  189. if (g_android_api >= __ANDROID_API_O__) {
  190. return "";
  191. }
  192. #endif
  193. char name[ASHMEM_NAME_LEN] = {0};
  194. if (ioctl(fd, ASHMEM_GET_NAME, name) != 0) {
  195. MMKVError("fail to get ashmem name:%d, %s", fd, strerror(errno));
  196. return "";
  197. }
  198. return {name};
  199. }
  200. } // namespace mmkv
  201. MMKVPath_t ashmemMMKVPathWithID(const MMKVPath_t &mmapID) {
  202. return MMKVPath_t(ASHMEM_NAME_DEF) + MMKV_PATH_SLASH + mmapID;
  203. }
  204. static long long timespec_to_ms(struct timespec ts) {
  205. return (long long)ts.tv_sec * 1000LL + ts.tv_nsec / 1000000LL;
  206. }
  207. long long getFileModifyTimeInMS(const char *path) {
  208. if (!path) {
  209. return -1;
  210. }
  211. struct stat soStat = {};
  212. if (::stat(path, &soStat) < 0) {
  213. MMKVError("fail to stat %s: %d(%s)", path, errno, strerror(errno));
  214. return -1;
  215. }
  216. return timespec_to_ms(soStat.st_mtim);
  217. }
  218. #endif // MMKV_ANDROID