MemoryFile_Android.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  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)
  64. : m_diskFile(std::move(path), OpenFlag::ReadWrite | OpenFlag::Create, size, fileType), m_ptr(nullptr), m_size(0), m_fileType(fileType) {
  65. if (m_fileType == MMFILE_TYPE_FILE) {
  66. reloadFromFile();
  67. } else {
  68. if (m_diskFile.isFileValid()) {
  69. m_size = m_diskFile.m_size;
  70. auto ret = mmap();
  71. if (!ret) {
  72. doCleanMemoryCache(true);
  73. }
  74. }
  75. }
  76. }
  77. MemoryFile::MemoryFile(int ashmemFD)
  78. : m_diskFile(ashmemFD), m_ptr(nullptr), m_size(0), m_fileType(MMFILE_TYPE_ASHMEM) {
  79. if (!m_diskFile.isFileValid()) {
  80. MMKVError("fd %d invalid", ashmemFD);
  81. } else {
  82. m_size = m_diskFile.m_size;
  83. MMKVInfo("ashmem name:%s, size:%zu", m_diskFile.m_path.c_str(), m_size);
  84. auto ret = mmap();
  85. if (!ret) {
  86. doCleanMemoryCache(true);
  87. }
  88. }
  89. }
  90. } // namespace mmkv
  91. # pragma mark - ashmem
  92. # include <dlfcn.h>
  93. # include <sys/ioctl.h>
  94. namespace mmkv {
  95. constexpr auto ASHMEM_NAME_LEN = 256;
  96. constexpr auto ASHMEM_IOC = 0x77;
  97. # define ASHMEM_SET_NAME _IOW(ASHMEM_IOC, 1, char[ASHMEM_NAME_LEN])
  98. # define ASHMEM_GET_NAME _IOR(ASHMEM_IOC, 2, char[ASHMEM_NAME_LEN])
  99. # define ASHMEM_SET_SIZE _IOW(ASHMEM_IOC, 3, size_t)
  100. # define ASHMEM_GET_SIZE _IO(ASHMEM_IOC, 4)
  101. int g_android_api = __ANDROID_API_L__;
  102. std::string g_android_tmpDir = "/data/local/tmp/";
  103. void *loadLibrary() {
  104. auto name = "libandroid.so";
  105. static auto handle = dlopen(name, RTLD_LAZY | RTLD_LOCAL);
  106. if (handle == RTLD_DEFAULT) {
  107. MMKVError("unable to load library %s", name);
  108. }
  109. return handle;
  110. }
  111. typedef int (*AShmem_create_t)(const char *name, size_t size);
  112. int ASharedMemory_create(const char *name, size_t size) {
  113. int fd = -1;
  114. if (g_android_api >= __ANDROID_API_O__) {
  115. static auto handle = loadLibrary();
  116. static AShmem_create_t funcPtr =
  117. (handle != nullptr) ? reinterpret_cast<AShmem_create_t>(dlsym(handle, "ASharedMemory_create")) : nullptr;
  118. if (funcPtr) {
  119. fd = funcPtr(name, size);
  120. if (fd < 0) {
  121. MMKVError("fail to ASharedMemory_create %s with size %zu, errno:%s", name, size, strerror(errno));
  122. }
  123. } else {
  124. MMKVWarning("fail to locate ASharedMemory_create() from loading libandroid.so");
  125. }
  126. }
  127. if (fd < 0) {
  128. fd = open(ASHMEM_NAME_DEF, O_RDWR | O_CLOEXEC);
  129. if (fd < 0) {
  130. MMKVError("fail to open ashmem:%s, %s", name, strerror(errno));
  131. } else {
  132. if (ioctl(fd, ASHMEM_SET_NAME, name) != 0) {
  133. MMKVError("fail to set ashmem name:%s, %s", name, strerror(errno));
  134. } else if (ioctl(fd, ASHMEM_SET_SIZE, size) != 0) {
  135. MMKVError("fail to set ashmem:%s, size %zu, %s", name, size, strerror(errno));
  136. }
  137. }
  138. }
  139. return fd;
  140. }
  141. typedef size_t (*AShmem_getSize_t)(int fd);
  142. size_t ASharedMemory_getSize(int fd) {
  143. size_t size = 0;
  144. if (g_android_api >= __ANDROID_API_O__) {
  145. static auto handle = loadLibrary();
  146. static AShmem_getSize_t funcPtr =
  147. (handle != nullptr) ? reinterpret_cast<AShmem_getSize_t>(dlsym(handle, "ASharedMemory_getSize")) : nullptr;
  148. if (funcPtr) {
  149. size = funcPtr(fd);
  150. if (size == 0) {
  151. MMKVError("fail to ASharedMemory_getSize:%d, %s", fd, strerror(errno));
  152. }
  153. } else {
  154. MMKVWarning("fail to locate ASharedMemory_create() from loading libandroid.so");
  155. }
  156. }
  157. if (size == 0) {
  158. int tmp = ioctl(fd, ASHMEM_GET_SIZE, nullptr);
  159. if (tmp < 0) {
  160. MMKVError("fail to get ashmem size:%d, %s", fd, strerror(errno));
  161. } else {
  162. size = static_cast<size_t>(tmp);
  163. }
  164. }
  165. return size;
  166. }
  167. string ASharedMemory_getName(int fd) {
  168. // Android Q doesn't have ASharedMemory_getName()
  169. // I've make a request to Google, https://issuetracker.google.com/issues/130741665
  170. // There's nothing we can do before it's supported officially by Google
  171. if (g_android_api >= __ANDROID_API_Q__) {
  172. return "";
  173. }
  174. char name[ASHMEM_NAME_LEN] = {0};
  175. if (ioctl(fd, ASHMEM_GET_NAME, name) != 0) {
  176. MMKVError("fail to get ashmem name:%d, %s", fd, strerror(errno));
  177. return "";
  178. }
  179. return {name};
  180. }
  181. } // namespace mmkv
  182. MMKVPath_t ashmemMMKVPathWithID(const MMKVPath_t &mmapID) {
  183. return MMKVPath_t(ASHMEM_NAME_DEF) + MMKV_PATH_SLASH + mmapID;
  184. }
  185. #endif // MMKV_ANDROID