]> git.eshelyaron.com Git - emacs.git/commitdiff
Update Android port
authorPo Lu <luangruo@yahoo.com>
Sun, 26 Feb 2023 02:33:41 +0000 (10:33 +0800)
committerPo Lu <luangruo@yahoo.com>
Sun, 26 Feb 2023 02:33:41 +0000 (10:33 +0800)
* 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.

doc/lispref/commands.texi
java/org/gnu/emacs/EmacsService.java
src/android.c
src/textconv.c

index 650178dc407f20b779a300ab23ef9e8ff8c6b71a..c2e694207ab5cafb76703b366dc180ef74b2d933 100644 (file)
@@ -2224,13 +2224,13 @@ form:
 @w{@code{((@var{buffer} @var{beg} @var{end} @var{ephemeral}) ...)}}
 @end indentedblock
 
-Where @var{ephemeral} is the buffer which was modified,
-@var{beg} and @var{end} are the positions of the edit at the
-time it was completed, and @var{ephemeral} is either a string,
-containing any text which was inserted, or any text before point
-which was deleted, @code{t}, meaning that the edit is a
-temporary edit made by the input method, and @code{nil}, meaning
-that some text was deleted after point.
+Where @var{ephemeral} is the buffer which was modified, @var{beg} and
+@var{end} are markers set to the positions of the edit at the time it
+was completed, and @var{ephemeral} is either a string, containing any
+text which was inserted, or any text before point which was deleted,
+@code{t}, meaning that the edit is a temporary edit made by the input
+method, and @code{nil}, meaning that some text was deleted after
+point.
 
 @vindex text-conversion-style
 Whether or not this event is sent depends on the value of the
index 48c7c743014433bbe262bd509bad4314b7187c2f..7f4f75b5147822a7a21c3966822cbe85e568d19a 100644 (file)
@@ -101,6 +101,8 @@ public class EmacsService extends Service
   /* Display metrics used by font backends.  */
   public DisplayMetrics metrics;
 
+  /* Flag that says whether or not to print verbose debugging
+     information.  */
   public static final boolean DEBUG_IC = false;
 
   @Override
@@ -117,8 +119,10 @@ public class EmacsService extends Service
       {
        tem = getSystemService (Context.NOTIFICATION_SERVICE);
        manager = (NotificationManager) tem;
-       infoBlurb = ("See (emacs)Android Environment for more"
-                    + " details about this notification.");
+       infoBlurb = ("This notification is displayed to keep Emacs"
+                    + " running while it is in the background.  You"
+                    + " may disable if you want;"
+                    + " see (emacs)Android Environment.");
        channel
          = new NotificationChannel ("emacs", "Emacs persistent notification",
                                     NotificationManager.IMPORTANCE_DEFAULT);
index 99c612f13df4ba4d9dfc2eb8d6999149374e76d9..cf6ddd736eb7a82205ddd32ca84493f0d96266fe 100644 (file)
@@ -1202,13 +1202,13 @@ android_file_access_p (const char *name, int amode)
   return false;
 }
 
-/* Get a file descriptor backed by a temporary in-memory file for the
-   given asset.  */
+/* Do the same as android_hack_asset_fd, but use an unlinked temporary
+   file to cater to old Android kernels where ashmem files are not
+   readable.  */
 
 static int
