diff options
| author | Po Lu | 2023-02-26 10:33:41 +0800 |
|---|---|---|
| committer | Po Lu | 2023-02-26 10:33:41 +0800 |
| commit | 39ddf1855bbebc5d31b544e6308a4ae49f4925b3 (patch) | |
| tree | 874c9903ca54abd0abcaed2c98d03a1d3fec9633 /src | |
| parent | 687f4fadde4fda8a320cbb4e26518af1034cbb9a (diff) | |
| download | emacs-39ddf1855bbebc5d31b544e6308a4ae49f4925b3.tar.gz emacs-39ddf1855bbebc5d31b544e6308a4ae49f4925b3.zip | |
Update Android port
* doc/lispref/commands.texi (Misc Events): Update documentation.
* java/org/gnu/emacs/EmacsService.java (EmacsService)
(onStartCommand): Improve notification message.
* src/android.c (android_hack_asset_fd): Detect if ashmem is
available dynamically.
(android_detect_ashmem): New function.
* src/textconv.c (record_buffer_change): Use markers to
represent BEG and END instead.
(syms_of_textconv): Update doc string.
Diffstat (limited to 'src')
| -rw-r--r-- | src/android.c | 152 | ||||
| -rw-r--r-- | src/textconv.c | 38 |
2 files changed, 172 insertions, 18 deletions
diff --git a/src/android.c b/src/android.c index 99c612f13df..cf6ddd736eb 100644 --- a/src/android.c +++ b/src/android.c | |||
| @@ -1202,13 +1202,13 @@ android_file_access_p (const char *name, int amode) | |||
| 1202 | return false; | 1202 | return false; |
| 1203 | } | 1203 | } |
| 1204 | 1204 | ||
| 1205 | /* Get a file descriptor backed by a temporary in-memory file for the | 1205 | /* Do the same as android_hack_asset_fd, but use an unlinked temporary |
| 1206 | given asset. */ | 1206 | file to cater to old Android kernels where ashmem files are not |
| 1207 | readable. */ | ||
| 1207 | 1208 | ||
| 1208 | static int | 1209 | static int |
| 1209 | android_hack_asset_fd (AAsset *asset) | 1210 | android_hack_asset_fd_fallback (AAsset *asset) |
| 1210 | { | 1211 | { |
| 1211 | #if __ANDROID_API__ < 9 | ||
| 1212 | int fd; | 1212 | int fd; |
| 1213 | char filename[PATH_MAX]; | 1213 | char filename[PATH_MAX]; |
| 1214 | size_t size; | 1214 | size_t size; |
| @@ -1220,8 +1220,8 @@ android_hack_asset_fd (AAsset *asset) | |||
| 1220 | 1220 | ||
| 1221 | /* Get an unlinked file descriptor from a file in the cache | 1221 | /* Get an unlinked file descriptor from a file in the cache |
| 1222 | directory, which is guaranteed to only be written to by Emacs. | 1222 | directory, which is guaranteed to only be written to by Emacs. |
| 1223 | Creating an asset file descriptor doesn't work on these old | 1223 | Creating an ashmem file descriptor and reading from it doesn't |
| 1224 | Android versions. */ | 1224 | work on these old Android versions. */ |
| 1225 | 1225 | ||
| 1226 | snprintf (filename, PATH_MAX, "%s/%s.%d", | 1226 | snprintf (filename, PATH_MAX, "%s/%s.%d", |
| 1227 | android_cache_dir, "temp-unlinked", | 1227 | android_cache_dir, "temp-unlinked", |
| @@ -1261,11 +1261,146 @@ android_hack_asset_fd (AAsset *asset) | |||
| 1261 | fail: | 1261 | fail: |
| 1262 | close (fd); | 1262 | close (fd); |
| 1263 | return -1; | 1263 | return -1; |
| 1264 | #else | 1264 | } |
| 1265 | |||
| 1266 | /* Pointer to the `ASharedMemory_create' function which is loaded | ||
| 1267 | dynamically. */ | ||
| 1268 | static int (*asharedmemory_create) (const char *, size_t); | ||
| 1269 | |||
| 1270 | /* Return whether or not shared memory file descriptors can also be | ||
| 1271 | read from, and are thus suitable for creating asset files. | ||
| 1272 | |||
| 1273 | This does not work on some ancient Android systems running old | ||
| 1274 | versions of the kernel. */ | ||
| 1275 | |||
| 1276 | static bool | ||
| 1277 | android_detect_ashmem (void) | ||
| 1278 | { | ||
| 1279 | int fd, rc; | ||
| 1280 | void *mem; | ||
| 1281 | char test_buffer[10]; | ||
| 1282 | |||
| 1283 | memcpy (test_buffer, "abcdefghi", 10); | ||
| 1284 | |||
| 1285 | /* Create the file descriptor to be used for the test. */ | ||
| 1286 | |||
| 1287 | /* Android 28 and earlier let Emacs access /dev/ashmem directly, so | ||
| 1288 | prefer that over using ASharedMemory. */ | ||
| 1289 | |||
| 1290 | if (android_api_level <= 28) | ||
| 1291 | { | ||
| 1292 | fd = open ("/dev/ashmem", O_RDWR); | ||
| 1293 | |||
| 1294 | if (fd < 0) | ||
| 1295 | return false; | ||
| 1296 | |||
| 1297 | /* An empty name means the memory area will exist until the file | ||
| 1298 | descriptor is closed, because no other process can | ||
| 1299 | attach. */ | ||
| 1300 | rc = ioctl (fd, ASHMEM_SET_NAME, ""); | ||
| 1301 | |||
| 1302 | if (rc < 0) | ||
| 1303 | { | ||
| 1304 | close (fd); | ||
| 1305 | return false; | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | rc = ioctl (fd, ASHMEM_SET_SIZE, sizeof test_buffer); | ||
| 1309 | |||
| 1310 | if (rc < 0) | ||
| 1311 | { | ||
| 1312 | close (fd); | ||
| 1313 | return false; | ||
| 1314 | } | ||
| 1315 | } | ||
| 1316 | else | ||
| 1317 | { | ||
| 1318 | /* On the other hand, SELinux restrictions on Android 29 and | ||
| 1319 | later require that Emacs use a system service to obtain | ||
| 1320 | shared memory. Load this dynamically, as this service is not | ||
| 1321 | available on all versions of the NDK. */ | ||
| 1322 | |||
| 1323 | if (!asharedmemory_create) | ||
| 1324 | { | ||
| 1325 | *(void **) (&asharedmemory_create) | ||
| 1326 | = dlsym (RTLD_DEFAULT, "ASharedMemory_create"); | ||
| 1327 | |||
| 1328 | if (!asharedmemory_create) | ||
| 1329 | { | ||
| 1330 | __android_log_print (ANDROID_LOG_FATAL, __func__, | ||
| 1331 | "dlsym: %s\n", | ||
| 1332 | strerror (errno)); | ||
| 1333 | emacs_abort (); | ||
| 1334 | } | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | fd = asharedmemory_create ("", sizeof test_buffer); | ||
| 1338 | |||
| 1339 | if (fd < 0) | ||
| 1340 | return false; | ||
| 1341 | } | ||
| 1342 | |||
| 1343 | /* Now map the resource and write the test contents. */ | ||
| 1344 | |||
| 1345 | mem = mmap (NULL, sizeof test_buffer, PROT_WRITE, | ||
| 1346 | MAP_SHARED, fd, 0); | ||
| 1347 | if (mem == MAP_FAILED) | ||
| 1348 | { | ||
| 1349 | close (fd); | ||
| 1350 | return false; | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | /* Copy over the test contents. */ | ||
| 1354 | memcpy (mem, test_buffer, sizeof test_buffer); | ||
| 1355 | |||
| 1356 | /* Return anyway even if munmap fails. */ | ||
| 1357 | munmap (mem, sizeof test_buffer); | ||
| 1358 | |||
| 1359 | /* Try to read the content back into test_buffer. If this does not | ||
| 1360 | compare equal to the original string, or the read fails, then | ||
| 1361 | ashmem descriptors are not readable on this system. */ | ||
| 1362 | |||
| 1363 | if ((read (fd, test_buffer, sizeof test_buffer) | ||
| 1364 | != sizeof test_buffer) | ||
| 1365 | || memcmp (test_buffer, "abcdefghi", sizeof test_buffer)) | ||
| 1366 | { | ||
| 1367 | __android_log_print (ANDROID_LOG_WARN, __func__, | ||
| 1368 | "/dev/ashmem does not produce real" | ||
| 1369 | " temporary files on this system, so" | ||
| 1370 | " Emacs will fall back to creating" | ||
| 1371 | " unlinked temporary files."); | ||
| 1372 | close (fd); | ||
| 1373 | return false; | ||
| 1374 | } | ||
| 1375 | |||
| 1376 | close (fd); | ||
| 1377 | return true; | ||
| 1378 | } | ||
| 1379 | |||
| 1380 | /* Get a file descriptor backed by a temporary in-memory file for the | ||
| 1381 | given asset. */ | ||
| 1382 | |||
| 1383 | static int | ||
| 1384 | android_hack_asset_fd (AAsset *asset) | ||
| 1385 | { | ||
| 1386 | static bool ashmem_readable_p; | ||
| 1387 | static bool ashmem_initialized; | ||
| 1265 | int fd, rc; | 1388 | int fd, rc; |
| 1266 | unsigned char *mem; | 1389 | unsigned char *mem; |
| 1267 | size_t size; | 1390 | size_t size; |
| 1268 | static int (*asharedmemory_create) (const char *, size_t); | 1391 | |
| 1392 | /* The first time this function is called, try to determine whether | ||
| 1393 | or not ashmem file descriptors can be read from. */ | ||
| 1394 | |||
| 1395 | if (!ashmem_initialized) | ||
| 1396 | ashmem_readable_p | ||
| 1397 | = android_detect_ashmem (); | ||
| 1398 | ashmem_initialized = true; | ||
| 1399 | |||
| 1400 | /* If it isn't, fall back. */ | ||
| 1401 | |||
| 1402 | if (!ashmem_readable_p) | ||
| 1403 | return android_hack_asset_fd_fallback (asset); | ||
| 1269 | 1404 | ||
| 1270 | /* Assets must be small enough to fit in size_t, if off_t is | 1405 | /* Assets must be small enough to fit in size_t, if off_t is |
| 1271 | larger. */ | 1406 | larger. */ |
| @@ -1386,7 +1521,6 @@ android_hack_asset_fd (AAsset *asset) | |||
| 1386 | /* Return anyway even if munmap fails. */ | 1521 | /* Return anyway even if munmap fails. */ |
| 1387 | munmap (mem, size); | 1522 | munmap (mem, size); |
| 1388 | return fd; | 1523 | return fd; |
| 1389 | #endif | ||
| 1390 | } | 1524 | } |
| 1391 | 1525 | ||
| 1392 | /* Make FD close-on-exec. If any system call fails, do not abort, but | 1526 | /* Make FD close-on-exec. If any system call fails, do not abort, but |
diff --git a/src/textconv.c b/src/textconv.c index 4b5f9e01162..3da8dc831c8 100644 --- a/src/textconv.c +++ b/src/textconv.c | |||
| @@ -430,13 +430,32 @@ static void | |||
| 430 | record_buffer_change (ptrdiff_t beg, ptrdiff_t end, | 430 | record_buffer_change (ptrdiff_t beg, ptrdiff_t end, |
| 431 | Lisp_Object ephemeral) | 431 | Lisp_Object ephemeral) |
| 432 | { | 432 | { |
| 433 | Lisp_Object buffer; | 433 | Lisp_Object buffer, beg_marker, end_marker; |
| 434 | 434 | ||
| 435 | XSETBUFFER (buffer, current_buffer); | 435 | XSETBUFFER (buffer, current_buffer); |
| 436 | 436 | ||
| 437 | /* Make markers for both BEG and END. */ | ||
| 438 | beg_marker = build_marker (current_buffer, beg, | ||
| 439 | CHAR_TO_BYTE (beg)); | ||
| 440 | |||
| 441 | /* If BEG and END are identical, make sure to keep the markers | ||
| 442 | eq. */ | ||
| 443 | |||
| 444 | if (beg == end) | ||
| 445 | end_marker = beg_marker; | ||
| 446 | else | ||
| 447 | { | ||
| 448 | end_marker = build_marker (current_buffer, end, | ||
| 449 | CHAR_TO_BYTE (end)); | ||
| 450 | |||
| 451 | /* Otherwise, make sure the marker extends past inserted | ||
| 452 | text. */ | ||
| 453 | Fset_marker_insertion_type (end_marker, Qt); | ||
| 454 | } | ||
| 455 | |||
| 437 | Vtext_conversion_edits | 456 | Vtext_conversion_edits |
| 438 | = Fcons (list4 (buffer, make_fixnum (beg), | 457 | = Fcons (list4 (buffer, beg_marker, end_marker, |
| 439 | make_fixnum (end), ephemeral), | 458 | ephemeral), |
| 440 | Vtext_conversion_edits); | 459 | Vtext_conversion_edits); |
| 441 | } | 460 | } |
| 442 | 461 | ||
| @@ -1720,19 +1739,20 @@ form: | |||
| 1720 | 1739 | ||
| 1721 | (BUFFER BEG END EPHEMERAL) | 1740 | (BUFFER BEG END EPHEMERAL) |
| 1722 | 1741 | ||
| 1723 | If an insertion or a change occured, then BEG and END are buffer | 1742 | If an insertion or a change occured, then BEG and END are markers |
| 1724 | positions denote the bounds of the text that was changed or inserted. | 1743 | which denote the bounds of the text that was changed or inserted. |
| 1744 | |||
| 1725 | If EPHEMERAL is t, then the input method will shortly make more | 1745 | If EPHEMERAL is t, then the input method will shortly make more |
| 1726 | changes to the text, so any actions that would otherwise be taken | 1746 | changes to the text, so any actions that would otherwise be taken |
| 1727 | (such as indenting or automatically filling text) should not take | 1747 | (such as indenting or automatically filling text) should not take |
| 1728 | place; otherwise, it is a string describing the text which was | 1748 | place; otherwise, it is a string describing the text which was |
| 1729 | inserted. | 1749 | inserted. |
| 1730 | 1750 | ||
| 1731 | If a deletion occured before point, then BEG and END are the same, and | 1751 | If a deletion occured before point, then BEG and END are the same |
| 1732 | EPHEMERAL is the text which was deleted. | 1752 | object, and EPHEMERAL is the text which was deleted. |
| 1733 | 1753 | ||
| 1734 | If a deletion occured after point, then BEG and END are also the same, | 1754 | If a deletion occured after point, then BEG and END are also the same |
| 1735 | but EPHEMERAL is nil. | 1755 | object, but EPHEMERAL is nil. |
| 1736 | 1756 | ||
| 1737 | The list contents are ordered later edits first, so you must iterate | 1757 | The list contents are ordered later edits first, so you must iterate |
| 1738 | through the list in reverse. */); | 1758 | through the list in reverse. */); |