-android_hack_asset_fd (AAsset *asset)
+android_hack_asset_fd_fallback (AAsset *asset)
 {
-#if __ANDROID_API__ < 9
   int fd;
   char filename[PATH_MAX];
   size_t size;
@@ -1220,8 +1220,8 @@ android_hack_asset_fd (AAsset *asset)
 
   /* Get an unlinked file descriptor from a file in the cache
      directory, which is guaranteed to only be written to by Emacs.
-     Creating an asset file descriptor doesn't work on these old
-     Android versions.  */
+     Creating an ashmem file descriptor and reading from it doesn't
+     work on these old Android versions.  */
 
   snprintf (filename, PATH_MAX, "%s/%s.%d",
            android_cache_dir, "temp-unlinked",
@@ -1261,11 +1261,146 @@ android_hack_asset_fd (AAsset *asset)
  fail:
   close (fd);
   return -1;
-#else
+}
+
+/* Pointer to the `ASharedMemory_create' function which is loaded
+   dynamically.  */
+static int (*asharedmemory_create) (const char *, size_t);
+
+/* Return whether or not shared memory file descriptors can also be
+   read from, and are thus suitable for creating asset files.
+
+   This does not work on some ancient Android systems running old
+   versions of the kernel.  */
+
+static bool
+android_detect_ashmem (void)
+{
+  int fd, rc;
+  void *mem;
+  char test_buffer[10];
+
+  memcpy (test_buffer, "abcdefghi", 10);
+
+  /* Create the file descriptor to be used for the test.  */
+
+  /* Android 28 and earlier let Emacs access /dev/ashmem directly, so
+     prefer that over using ASharedMemory.  */
+
+  if (android_api_level <= 28)
+    {
+      fd = open ("/dev/ashmem", O_RDWR);
+
+      if (fd < 0)
+       return false;
+
+      /* An empty name means the memory area will exist until the file
+        descriptor is closed, because no other process can
+        attach.  */
+      rc = ioctl (fd, ASHMEM_SET_NAME, "");
+
+      if (rc < 0)
+       {
+         close (fd);
+         return false;
+       }
+
+      rc = ioctl (fd, ASHMEM_SET_SIZE, sizeof test_buffer);
+
+      if (rc < 0)
+       {
+         close (fd);
+         return false;
+       }
+    }
+  else
+    {
+      /* On the other hand, SELinux restrictions on Android 29 and
+        later require that Emacs use a system service to obtain
+        shared memory.  Load this dynamically, as this service is not
+        available on all versions of the NDK.  */
+
+      if (!asharedmemory_create)
+       {
+         *(void **) (&asharedmemory_create)
+           = dlsym (RTLD_DEFAULT, "ASharedMemory_create");
+
+         if (!asharedmemory_create)
+           {
+             __android_log_print (ANDROID_LOG_FATAL, __func__,
+                                  "dlsym: %s\n",
+                                  strerror (errno));
+             emacs_abort ();
+           }
+       }
+
+      fd = asharedmemory_create ("", sizeof test_buffer);
+
+      if (fd < 0)
+       return false;
+    }
+
+  /* Now map the resource and write the test contents.  */
+
+  mem = mmap (NULL, sizeof test_buffer, PROT_WRITE,
+             MAP_SHARED, fd, 0);
+  if (mem == MAP_FAILED)
+    {
+      close (fd);
+      return false;
+    }
+
+  /* Copy over the test contents.  */
+  memcpy (mem, test_buffer, sizeof test_buffer);
+
+  /* Return anyway even if munmap fails.  */
+  munmap (mem, sizeof test_buffer);
+
+  /* Try to read the content back into test_buffer.  If this does not
+     compare equal to the original string, or the read fails, then
+     ashmem descriptors are not readable on this system.  */
+
+  if ((read (fd, test_buffer, sizeof test_buffer)
+       != sizeof test_buffer)
+      || memcmp (test_buffer, "abcdefghi", sizeof test_buffer))
+    {
+      __android_log_print (ANDROID_LOG_WARN, __func__,
+                          "/dev/ashmem does not produce real"
+                          " temporary files on this system, so"
+                          " Emacs will fall back to creating"
+                          " unlinked temporary files.");
+      close (fd);
+      return false;
+    }
+
+  close (fd);
+  return true;
+}
+
+/* Get a file descriptor backed by a temporary in-memory file for the
+   given asset.  */
+
+static int
+android_hack_asset_fd (AAsset *asset)
+{
+  static bool ashmem_readable_p;
+  static bool ashmem_initialized;
   int fd, rc;
   unsigned char *mem;
   size_t size;
-  static int (*asharedmemory_create) (const char *, size_t);
+
+  /* The first time this function is called, try to determine whether
+     or not ashmem file descriptors can be read from.  */
+
+  if (!ashmem_initialized)
+    ashmem_readable_p
+      = android_detect_ashmem ();
+  ashmem_initialized = true;
+
+  /* If it isn't, fall back.  */
+
+  if (!ashmem_readable_p)
+    return android_hack_asset_fd_fallback (asset);
 
   /* Assets must be small enough to fit in size_t, if off_t is
      larger.  */
@@ -1386,7 +1521,6 @@ android_hack_asset_fd (AAsset *asset)
   /* Return anyway even if munmap fails.  */
   munmap (mem, size);
   return fd;
-#endif
 }
 
 /* Make FD close-on-exec.  If any system call fails, do not abort, but
index 4b5f9e01162ba4ea769a1358a61a6ffebf585afb..3da8dc831c8db8287cb28da57f9069af11624ad9 100644 (file)
@@ -430,13 +430,32 @@ static void
 record_buffer_change (ptrdiff_t beg, ptrdiff_t end,
                      Lisp_Object ephemeral)
 {
-  Lisp_Object buffer;
+  Lisp_Object buffer, beg_marker, end_marker;
 
   XSETBUFFER (buffer, current_buffer);
 
+  /* Make markers for both BEG and END.  */
+  beg_marker = build_marker (current_buffer, beg,
+                            CHAR_TO_BYTE (beg));
+
+  /* If BEG and END are identical, make sure to keep the markers
+     eq.  */
+
+  if (beg == end)
+    end_marker = beg_marker;
+  else
+    {
+      end_marker = build_marker (current_buffer, end,
+                                CHAR_TO_BYTE (end));
+
+      /* Otherwise, make sure the marker extends past inserted
+        text.  */
+      Fset_marker_insertion_type (end_marker, Qt);
+    }
+
   Vtext_conversion_edits
-    = Fcons (list4 (buffer, make_fixnum (beg),
-                   make_fixnum (end), ephemeral),
+    = Fcons (list4 (buffer, beg_marker, end_marker,
+                   ephemeral),
             Vtext_conversion_edits);
 }
 
@@ -1720,19 +1739,20 @@ form:
 
     (BUFFER BEG END EPHEMERAL)
 
-If an insertion or a change occured, then BEG and END are buffer
-positions denote the bounds of the text that was changed or inserted.
+If an insertion or a change occured, then BEG and END are markers
+which denote the bounds of the text that was changed or inserted.
+
 If EPHEMERAL is t, then the input method will shortly make more
 changes to the text, so any actions that would otherwise be taken
 (such as indenting or automatically filling text) should not take
 place; otherwise, it is a string describing the text which was
 inserted.
 
-If a deletion occured before point, then BEG and END are the same, and
-EPHEMERAL is the text which was deleted.
+If a deletion occured before point, then BEG and END are the same
+object, and EPHEMERAL is the text which was deleted.
 
-If a deletion occured after point, then BEG and END are also the same,
-but EPHEMERAL is nil.
+If a deletion occured after point, then BEG and END are also the same
+object, but EPHEMERAL is nil.
 
 The list contents are ordered later edits first, so you must iterate
 through the list in reverse.  */);