emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

feature/android 4de6b187933 3/5: Update Android port


From: Po Lu
Subject: feature/android 4de6b187933 3/5: Update Android port
Date: Mon, 23 Jan 2023 21:39:40 -0500 (EST)

branch: feature/android
commit 4de6b187933479ce93b6079f42a485e5868f01a5
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    Update Android port
    
    * .gitignore: Update with new files.  Do not ignore std*.in.h.
    * INSTALL.android: Explain how to build Emacs with external
    dependencies.
    
    * Makefile.in (xcompile, cross): Rename to `cross'.
    (clean_dirs): Clean cross, not xcompile.
    
    * README: Document new directories.
    
    * build-aux/ndk-build-helper-1.mk (build_kind, NDK_SO_NAMES):
    * build-aux/ndk-build-helper-2.mk (build_kind, NDK_SO_NAMES):
    * build-aux/ndk-build-helper-3.mk (build_kind):
    * build-aux/ndk-build-helper-4.mk:
    * build-aux/ndk-build-helper.mk (NDK_BUILD_DIR, my-dir):
    * build-aux/ndk-module-extract.awk: New files.
    * configure.ac: Set up libgif, libwebp, and libpng for
    ndk-build.
    * cross/ndk-build/Makefile.in (srcdir, NDK_BUILD_ANDROID_MK):
    * cross/ndk-build/ndk-build-executable.mk:
    * cross/ndk-build/ndk-build-shared-library.mk (eq, objname):
    * cross/ndk-build/ndk-build-static-library.mk (eq, objname):
    * cross/ndk-build/ndk-build.in (NDK_BUILD_MODULES):
    * cross/ndk-build/ndk-build.mk.in (NDK_BUILD_MODULES)
    (NDK_BUILD_SHARED):
    * cross/ndk-build/ndk-clear-vars.mk:
    * cross/ndk-build/ndk-prebuilt-shared-library.mk:
    * cross/ndk-build/ndk-prebuilt-static-library.mk: New files.
    * doc/emacs/android.texi (Android, Android Environment):
    Document clipboard support on Android.
    * doc/emacs/emacs.texi (Top): Update menus.
    * etc/MACHINES: Document Android.
    * java/AndroidManifest.xml.in: Respect new
    `--with-android-debug' option.
    * java/Makefile.in (CROSS_BINS, CROSS_LIBS): Adjust for rename.
    Include ndk-build.mk.:(emacs.apk-in): Depend on shared
    libraries.  Then, package shared libraries.
    * java/org/gnu/emacs/EmacsClipboard.java (EmacsClipboard): New
    class.
    * java/org/gnu/emacs/EmacsFontDriver.java: Update comment to say
    this is unused.
    * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New
    function `sendExpose'.
    * java/org/gnu/emacs/EmacsSdk11Clipboard.java
    (EmacsSdk11Clipboard):
    * java/org/gnu/emacs/EmacsSdk8Clipboard.java
    (EmacsSdk8Clipboard): New classes.
    * java/org/gnu/emacs/EmacsView.java (EmacsView, handleDirtyBitmap)
    (onDetachedFromWindow): When window is reattached, expose the
    frame.
    
    * lib/Makefile.in (VPATH):
    (ALL_CFLAGS): Adjust for rename.
    
    * lisp/term/android-win.el (android-clipboard-exists-p)
    (android-get-clipboard, android-set-clipboard)
    (android-clipboard-owner-p, android-primary-selection)
    (android-get-clipboard-1, android-get-primary)
    (android-selection-bounds, android-encode-select-string)
    (gui-backend-get-selection, gui-backend-selection-exists-p)
    (gui-backend-selection-owner-p, gui-backend-set-selection): New
    functions.
    
    * m4/ndk-build.m4: New file.
    * src/Makefile.in (GIF_CFLAGS, ANDROID_LDFLAGS): New variables.
    (EMACS_CFLAGS): Add GIF_CFLAGS.  Include
    ndk-build.mk.
    (libemacs.so): Depend on and link with required
    libraries.
    
    * src/android.c (android_check_compressed_file): New function.
    (android_open): Work around Android platform bug.
    (sendExpose): New function.
    (android_readdir): Set d_type if this is a directory.
    
    * src/androidgui.h (enum android_event_type)
    (struct android_expose_event, union android_event): Add expose
    events.
    
    * src/androidselect.c (struct android_emacs_clipboard)
    (android_init_emacs_clipboard, Fandroid_clipboard_owner_p)
    (Fandroid_set_clipboard, Fandroid_get_clipboard)
    (Fandroid_clipboard_exists_p, init_androidselect)
    (syms_of_androidselect): New file.
    
    * src/androidterm.c (handle_one_android_event): Handle
    exposures.
    * src/androidterm.h: Update prototypes.
    * src/emacs.c (android_emacs_init): Initialize androidselect.
---
 .gitignore                                     |  64 ++---
 INSTALL.android                                | 334 ++++++++++++++++++++++++-
 Makefile.in                                    |   4 +-
 README                                         |   3 +
 build-aux/ndk-build-helper-1.mk                |  92 +++++++
 build-aux/ndk-build-helper-2.mk                |  87 +++++++
 build-aux/ndk-build-helper-3.mk                |  28 +++
 build-aux/ndk-build-helper-4.mk                |  38 +++
 build-aux/ndk-build-helper.mk                  |  52 ++++
 build-aux/ndk-module-extract.awk               |  64 +++++
 configure.ac                                   |  90 +++++--
 cross/ndk-build/Makefile.in                    | 115 +++++++++
 cross/ndk-build/ndk-build-executable.mk        |  22 ++
 cross/ndk-build/ndk-build-shared-library.mk    |  83 ++++++
 cross/ndk-build/ndk-build-static-library.mk    |  83 ++++++
 cross/ndk-build/ndk-build.in                   |  44 ++++
 cross/ndk-build/ndk-build.mk.in                |  43 ++++
 cross/ndk-build/ndk-clear-vars.mk              |  47 ++++
 cross/ndk-build/ndk-prebuilt-shared-library.mk |  24 ++
 cross/ndk-build/ndk-prebuilt-static-library.mk |  24 ++
 doc/emacs/android.texi                         |  29 ++-
 doc/emacs/emacs.texi                           |   1 +
 etc/MACHINES                                   |   8 +
 java/AndroidManifest.xml.in                    |   5 +-
 java/Makefile.in                               |  30 ++-
 java/org/gnu/emacs/EmacsClipboard.java         |  44 ++++
 java/org/gnu/emacs/EmacsFontDriver.java        |   3 +
 java/org/gnu/emacs/EmacsNative.java            |   4 +
 java/org/gnu/emacs/EmacsSdk11Clipboard.java    | 156 ++++++++++++
 java/org/gnu/emacs/EmacsSdk8Clipboard.java     | 116 +++++++++
 java/org/gnu/emacs/EmacsView.java              |  23 ++
 lib/Makefile.in                                |   4 +-
 lisp/term/android-win.el                       | 106 ++++++++
 m4/ndk-build.m4                                | 213 ++++++++++++++++
 src/Makefile.in                                |  19 +-
 src/android.c                                  |  82 +++++-
 src/androidgui.h                               |  11 +
 src/androidselect.c                            | 249 ++++++++++++++++++
 src/androidterm.c                              |  22 ++
 src/androidterm.h                              |   9 +
 src/emacs.c                                    |   2 +
 41 files changed, 2406 insertions(+), 71 deletions(-)

diff --git a/.gitignore b/.gitignore
index 6494e4e8f39..6b624767c66 100644
--- a/.gitignore
+++ b/.gitignore
@@ -96,36 +96,40 @@ src/lisp.mk
 src/verbose.mk
 
 # Stuff built during cross compilation
-xcompile/lib/alloca.h
-xcompile/lib/assert.h
-xcompile/lib/byteswap.h
-xcompile/lib/dirent.h
-xcompile/lib/errno.h
-xcompile/lib/execinfo.h
-xcompile/lib/fcntl.h
-xcompile/lib/getopt.h
-xcompile/lib/getopt-cdefs.h
-xcompile/lib/gmp.h
-xcompile/lib/ieee754.h
-xcompile/lib/inttypes.h
-xcompile/lib/libgnu.a
-xcompile/lib/limits.h
-xcompile/lib/malloc/*.gl.h
-xcompile/lib/signal.h
-xcompile/lib/std*.h
-xcompile/!lib/std*.in.h
-xcompile/!lib/stdio-impl.h
-xcompile/lib/string.h
-xcompile/lib/sys/
-xcompile/lib/time.h
-xcompile/lib/unistd.h
-xcompile/lib/config.h
-xcompile/lib/gnulib.mk
-xcompile/src/*
-xcompile/lib-src/*
-xcompile/sys/*
-xcompile/config.status
-xcompile/*.bak
+cross/lib/alloca.h
+cross/lib/assert.h
+cross/lib/byteswap.h
+cross/lib/dirent.h
+cross/lib/errno.h
+cross/lib/execinfo.h
+cross/lib/fcntl.h
+cross/lib/getopt.h
+cross/lib/getopt-cdefs.h
+cross/lib/gmp.h
+cross/lib/ieee754.h
+cross/lib/inttypes.h
+cross/lib/libgnu.a
+cross/lib/limits.h
+cross/lib/malloc/*.gl.h
+cross/lib/signal.h
+cross/lib/std*.h
+!cross/lib/std*.in.h
+!cross/lib/stdio-impl.h
+cross/lib/string.h
+cross/lib/sys/
+cross/lib/time.h
+cross/lib/unistd.h
+cross/lib/config.h
+cross/lib/gnulib.mk
+cross/src/*
+cross/lib-src/*
+cross/sys/*
+cross/config.status
+cross/*.bak
+
+cross/ndk-build/Makefile
+cross/ndk-build/ndk-build.mk
+cross/ndk-build/*.o
 
 # Lisp-level sources built by 'make'.
 *cus-load.el
diff --git a/INSTALL.android b/INSTALL.android
index 63c856fe56c..9abea74a55d 100644
--- a/INSTALL.android
+++ b/INSTALL.android
@@ -62,7 +62,7 @@ medium) and installed on-device.
 BUILDING WITH OLD NDK VERSIONS
 
 Building Emacs with an old version of the Android NDK requires special
-setup.  This is because there is no separate C compiler binary for
+setup. This is because there is no separate C compiler binary for
 each version of Android in those versions of the NDK.
 
 Before running `configure', you must identify three variables:
@@ -94,7 +94,339 @@ Emacs is known to build for Android 2.2 (API version 8) or 
later, and
 run on Android 2.3 or later.  It is supposed to run on Android 2.2 as
 well.
 
+DEBUG AND RELEASE BUILDS
+
+Android makes a distinction between ``debug'' and ``release'' builds
+of applications.  With ``release'' builds, the system will apply
+stronger optimizations to the application at the cost of being unable
+to debug them with the steps in etc/DEBUG.
+
+Emacs is built as a debuggable package by default, but:
+
+      ./configure --without-android-debug
+
+will create a release build of Emacs instead.  This may be useful when
+running Emacs on resource constrained machines.
+
+If you are building an Emacs package for redistribution, we urge you
+to provide both debug and release versions.
+
+BUILDING WITH THIRD PARTY LIBRARIES
+
+The Android NDK does not support the usual ways of locating third
+party libraries, especially not via `pkg-config'.  Instead, it uses
+its own system called `ndk-build'.  The one exception to this rule is
+zlib, which is considered a part of the Android OS itself and is
+available on all devices running Android.
+
+Android also requires that each application include its own
+dependencies, as the system makes no guarantee about the existence of
+any particular library.
+
+Emacs is not built with the `ndk-build' system.         Instead, it is built
+with Autoconf and Make.
+
+However, it supports building and including dependencies which use the
+similarly Make-based `ndk-build' system.
+
+To use dependencies built through `ndk-build', you must specify a list
+of directories within which Emacs will search for ``Android.mk''
+files, like so:
+
+  ./configure "--with-ndk-path=directory1 directory2"
+
+Emacs will then read the ``Android.mk'' file in each directory, and
+automatically build and use those modules.
+
+Google, Inc. has adapted many common Emacs dependencies to use the
+`ndk-build' system.  Here is a non-exhaustive list of what is known to
+work:
+
+  libpng       - https://android.googlesource.com/platform/external/libpng
+  libwebp      - https://android.googlesource.com/platform/external/webp
+  giflib       - https://android.googlesource.com/platform/external/giflib
+     (You must add LOCAL_EXPORT_CFLAGS := -I$(LOCAL_PATH) before
+      its Android.mk includes $(BUILD_STATIC_LIBRARY))
+
+We anticipate that most untested non-trivial ndk-build dependencies
+will need adjustments in Emacs to work, as the Emacs build system
+which emulates ndk-build is in an extremely early state.
+
+NDK BUILD SYSTEM IMPLEMENTATION
+
+Emacs implements ndk-build itself, because the version that comes with
+the Android NDK is not easy to use from another Makefile, and keeps
+accumulating incompatible changes.
+
+The Emacs implementation of ndk-build consists of one m4 file:
+
+  m4/ndk-build.m4
+
+four Makefiles in build-aux, run during configure:
+
+  build-aux/ndk-build-helper-1.mk
+  build-aux/ndk-build-helper-2.mk
+  build-aux/ndk-build-helper-3.mk
+  build-aux/ndk-build-helper.mk
+
+one awk script in build-awx, run during configure:
+
+  build-aux/ndk-module-extract.awk
+
+six Makefiles in cross/ndk-build,
+
+  cross/ndk-build/ndk-build-shared-library.mk
+  cross/ndk-build/ndk-build-static-library.mk
+  cross/ndk-build/ndk-build-executable.mk
+  cross/ndk-build/ndk-clear-vars.mk
+  cross/ndk-build/ndk-prebuilt-shared-library.mk
+  cross/ndk-build/ndk-prebuilt-static-library.mk
+
+and finally, two more Makefiles in cross/ndk-build, generated by
+configure:
+
+  cross/ndk-build/Makefile     (generated from cross/ndk-build/Makefile.in)
+  cross/ndk-build/ndk-build.mk (generated from cross/ndk-build/ndk-build.mk.in)
+
+m4/ndk-build.m4 is a collection of macros which are used by the
+configure script to set up the ndk-build system, look for modules, add
+the appropriate options to LIBS and CFLAGS, and generate the Makefiles
+necessary to build the rest of Emacs.
+
+Immediately after determining the list of directories in which to look
+for ``Android.mk'' files, the version and type of Android system being
+built for, configure calls:
+
+  ndk_INIT([$android_abi], [$ANDROID_SDK], [cross/ndk-build])
+
+This expands to a sequence of shell script that enumerates all of the
+Android.mk files specified in "$with_ndk_path", sets up some shell
+functions used by the rest of the ndk-build code run by the configure
+script, and teaches the ndk-build system that the Makefiles to be
+generated are found in the directory "cross/ndk-build/Makefile".
+
+When configure is cross-compiling for Android, the macro
+EMACS_CHECK_MODULES will expand to the macro ndk_CHECK_MODULES,
+instead of pkg-config.m4's PKG_CHECK_MODULES.  Thus, the following
+code:
+
+    EMACS_CHECK_MODULES([PNG], [libpng >= 1.0.0])
+
+will actually expand to:
+
+    ndk_CHECK_MODULES([PNG], [libpng >= 1.0.0], [HAVE_PNG=yes],
+                     [HAVE_PNG=no])
+
+which in turn expands to a sequence shell script that first invokes:
+
+    make -f build-aux/ndk-build-helper.mk
+
+for each ``Android.mk'' file found by ndk_INIT, with the following
+variables given to Make:
+
+    EMACS_SRCDIR=.  # the source directory (in which configure is running)
+    EMACS_ABI=$ndk_ABI # this is the $android_abi given to ndk_INIT
+    ANDROID_MAKEFILE="/opt/android/libpng/Android.mk"
+    ANDROID_MODULE_DIRECTORY="/opt/android/libpng"
+    NDK_BUILD_DIR="$ndk_DIR" # this is the directory given as to ndk_INIT
+
+build-aux/ndk-build-helper.mk will then evaluate the contents
+$(ANDROID_MAKEFILE), the ``Android.mk'' file, for the first time.  The
+purpose of this evaluation is to establish a list of packages (or
+modules) provided by the ``Android.mk'' file, and the corresponding
+Makefile targets and compiler and linker flags required to build and
+link to those tagets.
+
+Before doing so, build-aux/ndk-build-helper.mk will define several
+variables and functions required by all ``Android.mk'' files.  The
+most important of these are:
+
+  my-dir # the directory containing the Android.mk file.
+  BUILD_SHARED_LIBRARY # build-aux/ndk-build-helper-1.mk
+  BUILD_STATIC_LIBRARY # build-aux/ndk-build-helper-2.mk
+  BUILD_EXECUTABLE # build-aux/ndk-build-helper-3.mk
+  CLEAR_VARS # build-aux/ndk-build-helper-4.mk
+
+Then, ``Android.mk'' will include $(CLEAN_VARS) (to clear variables
+previously set), set several variables describing each module to the
+ndk-build system, and include one of $(BUILD_SHARED_LIBRARY),
+$(BUILD_STATIC_LIBRARY) and $(BUILD_EXECUTABLE).
+
+Each one of those three scripts will then read from the variables set
+by ``Android.mk'', resolve dependencies, and print out some text
+describing the module to Emacs.         For example, the shared library
+module "libpng" results in the following text being printed:
+
+Building shared
+libpng
+/opt/android/libpng/png.c /opt/android/libpng/pngerror.c 
/opt/android/libpng/pngget.c /opt/android/libpng/pngmem.c 
/opt/android/libpng/pngpread.c /opt/android/libpng/pngread.c 
/opt/android/libpng/pngrio.c /opt/android/libpng/pngrtran.c 
/opt/android/libpng/pngrutil.c /opt/android/libpng/pngset.c 
/opt/android/libpng/pngtrans.c /opt/android/libpng/pngwio.c 
/opt/android/libpng/pngwrite.c /opt/android/libpng/pngwtran.c 
/opt/android/libpng/pngwutil.c
+-I/opt/android/libpng
+
+  -L/opt/emacs/cross/ndk-build -l:libpng_emacs.so
+libpng_emacs.so
+End
+
+The output is arranged as follows:
+
+  - The first line consists of the word ``Building'', followed by
+    either ``shared'', ``static'', or ``executable'', depending on
+    what type of module being built.
+
+  - The second line consists of the name of the module currently being
+    built.
+
+  - The third line consists of all of the source code files comprising
+    the module.
+
+  - The fourth line consists of the text that has to be added to
+    CFLAGS in order to find the includes associated with the module.
+
+  - The fifth line consists of the text that has to be added to LIBS
+    in order to link with this module and all of its dependencies.
+
+  - The sixth line consists of the Make targets (more on this later)
+    that will build the final shared object or library archive of this
+    module, along with all of its dependencies.
+
+The output from Make is given to an awk script,
+build-aux/ndk-module-extract.awk.  This is responsible for parsing the
+that output and filtering out modules other than what is being built:
+
+  awk -f build-aux/ndk-module-extract.awk MODULE=libpng
+
+eventually generating this section of shell script:
+
+module_name=libpng
+module_kind=shared
+module_src="/opt/android/libpng/png.c /opt/android/libpng/pngerror.c 
/opt/android/libpng/pngget.c /opt/android/libpng/pngmem.c 
/opt/android/libpng/pngpread.c /opt/android/libpng/pngread.c 
/opt/android/libpng/pngrio.c /opt/android/libpng/pngrtran.c 
/opt/android/libpng/pngrutil.c /opt/android/libpng/pngset.c 
/opt/android/libpng/pngtrans.c /opt/android/libpng/pngwio.c 
/opt/android/libpng/pngwrite.c /opt/android/libpng/pngwtran.c 
/opt/android/libpng/pngwutil.c"
+module_includes="-I/opt/android/libpng"
+module_cflags=""
+module_ldflags="  -L/opt/emacs/cross/ndk-build -l:libpng_emacs.so"
+module_target="libpng_emacs.so"
+
+which is then evaluated by `configure'.         Once the variable
+`module_name' is set, configure apends the remaining
+$(module_includes), $(module_cflags) and $(module_ldflags) to the
+module's CFLAGS and LIBS variables, and appends the list of Makefile
+targets specified to the variable NDK_BUILD_MODULES.
+
+Finally, immediately before generating src/Makefile.android, configure
+expands:
+
+  ndk_CONFIG_FILES
+
+to generate $ndk_DIR/Makefile and $ndk_DIR/ndk-build.mk.
+
+Now, the $ndk_DIR directory is set up to build all modules upon which
+depends, and $ndk_DIR/ndk-build.mk includes a list of files required
+to link Emacs, along with the rules to chdir into $ndk_DIR in order to
+build them.
+
+$ndk_DIR/ndk-build.mk is included by cross/src/Makefile
+(Makefile.android) and java/Makefile.  It defines three different
+variables:
+
+  NDK_BUILD_MODULES    the file names of all modules to be built.
+  NDK_BUILD_STATIC     absolute names of all library archives
+                       to be built.
+  NDK_BUILD_SHARED     absolute names of all shared libraries to
+                       be built.
+
+and then proceeds to define rules to build each of the modules in
+$(NDK_BUILD_MODULES).
+
+cross/src/Makefile arranges to have all dependencies of Emacs not
+already built built before linking ``libemacs.so'' with them.
+
+java/Makefile additionally arranges to have all shared object
+dependencies built before the application package is built, which is
+normally redundant because they should have already been built before
+linking ``libemacs.so''.
+
+Building the modules is performed through $ndk_DIR/Makefile, which
+contains the actual implementation of the ``ndk-build'' build system.
+First, it defines certain variables constant within the ``ndk-build''
+build system, such as the files included by ``Android.mk'' to build
+shared or static libraries, and CLEAR_VARS.  The most important of
+these are:
+
+  CLEAR_VARS                   cross/ndk-build/ndk-clear-vars.mk
+  BUILD_EXECUTABLE             cross/ndk-build/ndk-build-executable.mk
+  BUILD_SHARED_LIBRARY         cross/ndk-build/ndk-build-shared-library.mk
+  BUILD_STATIC_LIBRARY         cross/ndk-build/ndk-build-static-library.mk
+  PREBUILT_SHARED_LIBRARY      cross/ndk-build/ndk-prebuilt-shared-library.mk
+  PREBUILT_STATIC_LIBRARY      cross/ndk-build/ndk-prebuilt-static-library.mk
+
+Then, it loads each Emacs dependency's ``Android.mk'' file.  For each
+module defined there, ``Android.mk'' includes $(CLEAR_VARS) to unset
+all variables specific to each module, and then includes
+$(BUILD_SHARED_LIBRARY) or $(BUILD_STATIC_LIBRARY) for each shared or
+static library module.
+
+This results in cross/ndk-build/ndk-build-shared-library.mk or
+cross/ndk-build/ndk-build-static-library being included, just like the
+Makefiles in build-aux were inside the configure script.
+
+Each one of those two scripts then defines rules to build all of the
+object files associated with the module, and then link or archive
+them.  The name under which the module is linked is the same as the
+Make target found on the sixth line of output from
+build-aux/ndk-build-helper.mk.
+
+However, none of the Makefiles in
+cross/ndk-build/ndk-build-shared-library.mk perform any kind of
+dependency resolution! Instead, they only define rules to build
+individual modules, leaving dependency resolution up to the Makefiles
+in the `build-aux' directory.
+
+libpng is a very simple module, providing only a single shared object
+module.         This module is named libpng_emacs.so and is eventually built
+and packaged into the library directory of the Emacs application
+package.  Now, let us look at a more complex module, libwebp:
+
 
+
+When built with libwebp, Emacs depends on a single library,
+libwebpdemux.  This library is named ``libwebpdemux'' on Unix systems,
+and that is the name by which it is found with pkg-config.
+
+However, the library's module is only named ``webpdemux'' on Android.
+When ndk_CHECK_MODULES begins to look for a module, it first tries to
+see if its name is found in the variable `ndk_package_map', which was
+set inside ndk_INIT.  In this case, it finds the following word:
+
+  libwebpdemux:webpdemux
+
+and immediately replaces ``libwebpdemux'' with ``webpdemux''.
+
+Then, it locates the ``Android.mk'' file containing a static library
+module named webpdemux and gives the output from
+build-aux/ndk-build-helper.mk to the awk script, resulting in:
+
+module_name=webpdemux
+module_kind=static
+module_src="/opt/android/webp/src/demux/anim_decode.c 
/opt/android/webp/src/demux/demux.c"
+module_includes="-I/opt/android/webp/src"
+module_cflags=""
+module_ldflags=" cross/ndk-build/libwebpdemux.a cross/ndk-build/libwebp.a 
cross/ndk-build/libwebpdecoder_static.a "
+module_target="libwebpdemux.a libwebp.a libwebpdecoder_static.a"
+
+The attentive reader will notice that in addition to the
+``libwebpdemux.a'' archive associated with the ``webpdemux'' library,
+Emacs has been made to link with two additional libraries.  This is
+because the ``webpdemux'' module specifies a dependency on the
+``webp'' module (defined in the same Android.mk).
+build-aux/ndk-build-helper.mk resolved that dependency, noticing that
+it in turn specified another dependency on ``webpdecoder_static'',
+which in turn was added to the linker command line and list of targets
+to build.
+
+As a result, all three dependencies will be built and linked to Emacs,
+instead of just the single ``webpdemux'' dependency that was
+specified.
+
+
+
 This file is part of GNU Emacs.
 
 GNU Emacs is free software: you can redistribute it and/or modify
diff --git a/Makefile.in b/Makefile.in
index 0090b858228..c4b346128dc 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -539,7 +539,7 @@ lib lib-src lisp nt: Makefile
 java: lisp info
        $(MAKE) -C $@ all
 
-xcompile: src
+cross: src
        $(MAKE) -C $@ all
 
 trampolines: src lisp
@@ -1012,7 +1012,7 @@ mostlyclean: $(mostlyclean_dirs:=_mostlyclean)
 ###      with them.
 ###
 ###      Delete '.dvi' files here if they are not part of the distribution.
-clean_dirs = $(mostlyclean_dirs) java xcompile nextstep admin/charsets \
+clean_dirs = $(mostlyclean_dirs) java cross nextstep admin/charsets \
   admin/unidata
 
 $(foreach dir,$(clean_dirs),$(eval $(call submake_template,$(dir),clean)))
diff --git a/README b/README
index 19d5c96e348..c8a3d485c28 100644
--- a/README
+++ b/README
@@ -95,6 +95,9 @@ There are several subdirectories:
 'admin'     holds files used by Emacs developers, and Unicode data files.
 'build-aux' holds auxiliary files used during the build.
 'm4'        holds Autoconf macros used for generating the configure script.
+'java'     holds the Java code for the Emacs port to Android.
+'cross'            holds Makefiles and an additional copy of gnulib used to 
build
+           Emacs for Android devices.
 
    Building Emacs on non-Posix platforms requires tools that aren't part
 of the standard distribution of the OS.  The platform-specific README
diff --git a/build-aux/ndk-build-helper-1.mk b/build-aux/ndk-build-helper-1.mk
new file mode 100644
index 00000000000..fb8e4da2f0a
--- /dev/null
+++ b/build-aux/ndk-build-helper-1.mk
@@ -0,0 +1,92 @@
+# ndk-build-helper-1.mk -- Helper for ndk-build.m4.
+# Copyright (C) 2023 Free Software Foundation, Inc.
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+# Print out information now defined.  Important details include:
+#   - list of source files to compile.
+#   - module export include directories.
+#   - module export CFLAGS.
+#   - module export LDFLAGS.
+#   - module name.
+
+build_kind = shared
+NDK_SO_NAMES =
+NDK_A_NAMES =
+
+# Record this module's dependencies.  This information is used later
+# on to recurse over libraries.
+NDK_$(LOCAL_MODULE)_STATIC_LIBRARIES := $(LOCAL_STATIC_LIBRARIES) 
$(LOCAL_WHOLE_STATIC_LIBRARIES)
+NDK_$(LOCAL_MODULE)_SHARED_LIBRARIES := $(LOCAL_SHARED_LIBRARIES)
+
+$(info Building $(build_kind))
+$(info $(LOCAL_MODULE))
+$(info $(addprefix $(ANDROID_MODULE_DIRECTORY)?,$(LOCAL_SRC_FILES) 
$(LOCAL_SRC_FILES$(EMACS_ABI))))
+
+$(info $(foreach dir,$(LOCAL_EXPORT_C_INCLUDE_DIRS) 
$(LOCAL_EXPORT_C_INCLUDES),-I$(dir)))
+$(info $(LOCAL_EXPORT_CFLAGS))
+ifeq ($(LOCAL_MODULE_FILENAME),)
+ifeq ($(findstring lib,$(LOCAL_MODULE)),lib)
+NDK_SO_NAMES = $(LOCAL_MODULE)_emacs.so
+else
+NDK_SO_NAMES = lib$(LOCAL_MODULE)_emacs.so
+endif
+else
+NDK_SO_NAMES = $(LOCAL_MODULE_FILENAME).so
+endif
+
+define add-a-name
+ifeq ($(findstring lib,$(1)),lib)
+NDK_A_NAME = $(1).a
+else
+NDK_A_NAME = lib$(1).a
+endif
+
+ifeq ($$(NDK_A_NAMES:$$(NDK_A_NAME)=),$$(NDK_A_NAMES))
+NDK_A_NAMES := $$(NDK_A_NAMES) $$(NDK_A_NAME)
+
+# Now recurse over this module's dependencies.
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), 
$$(NDK_$(1)_STATIC_LIBRARIES)),$$(eval $$(call add-a-name,$$(module))))
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), 
$$(NDK_$(1)_SHARED_LIBRARIES)),$$(eval $$(call add-so-name,$$(module))))
+endif
+endef
+
+define add-so-name
+ifeq ($(findstring lib,$(1)),lib)
+NDK_SO_NAME = $(1)_emacs.so
+else
+NDK_SO_NAME = lib$(1)_emacs.so
+endif
+
+ifeq ($$(NDK_SO_NAMES:$$(NDK_SO_NAME)=),$$(NDK_SO_NAMES))
+NDK_SO_NAMES := $$(NDK_SO_NAMES) $$(NDK_SO_NAME)
+
+# Now recurse over this module's dependencies.
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), 
$$(NDK_$(1)_STATIC_LIBRARIES)),$$(eval $$(call add-a-name,$$(module))))
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), 
$$(NDK_$(1)_SHARED_LIBRARIES)),$$(eval $$(call add-so-name,$$(module))))
+endif
+endef
+
+# Resolve additional dependencies based on LOCAL_STATIC_LIBRARIES and
+# LOCAL_SHARED_LIBRARIES.
+
+SYSTEM_LIBRARIES = z libz libc c
+
+$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), 
$(LOCAL_STATIC_LIBRARIES)),$(eval $(call add-a-name,$(module))))
+$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), 
$(LOCAL_SHARED_LIBRARIES)),$(eval $(call add-so-name,$(module))))
+
+$(info $(LOCAL_EXPORT_LDFLAGS) $(abspath $(addprefix 
$(NDK_BUILD_DIR)/,$(NDK_A_NAMES))) -L$(abspath $(NDK_BUILD_DIR)) $(foreach 
soname,$(NDK_SO_NAMES),-l:$(soname)))
+$(info $(NDK_SO_NAMES))
+$(info End)
diff --git a/build-aux/ndk-build-helper-2.mk b/build-aux/ndk-build-helper-2.mk
new file mode 100644
index 00000000000..82c1d53c7d0
--- /dev/null
+++ b/build-aux/ndk-build-helper-2.mk
@@ -0,0 +1,87 @@
+# ndk-build-helper-2.mk -- Helper for ndk-build.m4.
+# Copyright (C) 2023 Free Software Foundation, Inc.
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+# Say a static library is being built
+build_kind = static
+NDK_SO_NAMES =
+NDK_A_NAMES =
+
+# Record this module's dependencies.  This information is used later
+# on to recurse over libraries.
+NDK_$(LOCAL_MODULE)_STATIC_LIBRARIES := $(LOCAL_STATIC_LIBRARIES) 
$(LOCAL_WHOLE_STATIC_LIBRARIES)
+NDK_$(LOCAL_MODULE)_SHARED_LIBRARIES := $(LOCAL_SHARED_LIBRARIES)
+
+$(info Building $(build_kind))
+$(info $(LOCAL_MODULE))
+$(info $(addprefix $(ANDROID_MODULE_DIRECTORY)/,$(LOCAL_SRC_FILES) 
$(LOCAL_SRC_FILES$(EMACS_ABI))))
+
+$(info $(foreach dir,$(LOCAL_EXPORT_C_INCLUDE_DIRS) 
$(LOCAL_EXPORT_C_INCLUDES),-I$(dir)))
+$(info $(LOCAL_EXPORT_CFLAGS))
+ifeq ($(LOCAL_MODULE_FILENAME),)
+
+ifeq ($(findstring lib,$(LOCAL_MODULE)),lib)
+NDK_A_NAMES = $(LOCAL_MODULE).a
+else
+NDK_A_NAMES = lib$(LOCAL_MODULE).a
+endif
+else
+NDK_A_NAMES = $(LOCAL_MODULE_FILENAME).a
+endif
+
+define add-a-name
+ifeq ($(findstring lib,$(1)),lib)
+NDK_A_NAME = $(1).a
+else
+NDK_A_NAME = lib$(1).a
+endif
+
+ifeq ($$(NDK_A_NAMES:$$(NDK_A_NAME)=),$$(NDK_A_NAMES))
+NDK_A_NAMES := $$(NDK_A_NAMES) $$(NDK_A_NAME)
+
+# Now recurse over this module's dependencies.
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), 
$$(NDK_$(1)_STATIC_LIBRARIES)),$$(eval $$(call add-a-name,$$(module))))
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), 
$$(NDK_$(1)_SHARED_LIBRARIES)),$$(eval $$(call add-so-name,$$(module))))
+endif
+endef
+
+define add-so-name
+ifeq ($(findstring lib,$(1)),lib)
+NDK_SO_NAME = $(1)_emacs.so
+else
+NDK_SO_NAME = lib$(1)_emacs.so
+endif
+
+ifeq ($$(NDK_SO_NAMES:$$(NDK_SO_NAME)=),$$(NDK_SO_NAMES))
+NDK_SO_NAMES := $$(NDK_SO_NAMES) $$(NDK_SO_NAME)
+
+# Now recurse over this module's dependencies.
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), 
$$(NDK_$(1)_STATIC_LIBRARIES)),$$(eval $$(call add-a-name,$$(module))))
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), 
$$(NDK_$(1)_SHARED_LIBRARIES)),$$(eval $$(call add-so-name,$$(module))))
+endif
+endef
+
+# Resolve additional dependencies based on LOCAL_STATIC_LIBRARIES and
+# LOCAL_SHARED_LIBRARIES.
+
+SYSTEM_LIBRARIES = z libz
+
+$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), 
$(LOCAL_STATIC_LIBRARIES)),$(eval $(call add-a-name,$(module))))
+$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), 
$(LOCAL_SHARED_LIBRARIES)),$(eval $(call add-so-name,$(module))))
+
+$(info $(LOCAL_EXPORT_LDFLAGS) $(abspath $(addprefix 
$(NDK_BUILD_DIR)/,$(NDK_A_NAMES))) $(and $(NDK_SO_NAMES), -L$(abspath 
$(NDK_BUILD_DIR)) $(foreach soname,$(NDK_SO_NAMES),-l:$(soname))))
+$(info $(NDK_A_NAMES))
+$(info End)
diff --git a/build-aux/ndk-build-helper-3.mk b/build-aux/ndk-build-helper-3.mk
new file mode 100644
index 00000000000..4d0358d4f77
--- /dev/null
+++ b/build-aux/ndk-build-helper-3.mk
@@ -0,0 +1,28 @@
+# ndk-build-helper-3.mk -- Helper for ndk-build.m4.
+# Copyright (C) 2023 Free Software Foundation, Inc.
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+# Say a static library is being built
+build_kind = executable
+
+$(info Building $(build_kind))
+$(info $(LOCAL_MODULE))
+$(info $(addprefix $(ANDROID_MODULE_DIRECTORY),$(LOCAL_SRC_FILES) 
$(LOCAL_SRC_FILES$(EMACS_ABI))))
+
+$(info $(foreach dir,$(LOCAL_EXPORT_C_INCLUDE_DIRS) 
$(LOCAL_EXPORT_C_INCLUDES),-I$(dir)))
+$(info $(LOCAL_EXPORT_CFLAGS))
+$(info $(LOCAL_EXPORT_LDFLAGS))
+$(info End)
diff --git a/build-aux/ndk-build-helper-4.mk b/build-aux/ndk-build-helper-4.mk
new file mode 100644
index 00000000000..e1024f0a243
--- /dev/null
+++ b/build-aux/ndk-build-helper-4.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2023 Free Software Foundation, Inc.
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+undefine LOCAL_MODULE
+undefine LOCAL_MODULE_FILENAME
+undefine LOCAL_SRC_FILES
+undefine LOCAL_CPP_EXTENSION
+undefine LOCAL_CPP_FEATURES
+undefine LOCAL_C_INCLUDES
+undefine LOCAL_CFLAGS
+undefine LOCAL_CPPFLAGS
+undefine LOCAL_STATIC_LIBRARIES
+undefine LOCAL_SHARED_LIBRARIES
+undefine LOCAL_WHOLE_STATIC_LIBRARIES
+undefine LOCAL_LDLIBS
+undefine LOCAL_LDFLAGS
+undefine LOCAL_ALLOW_UNDEFINED_SYMBOLS
+undefine LOCAL_ARM_MODE
+undefine LOCAL_ARM_NEON
+undefine LOCAL_DISABLE_FORMAT_STRING_CHECKS
+undefine LOCAL_EXPORT_CFLAGS
+undefine LOCAL_EXPORT_CPPFLAGS
+undefine LOCAL_EXPORT_C_INCLUDES
+undefine LOCAL_EXPORT_LDFLAGS
+undefine LOCAL_EXPORT_LDLIBS
diff --git a/build-aux/ndk-build-helper.mk b/build-aux/ndk-build-helper.mk
new file mode 100644
index 00000000000..beb32cae3f1
--- /dev/null
+++ b/build-aux/ndk-build-helper.mk
@@ -0,0 +1,52 @@
+# ndk-build-helper.mk -- Helper for ndk-build.m4.
+# Copyright (C) 2023 Free Software Foundation, Inc.
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+# This Makefile sets up enough to parse an Android-style Android.mk
+# file and return useful information about its contents.
+
+# See the text under ``NDK BUILD SYSTEM IMPLEMENTATION'' in
+# INSTALL.android for more details.
+
+# Make NDK_BUILD_DIR absolute.
+NDK_BUILD_DIR := $(absname $(NDK_BUILD_DIR))
+
+# my-dir is a function that returns the Android module directory.
+my-dir = $(ANDROID_MODULE_DIRECTORY)
+
+# all-subdir-makefiles is a function which returns all Android.mk
+# files within this directory.
+all-subdir-makefiles = $(shell find . -name "Android.mk")
+
+# These functions are not implemented.
+parent-makefile =
+grand-parent-makefile =
+import-module =
+
+# Print out module information every time BUILD_SHARED_LIBRARY is
+# called.
+
+BUILD_SHARED_LIBRARY=$(EMACS_SRCDIR)/build-aux/ndk-build-helper-1.mk
+BUILD_STATIC_LIBRARY=$(EMACS_SRCDIR)/build-aux/ndk-build-helper-2.mk
+BUILD_EXECUTABLE=$(EMACS_SRCDIR)/build-aux/ndk-build-helper-3.mk
+CLEAR_VARS=$(EMACS_SRCDIR)/build-aux/ndk-build-helper-4.mk
+
+# Now include Android.mk.
+
+include $(ANDROID_MAKEFILE)
+
+# Dummy target.
+all:
diff --git a/build-aux/ndk-module-extract.awk b/build-aux/ndk-module-extract.awk
new file mode 100644
index 00000000000..eaea3e96dd8
--- /dev/null
+++ b/build-aux/ndk-module-extract.awk
@@ -0,0 +1,64 @@
+/^Building.+$/ {
+  kind = $2
+}
+
+// {
+  if (!match ($0, /^End$/) && !match ($0, /^Building.+$/))
+    {
+      if (kind)
+       {
+         if (ldflags_found)
+           target = $0
+         else if (cflags_found)
+           {
+             ldflags = $0
+             ldflags_found = 1
+           }
+         else if (includes_found)
+           {
+             cflags = $0
+             cflags_found = 1
+           }
+         else if (src_found)
+           {
+             includes = $0
+             includes_found = 1
+           }
+         else if (name_found)
+           {
+             src = $0
+             src_found = 1;
+           }
+         else
+           {
+             name = $0
+             name_found = 1
+           }
+       }
+    }
+}
+
+/^End$/ {
+  if (name == MODULE && (kind == "shared" || kind == "static"))
+    {
+      printf "module_name=%s\n", name
+      printf "module_kind=%s\n", kind
+      printf "module_src=\"%s\"\n", src
+      printf "module_includes=\"%s\"\n", includes
+      printf "module_cflags=\"%s\"\n", cflags
+      printf "module_ldflags=\"%s\"\n", ldflags
+      printf "module_target=\"%s\"\n", target
+    }
+
+  src = ""
+  name = ""
+  kind = ""
+  includes = ""
+  cflags = ""
+  ldflags = ""
+  name_found = ""
+  src_found = ""
+  includes_found = ""
+  cflags_found = ""
+  ldflags_found = ""
+}
diff --git a/configure.ac b/configure.ac
index bdcce8d8694..9df4a6a073f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -166,6 +166,13 @@ fi
 AC_CANONICAL_HOST
 AC_CANONICAL_BUILD
 
+if test "$XCONFIGURE" = "android"; then
+  # Initialize the Android NDK build system.  Make sure to use the
+  # passed through NDK path.
+  with_ndk_path="$android_ndk_path"
+  ndk_INIT([$android_abi], [$ANDROID_SDK], [cross/ndk-build])
+fi
+
 case $host in
  *-mingw*)
 
@@ -544,6 +551,7 @@ OPTION_DEFAULT_OFF([cygwin32-native-compilation],[use 
native compilation on 32-b
 OPTION_DEFAULT_ON([xinput2],[don't use version 2 of the X Input Extension for 
input])
 OPTION_DEFAULT_OFF([small-ja-dic],[generate a smaller-size Japanese 
dictionary])
 OPTION_DEFAULT_OFF([android],[cross-compile Android application package])
+OPTION_DEFAULT_ON([android-debug],[don't build Emacs as a debug package on 
Android])
 
 AC_ARG_WITH([file-notification],[AS_HELP_STRING([--with-file-notification=LIB],
  [use a file notification library (LIB one of: yes, inotify, kqueue, gfile, 
w32, no)])],
@@ -1033,8 +1041,14 @@ package will likely install on older systems but crash 
on startup.])
   mv -f confdefs.h _confdefs.h
   mv -f config.log _config.log
 
+  # Figure out what --with-FOO options to pass through.
+  passthrough="$passthrough --with-png=$with_png"
+  passthrough="$passthrough --with-webp=$with_webp"
+  passthrough="$passthrough --with-gif=$with_gif"
+
   AS_IF([XCONFIGURE=android ANDROID_CC="$ANDROID_CC" \
-         ANDROID_SDK="$android_sdk" $0], [],
+         ANDROID_SDK="$android_sdk" android_abi=$android_abi \
+        android_ndk_path="$with_ndk_path" $0 $passthrough], [],
     [AC_MSG_ERROR([Failed to cross-configure Emacs for android.])])
 
   # Now set ANDROID to yes.
@@ -1048,6 +1062,14 @@ package will likely install on older systems but crash 
on startup.])
   AC_MSG_NOTICE([Generating src/config.h.android])
   mv -f src/config.h src/config.h.android
 
+  # Tell AndroidManifest.xml whether or not Emacs should be built
+  # debug.
+  ANDROID_DEBUGGABLE=false
+  if test "$with_android_debug" = "yes"; then
+    ANDROID_DEBUGGABLE=true
+  fi
+  AC_SUBST([ANDROID_DEBUGGABLE])
+
   # Move confdefs.h back now that the recursive call to configure is
   # complete.
   mv -f _confdefs.h confdefs.h
@@ -1083,15 +1105,21 @@ if test "$ANDROID" = "yes"; then
   with_xpm=no
   with_jpeg=no
   with_tiff=no
-  with_gif=no
-  with_png=no
+
+  # Some of these dependencies are now supported within Android, so
+  # they can be enabled.
+  if test "$XCONFIGURE" != "android"; then
+    with_png=no
+    with_webp=no
+    with_gif=no
+  fi
+
+  with_xml2=no
   with_rsvg=no
-  with_webp=no
   with_sqlite3=no
   with_lcms2=no
   with_libsystemd=no
   with_cairo=no
-  with_xml2=no
   with_imagemagick=no
   with_json=no
   with_tree_sitter=no
@@ -2218,10 +2246,13 @@ dnl EMACS_CHECK_MODULES accepts optional 3rd and 4th 
arguments that
 dnl can take the place of the default HAVE_GSTUFF=yes and HAVE_GSTUFF=no
 dnl actions.
 AC_DEFUN([EMACS_CHECK_MODULES],
-  [PKG_CHECK_MODULES([$1], [$2],
-     [$1_CFLAGS=`AS_ECHO(["$$1_CFLAGS"]) | sed -e "$edit_cflags"`
-      m4_default([$3], [HAVE_$1=yes])],
-     [m4_default([$4], [HAVE_$1=no])])])
+  [AS_IF([test -n "$ndk_INITIALIZED"],
+    [ndk_CHECK_MODULES([$1], [$2], m4_default([$3], [HAVE_$1=yes]),
+      m4_default([$4],[HAVE_$1=no]))],
+    [PKG_CHECK_MODULES([$1], [$2],
+       [$1_CFLAGS=`AS_ECHO(["$$1_CFLAGS"]) | sed -e "$edit_cflags"`
+        m4_default([$3], [HAVE_$1=yes])],
+       [m4_default([$4], [HAVE_$1=no])])])])
 
 HAVE_SOUND=no
 if test "${with_sound}" != "no"; then
@@ -2394,6 +2425,7 @@ window_system=none
 ANDROID_OBJ=
 ANDROID_LIBS=
 ANDROID_CFLAGS=
+REALLY_ANDROID=
 CM_OBJ="cm.o"
 
 if test "${ANDROID}" = "yes"; then
@@ -2423,18 +2455,31 @@ for Android, but all API calls need to be stubbed out])
     # Link with libraries required for Android support.
     ANDROID_LIBS="-landroid -llog -ljnigraphics"
 
+    # This is required to make the system load emacs.apk's libpng
+    # (among others) instead of the system's own.  But it doesn't work
+    # on all Android versions yet, so for now just suffix shared
+    # libraries with _emacs.
+    # ANDROID_LDFLAGS="-Wl,-rpath,'\$\$ORIGIN'"
+
     # Link with the sfnt font library and sfntfont.o, along with
     # sfntfont-android.o.
     ANDROID_OBJ="$ANDROID_OBJ sfnt.o sfntfont.o sfntfont-android.o"
 
+    # Build androidselect.o.
+    ANDROID_OBJ="$ANDROID_OBJ androidselect.o"
+
     # Check for some functions not always present in the NDK.
     AC_CHECK_DECLS([android_get_device_api_level])
+
+    # Say this build is really for Android.
+    REALLY_ANDROID=yes
   fi
 fi
 
 AC_SUBST(ANDROID)
 AC_SUBST(ANDROID_OBJ)
 AC_SUBST(ANDROID_LIBS)
+AC_SUBST(ANDROID_LDFLAGS)
 AC_SUBST(ANDROID_CFLAGS)
 
 if test "${with_pgtk}" = "yes"; then
@@ -3265,7 +3310,8 @@ HAVE_WEBP=no
 if test "${with_webp}" != "no"; then
    if test "${HAVE_X11}" = "yes" || test "${opsys}" = "mingw32" \
    || test "${HAVE_W32}" = "yes" || test "${HAVE_NS}" = "yes" \
-   || test "${HAVE_BE_APP}" = "yes" || test "${HAVE_PGTK}" = "yes"; then
+   || test "${HAVE_BE_APP}" = "yes" || test "${HAVE_PGTK}" = "yes" \
+   || test "${REALLY_ANDROID}" = "yes"; then
       WEBP_REQUIRED=0.6.0
       WEBP_MODULE="libwebpdemux >= $WEBP_REQUIRED"
 
@@ -4506,7 +4552,8 @@ HAVE_JPEG=no
 LIBJPEG=
 if test "${HAVE_X11}" = "yes" || test "${HAVE_W32}" = "yes" \
    || test "${HAVE_NS}" = "yes" || test "${HAVE_BE_APP}" = "yes" \
-   || test "$window_system" = "pgtk"; then
+   || test "$window_system" = "pgtk" \
+   || test "${REALLY_ANDROID}" = "yes"; then
   if test "${with_jpeg}" != "no"; then
     AC_CACHE_CHECK([for jpeglib 6b or later],
       [emacs_cv_jpeglib],
@@ -4852,7 +4899,8 @@ if test "${with_png}" != no; then
     AC_CHECK_HEADER([png.h], [HAVE_PNG=yes])
   elif test "${HAVE_X11}" = "yes" || test "${HAVE_W32}" = "yes" \
        || test "${HAVE_NS}" = "yes" || test "${HAVE_BE_APP}" = "yes" \
-       || test "$window_system" = "pgtk"; then
+       || test "$window_system" = "pgtk" \
+       || test "${REALLY_ANDROID}" = "yes"; then
     EMACS_CHECK_MODULES([PNG], [libpng >= 1.0.0])
     if test $HAVE_PNG = yes; then
       LIBPNG=$PNG_LIBS
@@ -4951,6 +4999,7 @@ AC_SUBST([LIBTIFF])
 ### Use -lgif or -lungif if available, unless '--with-gif=no'.
 ### mingw32 doesn't use -lgif/-lungif, since it loads the library dynamically.
 HAVE_GIF=no
+GIF_CFLAGS=
 LIBGIF=
 if test "${opsys}" = "mingw32"; then
   if test "${with_gif}" != "no"; then
@@ -4963,6 +5012,7 @@ if test "${opsys}" = "mingw32"; then
 elif test "${HAVE_X11}" = "yes" && test "${with_gif}" != "no" \
         || test "${HAVE_W32}" = "yes" || test "${HAVE_NS}" = "yes" \
        || test "${HAVE_BE_APP}" = "yes" || test "$window_system" = "pgtk" \
+       || test "${REALLY_ANDROID}" = "yes" \
        && test "${with_gif}" != "no"; then
   AC_CHECK_HEADER([gif_lib.h],
 # EGifPutExtensionLast only exists from version libungif-4.1.0b1.
@@ -4982,12 +5032,20 @@ elif test "${HAVE_X11}" = "yes" && test "${with_gif}" 
!= "no" \
     test "$HAVE_GIF" = yes && LIBGIF=-lungif
   fi
 
+# Finally, try ndk-build on Android.
+  if test "$REALLY_ANDROID" = "yes"; then
+    ndk_SEARCH_MODULE([libgif], [GIF], [HAVE_GIF=yes],
+      [HAVE_GIF=no])
+    test "$HAVE_GIF" = yes && LIBGIF="$GIF_LIBS"
+  fi
+
   if test "${HAVE_GIF}" = "yes"; then
     AC_DEFINE([HAVE_GIF], [1],
       [Define to 1 if you have a gif (or ungif) library.])
   fi
 fi
 AC_SUBST([LIBGIF])
+AC_SUBST([GIF_CFLAGS])
 
 dnl Check for required libraries.
 MISSING=
@@ -7291,7 +7349,7 @@ if test "$ANDROID" = "yes"; then
 fi
 
 if test "$XCOMPILE" = "yes"; then
-  SUBDIR_MAKEFILES="$SUBDIR_MAKEFILES xcompile/Makefile"
+  SUBDIR_MAKEFILES="$SUBDIR_MAKEFILES cross/Makefile"
 fi
 
 dnl The admin/ directory used to be excluded from tarfiles.
@@ -7377,11 +7435,15 @@ fi
 
 # Make java/Makefile
 ARCH_INDEPENDENT_CONFIG_FILES([java/Makefile])
-ARCH_INDEPENDENT_CONFIG_FILES([xcompile/Makefile])
+ARCH_INDEPENDENT_CONFIG_FILES([cross/Makefile])
 
 # Make java/AndroidManifest.xml
 ARCH_INDEPENDENT_CONFIG_FILES([java/AndroidManifest.xml])
 
+# Make ndk-build Makefiles.  This is only done inside the recursive
+# configure.
+ndk_CONFIG_FILES
+
 AC_OUTPUT
 
 if test ! "$with_mailutils"; then
diff --git a/cross/ndk-build/Makefile.in b/cross/ndk-build/Makefile.in
new file mode 100644
index 00000000000..ec8e6205680
--- /dev/null
+++ b/cross/ndk-build/Makefile.in
@@ -0,0 +1,115 @@
+### @configure_input@
+
+# Copyright 2023 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+# ndk-build works by including a bunch of Makefiles which set
+# variables, and then having those Makefiles include another makefile
+# which actually builds targets.
+
+              srcdir = @srcdir@
+
+# This is a list of Android.mk files which provide targets.
+NDK_BUILD_ANDROID_MK = @NDK_BUILD_ANDROID_MK@
+      NDK_BUILD_ARCH = @NDK_BUILD_ARCH@
+       NDK_BUILD_ABI = @NDK_BUILD_ABI@
+       NDK_BUILD_SDK = @NDK_BUILD_SDK@
+        NDK_BUILD_CC = @NDK_BUILD_CC@
+        NDK_BUILD_AR = @NDK_BUILD_AR@
+
+# This is a list of targets to build.
+   NDK_BUILD_MODULES = @NDK_BUILD_MODULES@
+
+# This is set by the Android in tree build system and is used by some
+# libraries to look for the NDK.  Its value is unimportant.
+           NDK_ROOT = /tmp/
+
+# Finally, here are rules common to Emacs.
+.PHONY: all
+all: $(NDK_BUILD_MODULES)
+
+define uniqify
+$(if $1,$(firstword $1) $(call uniqify,$(filter-out $(firstword $1),$1)))
+endef
+
+# Remove duplicate files.
+NDK_BUILD_ANDROID_MK := $(call uniqify,$(NDK_BUILD_ANDROID_MK))
+
+define subr-1
+
+# Define ndk-build functions.
+
+define my-dir
+$(dir $(abspath $(1)))
+endef
+
+# NDK-defined include variables.
+
+CLEAR_VARS = $(srcdir)/ndk-clear-vars.mk
+BUILD_EXECUTABLE = $(srcdir)/ndk-build-executable.mk
+BUILD_SHARED_LIBRARY = $(srcdir)/ndk-build-shared-library.mk
+BUILD_STATIC_LIBRARY = $(srcdir)/ndk-build-static-library.mk
+PREBUILT_SHARED_LIBRARY = $(srcdir)/ndk-prebuilt-shared-library.mk
+PREBUILT_STATIC_LIBRARY = $(srcdir)/ndk-prebuilt-static-library.mk
+
+# Target information variables.
+
+TARGET_ARCH = $(NDK_BUILD_ARCH)
+TARGET_PLATFORM = android-$(NDK_BUILD_SDK)
+TARGET_ARCH_ABI = $(NDK_BUILD_ABI)
+TARGET_ABI = $(TARGET_PLATFORM)-$(TARGET_ABI)
+
+# Module description variables.  These are defined by Android.mk.
+LOCAL_PATH :=
+LOCAL_MODULE :=
+LOCAL_MODULE_FILENAME :=
+LOCAL_SRC_FILES :=
+LOCAL_CPP_EXTENSION :=
+LOCAL_CPP_FEATURES :=
+LOCAL_C_INCLUDES :=
+LOCAL_CFLAGS :=
+LOCAL_CPPFLAGS :=
+LOCAL_STATIC_LIBRARIES :=
+LOCAL_SHARED_LIBRARIES :=
+LOCAL_WHOLE_STATIC_LIBRARIES :=
+LOCAL_LDLIBS :=
+LOCAL_LDFLAGS :=
+LOCAL_ALLOW_UNDEFINED_SYMBOLS :=
+LOCAL_ARM_MODE :=
+LOCAL_ARM_NEON :=
+LOCAL_DISABLE_FORMAT_STRING_CHECKS :=
+LOCAL_EXPORT_CFLAGS :=
+LOCAL_EXPORT_CPPFLAGS :=
+LOCAL_EXPORT_C_INCLUDES :=
+LOCAL_EXPORT_LDFLAGS :=
+LOCAL_EXPORT_LDLIBS :=
+
+# Now load Android.mk.
+include $(1)
+
+endef
+
+# Now define rules for each Android.mk file.
+$(foreach android_mk,$(NDK_BUILD_ANDROID_MK),$(eval $(call 
subr-1,$(android_mk))))
+
+.PHONY: clean mostlyclean
+clean mostlyclean:
+       rm -rf *.o *.so *.a
+
+.PHONY: extraclean dist-clean maintainer-clean
+extraclean dist-clean maintainer-clean:
+       rm -rf Makefile
diff --git a/cross/ndk-build/ndk-build-executable.mk 
b/cross/ndk-build/ndk-build-executable.mk
new file mode 100644
index 00000000000..9591c862b18
--- /dev/null
+++ b/cross/ndk-build/ndk-build-executable.mk
@@ -0,0 +1,22 @@
+# Copyright 2023 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+# ndk-build works by including a bunch of Makefiles which set
+# variables, and then having those Makefiles include another makefile
+# which actually builds targets.
+
+# Building executables is not supported
diff --git a/cross/ndk-build/ndk-build-shared-library.mk 
b/cross/ndk-build/ndk-build-shared-library.mk
new file mode 100644
index 00000000000..0d90d89e55d
--- /dev/null
+++ b/cross/ndk-build/ndk-build-shared-library.mk
@@ -0,0 +1,83 @@
+# Copyright 2023 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+# ndk-build works by including a bunch of Makefiles which set
+# variables, and then having those Makefiles include another makefile
+# which actually builds targets.
+
+eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
+objname = $(1)-$(subst /,_,$(2).o)
+
+define single-object-target
+
+ifeq (x$(suffix $(1)),x.c)
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(LOCAL_PATH)/$(1)
+       $(NDK_BUILD_CC) -c $$< -o $$@ $(NDK_CFLAGS_$(LOCAL_MODULE))
+
+else
+ifneq ($(or $(call eq,x$(suffix $(1)),x.s),$(call eq,x$(suffix $(1)),x.S)),)
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(LOCAL_PATH)/$(1)
+       $(NDK_BUILD_CC) -c $$< -o $$@ $(NDK_ASFLAGS_$(LOCAL_MODULE))
+
+else
+$$(error Unsupported suffix: $(suffix $(1)))
+endif
+endif
+
+ALL_OBJECT_FILES$(LOCAL_MODULE) += $(call objname,$(LOCAL_MODULE),$(basename 
$(1)))
+
+endef
+
+NDK_CFLAGS_$(LOCAL_MODULE)      := $(addprefix -I,$(addprefix 
$(LOCAL_PATH),$(LOCAL_C_INCLUDES)))
+NDK_CFLAGS_$(LOCAL_MODULE)      ::= -fPIC -iquote $(LOCAL_EXPORT_CFLAGS) 
$(LOCAL_PATH) $(LOCAL_CFLAGS)
+NDK_LDFLAGS_$(LOCAL_MODULE)     := $(LOCAL_LDLIBS)
+NDK_LDFLAGS_$(LOCAL_MODULE)     := $(LOCAL_LDFLAGS)
+ALL_OBJECT_FILES_$(LOCAL_MODULE) :=
+
+ifeq ($(NDK_BUILD_ARCH)$(NDK_ARM_MODE),armarm)
+NDK_CFLAGS ::= -marm
+else
+ifeq ($(NDK_BUILD_ARCH),arm)
+NDK_CFLAGS ::= -mthumb
+endif
+endif
+
+LOCAL_MODULE_FILENAME := $(strip $(LOCAL_MODULE_FILENAME))
+
+ifndef LOCAL_MODULE_FILENAME
+ifeq ($(findstring lib,$(LOCAL_MODULE)),lib)
+LOCAL_MODULE_FILENAME := $(LOCAL_MODULE)_emacs
+else
+LOCAL_MODULE_FILENAME := lib$(LOCAL_MODULE)_emacs
+endif
+endif
+
+# Since a shared library is being built, suffix the library with
+# _emacs.  Otherwise, libraries already on the system will be found
+# first, with potentially nasty consequences.
+
+LOCAL_MODULE_FILENAME := $(LOCAL_MODULE_FILENAME).so
+
+# Then define rules to build all objects.
+ALL_SOURCE_FILES = $(LOCAL_SRC_FILES)
+$(foreach source,$(ALL_SOURCE_FILES),$(eval $(call 
single-object-target,$(source))))
+
+# Now define the rule to build the shared library.
+$(LOCAL_MODULE_FILENAME): $(ALL_OBJECT_FILES$(LOCAL_MODULE))
+       $(NDK_BUILD_CC) $^ -o $@ -shared $(NDK_LDFLAGS$(LOCAL_MODULE))
diff --git a/cross/ndk-build/ndk-build-static-library.mk 
b/cross/ndk-build/ndk-build-static-library.mk
new file mode 100644
index 00000000000..1423cf243cf
--- /dev/null
+++ b/cross/ndk-build/ndk-build-static-library.mk
@@ -0,0 +1,83 @@
+# Copyright 2023 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+# ndk-build works by including a bunch of Makefiles which set
+# variables, and then having those Makefiles include another makefile
+# which actually builds targets.
+
+eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
+objname = $(1)-$(subst /,_,$(2).o)
+
+define single-object-target
+
+ifeq (x$(suffix $(1)),x.c)
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(LOCAL_PATH)/$(1)
+       $(NDK_BUILD_CC) -c $$< -o $$@ $(NDK_CFLAGS_$(LOCAL_MODULE))
+
+else
+ifneq ($(or $(call eq,x$(suffix $(1)),x.s),$(call eq,x$(suffix $(1)),x.S)),)
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(LOCAL_PATH)/$(1)
+       $(NDK_BUILD_CC) -c $$< -o $$@ $(NDK_ASFLAGS_$(LOCAL_MODULE))
+
+else
+$$(error Unsupported suffix: $(suffix $(1)))
+endif
+endif
+
+ALL_OBJECT_FILES$(LOCAL_MODULE) += $(call objname,$(LOCAL_MODULE),$(basename 
$(1)))
+endef
+
+NDK_CFLAGS_$(LOCAL_MODULE)  := $(addprefix -I,$(addprefix 
$(LOCAL_PATH),$(LOCAL_C_INCLUDES)))
+NDK_CFLAGS_$(LOCAL_MODULE) ::= -iquote $(LOCAL_PATH) $(LOCAL_EXPORT_CFLAGS) 
$(LOCAL_CFLAGS) $(LOCAL_CFLAGS_$(NDK_BUILD_ARCH))
+NDK_ASFLAGS_$(LOCAL_MODULE) ::= $(LOCAL_ASFLAGS) 
$(LOCAL_ASFLAGS_$(NDK_BUILD_ARCH))
+NDK_LDFLAGS_$(LOCAL_MODULE) := $(LOCAL_LDLIBS)
+NDK_LDFLAGS_$(LOCAL_MODULE) := $(LOCAL_LDFLAGS)
+ALL_OBJECT_FILES$(LOCAL_MODULE) :=
+
+ifeq ($(NDK_BUILD_ARCH)$(NDK_ARM_MODE),armarm)
+NDK_CFLAGS ::= -marm
+else
+ifeq ($(NDK_BUILD_ARCH),arm)
+NDK_CFLAGS ::= -mthumb
+endif
+endif
+
+LOCAL_MODULE_FILENAME := $(strip $(LOCAL_MODULE_FILENAME))
+
+ifndef LOCAL_MODULE_FILENAME
+ifeq ($(findstring lib,$(LOCAL_MODULE)),lib)
+LOCAL_MODULE_FILENAME := $(LOCAL_MODULE)
+else
+LOCAL_MODULE_FILENAME := lib$(LOCAL_MODULE)
+endif
+endif
+
+LOCAL_MODULE_FILENAME := $(LOCAL_MODULE_FILENAME).a
+
+# Then define rules to build all objects.
+ALL_SOURCE_FILES = $(LOCAL_SRC_FILES) $(LOCAL_SRC_FILES_$(NDK_BUILD_ARCH))
+
+# This defines all dependencies.
+ALL_OBJECT_FILES$(LOCAL_MODULE) =
+
+$(foreach source,$(ALL_SOURCE_FILES),$(eval $(call 
single-object-target,$(source))))
+
+# Now define the rule to build the library.
+$(LOCAL_MODULE_FILENAME): $(ALL_OBJECT_FILES$(LOCAL_MODULE))
+       $(NDK_BUILD_AR) r $@ $^
diff --git a/cross/ndk-build/ndk-build.in b/cross/ndk-build/ndk-build.in
new file mode 100644
index 00000000000..8586b90d840
--- /dev/null
+++ b/cross/ndk-build/ndk-build.in
@@ -0,0 +1,44 @@
+### @configure_input@
+
+# Copyright (C) 2023 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+# This file is included all over the place to build prerequisites.
+
+NDK_BUILD_MODULES = @NDK_BUILD_MODULES@
+NDK_BUILD_SHARED =
+NDK_BUILD_STATIC =
+
+define subr-1
+
+.PHONY $(top_builddir)/cross/ndk-build/$(0)
+$(top_builddir)/cross/ndk-build/$(0):
+       $(MAKE) -C $(top_builddir)/cross/ndk-build $(0)
+
+ifeq ($(suffix $(0)),.so)
+NDK_BUILD_SHARED += $(top_builddir)/cross/ndk-build/$(0)
+else
+ifeq ($(suffix $(0)),.a)
+NDK_BUILD_STATIC += $(top_builddir)/cross/ndk-build/$(0)
+endif
+endif
+
+endef
+
+# Generate rules for each module.
+
+$(foreach module,$(NDK_BUILD_MODULES),$(eval $(call subr-1,$(module))))
diff --git a/cross/ndk-build/ndk-build.mk.in b/cross/ndk-build/ndk-build.mk.in
new file mode 100644
index 00000000000..6ad577ccc35
--- /dev/null
+++ b/cross/ndk-build/ndk-build.mk.in
@@ -0,0 +1,43 @@
+### @configure_input@
+
+# Copyright (C) 2023 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+# This file is included all over the place to build prerequisites.
+
+NDK_BUILD_MODULES = @NDK_BUILD_MODULES@
+NDK_BUILD_SHARED =
+NDK_BUILD_STATIC =
+
+define subr-1
+
+$(top_builddir)/cross/ndk-build/$(1):
+       $(MAKE) -C $(top_builddir)/cross/ndk-build $(1)
+
+ifeq ($(suffix $(1)),.so)
+NDK_BUILD_SHARED += $(top_builddir)/cross/ndk-build/$(1)
+else
+ifeq ($(suffix $(1)),.a)
+NDK_BUILD_STATIC += $(top_builddir)/cross/ndk-build/$(1)
+endif
+endif
+
+endef
+
+# Generate rules for each module.
+
+$(foreach module,$(NDK_BUILD_MODULES),$(eval $(call subr-1,$(module))))
diff --git a/cross/ndk-build/ndk-clear-vars.mk 
b/cross/ndk-build/ndk-clear-vars.mk
new file mode 100644
index 00000000000..df2bf95fd5e
--- /dev/null
+++ b/cross/ndk-build/ndk-clear-vars.mk
@@ -0,0 +1,47 @@
+# Copyright 2023 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+# ndk-build works by including a bunch of Makefiles which set
+# variables, and then having those Makefiles include another makefile
+# which actually builds targets.
+
+undefine LOCAL_MODULE
+undefine LOCAL_MODULE_FILENAME
+undefine LOCAL_SRC_FILES
+undefine LOCAL_CPP_EXTENSION
+undefine LOCAL_CPP_FEATURES
+undefine LOCAL_C_INCLUDES
+undefine LOCAL_CFLAGS
+undefine LOCAL_CPPFLAGS
+undefine LOCAL_STATIC_LIBRARIES
+undefine LOCAL_SHARED_LIBRARIES
+undefine LOCAL_WHOLE_STATIC_LIBRARIES
+undefine LOCAL_LDLIBS
+undefine LOCAL_LDFLAGS
+undefine LOCAL_ALLOW_UNDEFINED_SYMBOLS
+undefine LOCAL_ARM_MODE
+undefine LOCAL_ARM_NEON
+undefine LOCAL_DISABLE_FORMAT_STRING_CHECKS
+undefine LOCAL_EXPORT_CFLAGS
+undefine LOCAL_EXPORT_CPPFLAGS
+undefine LOCAL_EXPORT_C_INCLUDES
+undefine LOCAL_EXPORT_LDFLAGS
+undefine LOCAL_EXPORT_LDLIBS
+
+undefine LOCAL_SRC_FILES_$(NDK_BUILD_ARCH)
+undefine LOCAL_ASFLAGS_$(NDK_BUILD_ARCH)
+undefine LOCAL_CFLAGS_$(NDK_BUILD_ARCH)
diff --git a/cross/ndk-build/ndk-prebuilt-shared-library.mk 
b/cross/ndk-build/ndk-prebuilt-shared-library.mk
new file mode 100644
index 00000000000..2a8260f9851
--- /dev/null
+++ b/cross/ndk-build/ndk-prebuilt-shared-library.mk
@@ -0,0 +1,24 @@
+### @configure_input@
+
+# Copyright 2023 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+# ndk-build works by including a bunch of Makefiles which set
+# variables, and then having those Makefiles include another makefile
+# which actually builds targets.
+
+$(warn Prebuilt shared libraries are not supported)
diff --git a/cross/ndk-build/ndk-prebuilt-static-library.mk 
b/cross/ndk-build/ndk-prebuilt-static-library.mk
new file mode 100644
index 00000000000..9230f690bb1
--- /dev/null
+++ b/cross/ndk-build/ndk-prebuilt-static-library.mk
@@ -0,0 +1,24 @@
+### @configure_input@
+
+# Copyright 2023 Free Software Foundation, Inc.
+
+# This file is part of GNU Emacs.
+
+# GNU Emacs is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU Emacs is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+# ndk-build works by including a bunch of Makefiles which set
+# variables, and then having those Makefiles include another makefile
+# which actually builds targets.
+
+$(warn Prebuilt static libraries are not supported)
diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi
index 223e9dbf8ef..e4498da401d 100644
--- a/doc/emacs/android.texi
+++ b/doc/emacs/android.texi
@@ -18,6 +18,7 @@ about using such devices with Emacs, @pxref{Other Input 
Devices}.
 * Android Startup::     Starting up Emacs on Android.
 * Android File System:: The Android file system.
 * Android Environment:: Running Emacs under Android.
+* Android Windowing::   The Android window system.
 * Android Fonts::      Font selection under Android.
 @end menu
 
@@ -299,7 +300,8 @@ settings application.  Consult the manufacturer of your 
device for
 more details, as how to do this varies by device.
 @end itemize
 
-@section Android windowing
+@node Android Windowing
+@section The Android window system
 
   Android has an unusual window system; there, all windows are
 maximized or full-screen, and only one window can be displayed at a
@@ -372,6 +374,31 @@ The @code{fullscreen} frame parameter is always 
@code{maximized} for
 top-level frames.
 @end itemize
 
+@cindex selections, android
+@cindex android clipboard
+  Emacs does not implement all selection related features supported
+under the X Window System on Android.  For example, only the
+@code{CLIPBOARD} and @code{PRIMARY} selections (@pxref{Cut and Paste})
+are supported, and plain text is the only supported data type.
+
+  In addition, the Android system itself places certain restrictions
+on what selection data Emacs can access:
+
+@itemize @bullet
+@item
+On Android 2.3 and earlier, the function @code{gui-selection-owner-p}
+always returns @code{nil} for the clipboard selection.
+
+@item
+On Android 3.0 and later, Emacs can only access clipboard data when
+one of its frames has the input focus.
+@end itemize
+
+  Since the Android system itself has no concept of a primary
+selection, Emacs provides an emulation instead.  This means there is
+no way to transfer the contents of the primary selection to another
+application via cut-and-paste.
+
 @node Android Fonts
 @section Font backends and selection under Android
 @cindex fonts, android
diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi
index 0cb454e5294..30758efb43a 100644
--- a/doc/emacs/emacs.texi
+++ b/doc/emacs/emacs.texi
@@ -1264,6 +1264,7 @@ Emacs and Android
 * Android Startup::     Starting up Emacs on Android.
 * Android File System:: The Android file system.
 * Android Environment:: Running Emacs under Android.
+* Android Windowing::   The Android window system.
 * Android Fonts::      Font selection under Android.
 
 Emacs and unconventional input devices
diff --git a/etc/MACHINES b/etc/MACHINES
index 8c6f3f48ce7..8ba0b1faa77 100644
--- a/etc/MACHINES
+++ b/etc/MACHINES
@@ -131,6 +131,14 @@ the list at the end of this file.
   The earliest release of Haiku that will successfully compile Emacs
   is R1/Beta2.  For windowing support, R1/Beta3 or later is required.
 
+** Android
+
+  Emacs is known to run on all Android versions from 2.3 onwards.
+  It should work with Android 2.2 as well, but only the build
+  has been tested, and actually running the built Emacs has not.
+
+  See the file INSTALL.android for detailed installation instructions.
+
 
 * Obsolete platforms
 
diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in
index 74f69d2a8e5..c4a9d1f5177 100644
--- a/java/AndroidManifest.xml.in
+++ b/java/AndroidManifest.xml.in
@@ -60,12 +60,13 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>. -->
               android:hardwareAccelerated="true"
               android:supportsRtl="true"
               android:theme="@android:style/Theme"
-              android:debuggable="true"
+              android:debuggable="@ANDROID_DEBUGGABLE@"
               android:extractNativeLibs="true">
 
     <activity android:name="org.gnu.emacs.EmacsActivity"
              android:launchMode="singleTop"
              android:windowSoftInputMode="adjustResize"
+             android:exported="true"
              
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
       <intent-filter>
         <action android:name="android.intent.action.MAIN" />
@@ -76,10 +77,12 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>. -->
 
     <activity android:name="org.gnu.emacs.EmacsMultitaskActivity"
              android:windowSoftInputMode="adjustResize"
+             android:exported="true"
              
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"/>
 
     <activity android:autoRemoveFromRecents="true"
               android:label="Emacs options"
+             android:exported="true"
               android:name=".EmacsPreferencesActivity">
       <intent-filter>
         <action android:name="android.intent.action.APPLICATION_PREFERENCES" />
diff --git a/java/Makefile.in b/java/Makefile.in
index 22c912fdce5..d27775ea3db 100644
--- a/java/Makefile.in
+++ b/java/Makefile.in
@@ -81,21 +81,24 @@ APK_NAME = 
emacs-$(version)-$(ANDROID_MIN_SDK)-$(ANDROID_ABI).apk
 all: $(APK_NAME)
 
 # Binaries to cross-compile.
-CROSS_BINS = ../xcompile/src/android-emacs ../xcompile/lib-src/ctags   \
-            ../xcompile/lib-src/hexl ../xcompile/lib-src/movemail      \
-            ../xcompile/lib-src/ebrowse ../xcompile/lib-src/emacsclient
+CROSS_BINS = ../cross/src/android-emacs ../cross/lib-src/ctags \
+            ../cross/lib-src/hexl ../cross/lib-src/movemail    \
+            ../cross/lib-src/ebrowse ../cross/lib-src/emacsclient
 
 # Libraries to cross-compile.
-CROSS_LIBS = ../xcompile/src/libemacs.so
+CROSS_LIBS = ../cross/src/libemacs.so
+
+# Third party libraries to compile.
+include $(top_builddir)/cross/ndk-build/ndk-build.mk
 
 .PHONY: $(CROSS_BINS) $(CROSS_LIBS)
 
-../xcompile/src/android-emacs ../xcompile/src/libemacs.so:
-       make -C ../xcompile src/$(notdir $@)
+../cross/src/android-emacs ../cross/src/libemacs.so:
+       make -C ../cross src/$(notdir $@)
 
-../xcompile/lib-src/hexl ../xcompile/lib-src/movemail \
-../xcompile/lib-src/ctags ../xcompile/lib-src/ebrowse &:
-       make -C ../xcompile lib-src/$(notdir $@)
+../cross/lib-src/hexl ../cross/lib-src/movemail \
+../cross/lib-src/ctags ../cross/lib-src/ebrowse &:
+       make -C ../cross lib-src/$(notdir $@)
 
 # This is needed to generate the ``.directory-tree'' file used by the
 # Android emulations of readdir and faccessat.
@@ -104,7 +107,7 @@ $(libsrc)/asset-directory-tool:
        $(MAKE) -C $(libsrc) $(notdir $@)
 
 emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) $(libsrc)/asset-directory-tool \
-  AndroidManifest.xml
+  AndroidManifest.xml $(NDK_BUILD_SHARED)
 # Make the working directory for this stuff
        rm -rf install_temp
        mkdir -p install_temp/lib/$(ANDROID_ABI)
@@ -145,11 +148,13 @@ emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) 
$(libsrc)/asset-directory-tool \
            cp -f $$file install_temp/lib/$(ANDROID_ABI);       \
          fi                                                    \
        done
+       $(foreach module,$(NDK_BUILD_SHARED), \
+         cp -f $(module) install_temp/lib/$(ANDROID_ABI))
 # Package everything.  Specifying the assets on this command line is
 # necessary for AAssetManager_getNextFileName to work on old versions
 # of Android.
-       $(AAPT) package -I "$(ANDROID_JAR)" -F $@ -f -M AndroidManifest.xml \
-         -A install_temp/assets
+       $(AAPT) package -I "$(ANDROID_JAR)" -F $@ -f \
+         -M AndroidManifest.xml -A install_temp/assets
        pushd install_temp; $(AAPT) add ../$@ `find lib -type f`; popd
        rm -rf install_temp
 
@@ -196,3 +201,4 @@ clean:
        find . -name '*.class' -delete
 
 maintainer-clean distclean bootstrap-clean: clean
+       rm -f Makefile ndk-build.mk
diff --git a/java/org/gnu/emacs/EmacsClipboard.java 
b/java/org/gnu/emacs/EmacsClipboard.java
new file mode 100644
index 00000000000..cd6bcebfe0e
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsClipboard.java
@@ -0,0 +1,44 @@
+/* Communication module for Android terminals.  -*- c-file-style: "GNU" -*-
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+package org.gnu.emacs;
+
+import android.os.Build;
+
+/* This class provides helper code for accessing the clipboard,
+   abstracting between the different interfaces on API 8 and 11.  */
+
+public abstract class EmacsClipboard
+{
+  public abstract void setClipboard (byte[] bytes);
+  public abstract int ownsClipboard ();
+  public abstract boolean clipboardExists ();
+  public abstract byte[] getClipboard ();
+
+  /* Create the correct kind of clipboard for this system.  */
+
+  public static EmacsClipboard
+  makeClipboard ()
+  {
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
+      return new EmacsSdk11Clipboard ();
+    else
+      return new EmacsSdk8Clipboard ();
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsFontDriver.java 
b/java/org/gnu/emacs/EmacsFontDriver.java
index 1d1e6f7b33f..39bda5a456d 100644
--- a/java/org/gnu/emacs/EmacsFontDriver.java
+++ b/java/org/gnu/emacs/EmacsFontDriver.java
@@ -23,6 +23,9 @@ import java.util.List;
 
 import android.os.Build;
 
+/* This code is mostly unused.  See sfntfont-android.c for the code
+   that is actually used.  */
+
 public abstract class EmacsFontDriver
 {
   /* Font weights.  */
diff --git a/java/org/gnu/emacs/EmacsNative.java 
b/java/org/gnu/emacs/EmacsNative.java
index 3efdc0cff9a..962538bef7b 100644
--- a/java/org/gnu/emacs/EmacsNative.java
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -132,6 +132,10 @@ public class EmacsNative
   /* Send an ANDROID_CONTEXT_MENU event.  */
   public static native long sendContextMenu (short window, int menuEventID);
 
+  /* Send an ANDROID_EXPOSE event.  */
+  public static native long sendExpose (short window, int x, int y,
+                                       int width, int height);
+
   static
   {
     System.loadLibrary ("emacs");
diff --git a/java/org/gnu/emacs/EmacsSdk11Clipboard.java 
b/java/org/gnu/emacs/EmacsSdk11Clipboard.java
new file mode 100644
index 00000000000..0a725200723
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSdk11Clipboard.java
@@ -0,0 +1,156 @@
+/* Communication module for Android terminals.  -*- c-file-style: "GNU" -*-
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+package org.gnu.emacs;
+
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.ClipData;
+
+import android.util.Log;
+
+import java.io.UnsupportedEncodingException;
+
+/* This class implements EmacsClipboard for Android 3.0 and later
+   systems.  */
+
+public class EmacsSdk11Clipboard extends EmacsClipboard
+  implements ClipboardManager.OnPrimaryClipChangedListener
+{
+  private static final String TAG = "EmacsSdk11Clipboard";
+  private ClipboardManager manager;
+  private boolean ownsClipboard;
+  private int clipboardChangedCount;
+  private int monitoredClipboardChangedCount;
+
+  public
+  EmacsSdk11Clipboard ()
+  {
+    String what;
+    Context context;
+
+    what = Context.CLIPBOARD_SERVICE;
+    context = EmacsService.SERVICE;
+    manager
+      = (ClipboardManager) context.getSystemService (what);
+    manager.addPrimaryClipChangedListener (this);
+  }
+
+  @Override
+  public synchronized void
+  onPrimaryClipChanged ()
+  {
+    Log.d (TAG, ("onPrimaryClipChanged: "
+                + monitoredClipboardChangedCount
+                + " " + clipboardChangedCount));
+
+    /* Increment monitoredClipboardChangeCount.  If it is now greater
+       than clipboardChangedCount, then Emacs no longer owns the
+       clipboard.  */
+    monitoredClipboardChangedCount++;
+
+    if (monitoredClipboardChangedCount > clipboardChangedCount)
+      {
+       ownsClipboard = false;
+
+       /* Reset both values back to 0.  */
+       monitoredClipboardChangedCount = 0;
+       clipboardChangedCount = 0;
+      }
+  }
+
+  /* Set the clipboard text to CLIPBOARD, a string in UTF-8
+     encoding.  */
+
+  @Override
+  public synchronized void
+  setClipboard (byte[] bytes)
+  {
+    ClipData data;
+    String string;
+
+    try
+      {
+       string = new String (bytes, "UTF-8");
+       data = ClipData.newPlainText ("Emacs", string);
+       manager.setPrimaryClip (data);
+       ownsClipboard = true;
+
+       /* onPrimaryClipChanged will be called again.  Use this
+          variable to keep track of how many times the clipboard has
+          been changed.  */
+       ++clipboardChangedCount;
+      }
+    catch (UnsupportedEncodingException exception)
+      {
+       Log.w (TAG, "setClipboard: " + exception);
+      }
+  }
+
+  /* Return whether or not Emacs owns the clipboard.  Value is 1 if
+     Emacs does, 0 if Emacs does not, and -1 if that information is
+     unavailable.  */
+
+  @Override
+  public synchronized int
+  ownsClipboard ()
+  {
+    return ownsClipboard ? 1 : 0;
+  }
+
+  /* Return whether or not clipboard content currently exists.  */
+
+  @Override
+  public boolean
+  clipboardExists ()
+  {
+    return manager.hasPrimaryClip ();
+  }
+
+  /* Return the current content of the clipboard, as plain text, or
+     NULL if no content is available.  */
+
+  @Override
+  public byte[]
+  getClipboard ()
+  {
+    ClipData clip;
+    CharSequence text;
+    Context context;
+
+    clip = manager.getPrimaryClip ();
+
+    if (clip == null || clip.getItemCount () < 1)
+      return null;
+
+    context = EmacsService.SERVICE;
+
+    try
+      {
+       text = clip.getItemAt (0).coerceToText (context);
+       return text.toString ().getBytes ("UTF-8");
+      }
+    catch (UnsupportedEncodingException exception)
+      {
+       Log.w (TAG, "getClipboard: " + exception);
+      }
+
+    return null;
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsSdk8Clipboard.java 
b/java/org/gnu/emacs/EmacsSdk8Clipboard.java
new file mode 100644
index 00000000000..34e66912562
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSdk8Clipboard.java
@@ -0,0 +1,116 @@
+/* Communication module for Android terminals.  -*- c-file-style: "GNU" -*-
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+package org.gnu.emacs;
+
+/* Importing the entire package avoids the deprecation warning.  */
+import android.text.*;
+
+import android.content.Context;
+import android.util.Log;
+
+import java.io.UnsupportedEncodingException;
+
+/* This class implements EmacsClipboard for Android 2.2 and other
+   similarly old systems.  */
+
+@SuppressWarnings ("deprecation")
+public class EmacsSdk8Clipboard extends EmacsClipboard
+{
+  private static final String TAG = "EmacsSdk8Clipboard";
+  private ClipboardManager manager;
+
+  public
+  EmacsSdk8Clipboard ()
+  {
+    String what;
+    Context context;
+
+    what = Context.CLIPBOARD_SERVICE;
+    context = EmacsService.SERVICE;
+    manager
+      = (ClipboardManager) context.getSystemService (what);
+  }
+
+  /* Set the clipboard text to CLIPBOARD, a string in UTF-8
+     encoding.  */
+
+  @Override
+  public void
+  setClipboard (byte[] bytes)
+  {
+    try
+      {
+       manager.setText (new String (bytes, "UTF-8"));
+      }
+    catch (UnsupportedEncodingException exception)
+      {
+       Log.w (TAG, "setClipboard: " + exception);
+      }
+  }
+
+  /* Return whether or not Emacs owns the clipboard.  Value is 1 if
+     Emacs does, 0 if Emacs does not, and -1 if that information is
+     unavailable.  */
+
+  @Override
+  public int
+  ownsClipboard ()
+  {
+    return -1;
+  }
+
+  /* Return whether or not clipboard content currently exists.  */
+
+  @Override
+  public boolean
+  clipboardExists ()
+  {
+    return manager.hasText ();
+  }
+
+  /* Return the current content of the clipboard, as plain text, or
+     NULL if no content is available.  */
+
+  @Override
+  public byte[]
+  getClipboard ()
+  {
+    String string;
+    CharSequence text;
+
+    text = manager.getText ();
+
+    if (text == null)
+      return null;
+
+    string = text.toString ();
+
+    try
+      {
+       return string.getBytes ("UTF-8");
+      }
+    catch (UnsupportedEncodingException exception)
+      {
+       Log.w (TAG, "getClipboard: " + exception);
+      }
+
+    return null;
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsView.java 
b/java/org/gnu/emacs/EmacsView.java
index 74bbb7b3ecc..881cbc363ba 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -99,6 +99,9 @@ public class EmacsView extends ViewGroup
      yet responded to.  0 if there is no such outstanding event.  */
   public long pendingConfigure;
 
+  /* Whether or not this view is attached to a window.  */
+  public boolean isAttachedToWindow;
+
   public
   EmacsView (EmacsWindow window)
   {
@@ -140,6 +143,9 @@ public class EmacsView extends ViewGroup
     if (measuredWidth == 0 || measuredHeight == 0)
       return;
 
+    if (!isAttachedToWindow)
+      return;
+
     /* If bitmap is the same width and height as the measured width
        and height, there is no need to do anything.  Avoid allocating
        the extra bitmap.  */
@@ -547,6 +553,8 @@ public class EmacsView extends ViewGroup
   public synchronized void
   onDetachedFromWindow ()
   {
+    isAttachedToWindow = false;
+
     synchronized (this)
       {
        /* Recycle the bitmap and call GC.  */
@@ -559,6 +567,21 @@ public class EmacsView extends ViewGroup
       }
   }
 
+  @Override
+  public synchronized void
+  onAttachedToWindow ()
+  {
+    isAttachedToWindow = true;
+
+    /* Dirty the bitmap, as it was destroyed when onDetachedFromWindow
+       was called.  */
+    bitmapDirty = true;
+
+    /* Now expose the view contents again.  */
+    EmacsNative.sendExpose (this.window.handle, 0, 0,
+                           measuredWidth, measuredHeight);
+  }
+
   public void
   showOnScreenKeyboard ()
   {
diff --git a/lib/Makefile.in b/lib/Makefile.in
index fd730b554e3..b84a1bf9741 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -21,7 +21,7 @@ srcdir = @srcdir@
 VPATH = @srcdir@
 
 # This is not empty if this is a Makefile that will be copied to
-# xcompile/lib.
+# cross/lib.
 XCONFIGURE = @XCONFIGURE@
 
 # This is required to make sure symbol visibility is correct and
@@ -64,7 +64,7 @@ endif
 ifeq ($(XCONFIGURE),android)
 # The next line is necessary to override -I$(srcdir), which will end
 # up pulling in lots of headers from the host.
-ALL_CFLAGS += -I$(top_srcdir)/xcompile -I.
+ALL_CFLAGS += -I$(top_srcdir)/cross -I.
 endif
 
 DEPDIR = deps
diff --git a/lisp/term/android-win.el b/lisp/term/android-win.el
index 43b75d39f50..6ab469dc0b0 100644
--- a/lisp/term/android-win.el
+++ b/lisp/term/android-win.el
@@ -59,5 +59,111 @@ DISPLAY is ignored on Android."
   ;; arguments on.
   (ignore))
 
+
+;;; Selection support.
+
+(declare-function android-clipboard-exists-p "androidselect.c")
+(declare-function android-get-clipboard "androidselect.c")
+(declare-function android-set-clipboard "androidselect.c")
+(declare-function android-clipboard-owner-p "androidselect.c")
+
+(defvar android-primary-selection nil
+  "The last string placed in the primary selection.
+Nil if there was no such string.
+
+Android does not have a primary selection of its own, so Emacs
+emulates one inside Lisp.")
+
+(defun android-get-clipboard-1 (data-type)
+  "Return the clipboard data.
+DATA-TYPE is a selection conversion target; only STRING and
+TARGETS are supported."
+  (or (and (eq data-type 'STRING)
+           (android-get-clipboard))
+      (and (eq data-type 'TARGETS)
+           (android-clipboard-exists-p)
+           [TARGETS STRING])))
+
+(defun android-get-primary (data-type)
+  "Return the last string placed in the primary selection, or nil.
+Return nil if DATA-TYPE is anything other than STRING or TARGETS."
+  (when android-primary-selection
+    (or (and (eq data-type 'STRING)
+             android-primary-selection)
+        (and (eq data-type 'TARGETS)
+             [TARGETS]))))
+
+(defun android-selection-bounds (value)
+  "Return bounds of selection value VALUE.
+The return value is a list (BEG END BUF) if VALUE is a cons of
+two markers or an overlay.  Otherwise, it is nil."
+  (cond ((bufferp value)
+        (with-current-buffer value
+          (when (mark t)
+            (list (mark t) (point) value))))
+       ((and (consp value)
+             (markerp (car value))
+             (markerp (cdr value)))
+        (when (and (marker-buffer (car value))
+                   (buffer-name (marker-buffer (car value)))
+                   (eq (marker-buffer (car value))
+                       (marker-buffer (cdr value))))
+          (list (marker-position (car value))
+                (marker-position (cdr value))
+                (marker-buffer (car value)))))
+       ((overlayp value)
+        (when (overlay-buffer value)
+          (list (overlay-start value)
+                (overlay-end value)
+                (overlay-buffer value))))))
+
+(defun android-encode-select-string (value)
+  "Turn VALUE into a string suitable for placing in the clipboard.
+VALUE should be something suitable for passing to
+`gui-set-selection'."
+  (unless (stringp value)
+    (when-let ((bounds (android-selection-bounds value)))
+      (setq value (ignore-errors
+                    (with-current-buffer (nth 2 bounds)
+                      (buffer-substring (nth 0 bounds)
+                                        (nth 1 bounds)))))))
+  value)
+
+(cl-defmethod gui-backend-get-selection (type data-type
+                                              &context (window-system android))
+  (cond ((eq type 'CLIPBOARD)
+         (android-get-clipboard-1 data-type))
+        ((eq type 'PRIMARY)
+         (android-get-primary data-type))))
+
+(cl-defmethod gui-backend-selection-exists-p (selection
+                                              &context (window-system android))
+  (cond ((eq selection 'CLIPBOARD)
+         (android-clipboard-exists-p))
+        ((eq selection 'PRIMARY)
+         (not (null android-primary-selection)))))
+
+(cl-defmethod gui-backend-selection-owner-p (selection
+                                             &context (window-system android))
+  (cond ((eq selection 'CLIPBOARD)
+         (let ((ownership (android-clipboard-owner-p)))
+           ;; If ownership is `lambda', then Emacs couldn't determine
+           ;; whether or not it owns the clipboard.
+           (and (not (eq ownership 'lambda)) ownership)))
+        ((eq selection 'PRIMARY)
+         ;; Emacs always owns its own primary selection as long as it
+         ;; exists.
+         (not (null android-primary-selection)))))
+
+(cl-defmethod gui-backend-set-selection (type value
+                                              &context (window-system android))
+  ;; First, try to turn value into a string.
+  ;; Don't set anything if that did not work.
+  (when-let ((string (android-encode-select-string value)))
+    (cond ((eq type 'CLIPBOARD)
+           (android-set-clipboard string))
+          ((eq type 'PRIMARY)
+           (setq android-primary-selection string)))))
+
 (provide 'android-win)
 ;; android-win.el ends here.
diff --git a/m4/ndk-build.m4 b/m4/ndk-build.m4
new file mode 100644
index 00000000000..a35900884d9
--- /dev/null
+++ b/m4/ndk-build.m4
@@ -0,0 +1,213 @@
+dnl Copyright (C) 2023 Free Software Foundation, Inc.
+dnl This file is part of GNU Emacs.
+
+dnl GNU Emacs is free software: you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation, either version 3 of the License, or
+dnl (at your option) any later version.
+
+dnl GNU Emacs is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+dnl GNU General Public License for more details.
+
+dnl You should have received a copy of the GNU General Public License
+dnl along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+# Support for building Emacs with dependencies using the Android NDK
+# build system.
+
+AC_ARG_WITH([ndk_path],
+  [AS_HELP_STRING([--with-ndk-path],
+    [find Android libraries in these directories])])
+
+# ndk_INIT(ABI, API, DIR)
+# --------
+# Initialize the Android NDK.  ABI is the ABI being built for.
+# API is the API version being built for.
+# As a side effect, set the variable ndk_INITIALIZED to true.
+# DIR should be a directory containing the Makefile.in actually
+# implementing the Android NDK build system.
+
+AC_DEFUN_ONCE([ndk_INIT],
+[
+# Look for Android.mk files.
+ndk_module_files=
+for file in $with_ndk_path; do
+  if test -f $file/Android.mk; then
+    ndk_module_files="$ndk_module_files$file/Android.mk "
+  fi
+done
+
+ndk_ABI=$1
+ndk_MODULES=
+ndk_MAKEFILES=
+ndk_INITIALIZED=yes
+ndk_API=$2
+ndk_DIR=$3
+
+case "$ndk_ABI" in
+ *arm64* )
+   ndk_ARCH=arm64
+   ;;
+ *arm* )
+   ndk_ARCH=arm
+   ;;
+ *x86_64* )
+   ndk_ARCH=x86_64
+   ;;
+ *x86* )
+   ndk_ARCH=x86
+   ;;
+ * )
+   AC_MSG_ERROR([Failed to determine Android device architecture])
+   ;;
+esac
+
+# This is a map between pkg-config style package names and Android
+# ones.
+
+ndk_package_map="libwebpdemux:webpdemux libxml-2.0:libxml2"
+
+# Replace ndk_module with the appropriate Android module name if it is
+# found in ndk_package_map.
+
+ndk_replace_pkg_config_package () {
+  for ndk_stuff in $ndk_package_map; do
+    ndk_key=${ndk_stuff%%:*}
+    ndk_value=${ndk_stuff#*:}
+
+    if test "$ndk_key" = "$ndk_module"; then
+      ndk_module="$ndk_value"
+      break
+    fi
+  done
+}
+
+# Parse a pkg-config style list of modules.  Place the resulting list
+# in ndk_modules.
+
+ndk_parse_pkg_config_string () {
+  ndk_input=[$]1
+  ndk_modules=
+  while test -n "$ndk_input"; do
+    ndk_str=$(printf "$ndk_input" | cut -f1 -d' ')
+    ndk_input="$(printf "$ndk_input" | cut -s -f2- -d' ')"
+
+    if test "$ndk_str" = ">=" || test "$ndk_str" = "<=" \
+      || test "$ndk_str" = ">" || test "$ndk_str" = "<"; then
+      ndk_input="$(printf "$ndk_input" | cut -s -f2- -d' ')"
+    else
+      ndk_modules="$ndk_modules$ndk_str "
+    fi
+  done
+}
+
+# Look for a suitable ar in the same directory as the C compiler.
+ndk_where_cc=$(which $CC)
+ndk_ar_search_path=$PATH
+
+# First, try to find $host_alias-ar in PATH.
+AC_PATH_PROGS([AR], [$host_alias-ar], [], [$ndk_ar_search_path])
+
+if test -z "$AR"; then
+  # Next, try finding either that or llvm-ar in the directory holding
+  # CC.
+  ndk_ar_search_path="$(dirname $ndk_where_cc):$ndk_ar_search_path"
+  AC_PATH_PROGS([AR], [$host_alias-ar llvm-ar], [], [$ndk_ar_search_path])
+fi
+
+# These variables have now been found.
+])
+
+# ndk_SEARCH_MODULE(MODULE, NAME, ACTION-IF-FOUND, [ACTION-IF-NOT-FOUND])
+# --------------------------------------------------------------------
+# Search for a module named MODULE in `with_ndk_path'.  Add the file
+# name of the module's Android.mk file to the variable ndk_MODULES.
+# Set NAME_CFLAGS and NAME_LIBS to the appropriate values.  Then,
+# call ACTION-IF-FOUND, or ACTION-IF-NOT-FOUND upon failure.
+AC_DEFUN([ndk_SEARCH_MODULE],
+[
+module_name=
+ndk_module=$1
+ndk_replace_pkg_config_package
+AC_MSG_CHECKING([for Android.mk that builds $ndk_module])
+
+for ndk_android_mk in $ndk_module_files; do
+  # Read this Android.mk file.  Set NDK_ROOT to /tmp: the Android in
+  # tree build system sets it to a meaning value, but build files just
+  # use it to test whether or not the NDK is being used.
+  ndk_commands=$(make -s -f build-aux/ndk-build-helper.mk EMACS_SRCDIR=.     \
+                EMACS_ABI=$ndk_ABI ANDROID_MAKEFILE="$ndk_android_mk"       \
+                ANDROID_MODULE_DIRECTORY=$(dirname "$ndk_android_mk")       \
+                NDK_BUILD_DIR="$ndk_DIR" NDK_ROOT="/tmp"                    \
+                | awk -f build-aux/ndk-module-extract.awk                   \
+                MODULE="$ndk_module")
+
+  AS_IF([test -n "${ndk_commands//\n }"], [eval "$ndk_commands"])
+
+  if test -n "$module_name"; then
+    break
+  fi
+done
+
+if test -z "$module_name"; then
+  AC_MSG_RESULT([no])
+  $4
+else
+  $2[]_CFLAGS="[$]$2[]_CFLAGS $module_cflags $module_includes"
+  $2[]_LIBS="[$]$2[]_LIBS $module_ldflags"
+  ndk_MAKEFILES="$ndk_MAKEFILES $ndk_android_mk"
+  ndk_MODULES="$ndk_MODULES $module_target"
+  AC_MSG_RESULT([yes])
+  $3
+fi
+])
+
+# ndk_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+#   [ACTION-IF-NOT-FOUND])
+# --------------------------------------------------------------
+# Just like `PKG_CHECK_MODULES'.  However, it uses the ndk-build
+# system instead.
+
+AC_DEFUN([ndk_CHECK_MODULES],
+[
+  ndk_modules=
+  ndk_parse_pkg_config_string "$2"
+  ndk_found=no
+
+  for module in $ndk_modules; do
+    ndk_SEARCH_MODULE([$module], [$1], [ndk_found=yes], [ndk_found=no])
+  done
+
+  AS_IF([test "$ndk_found" = "yes"],[$3],[$4])
+])
+
+# ndk_CONFIG_FILES
+# -------------------------------------------------------------
+# Write out the NDK build Makefile with the appropriate variables
+# set if the NDK has been initialized.
+
+AC_DEFUN_ONCE([ndk_CONFIG_FILES],
+[
+  if test "$ndk_INITIALIZED" = "yes"; then
+    NDK_BUILD_ANDROID_MK="$ndk_MAKEFILES"
+    NDK_BUILD_ARCH=$ndk_ARCH
+    NDK_BUILD_ABI=$ndk_ABI
+    NDK_BUILD_SDK=$ndk_API
+    NDK_BUILD_CC=$CC
+    NDK_BUILD_AR=$AR
+    NDK_BUILD_MODULES="$ndk_MODULES"
+
+    AC_SUBST([NDK_BUILD_ANDROID_MK])
+    AC_SUBST([NDK_BUILD_ARCH])
+    AC_SUBST([NDK_BUILD_ABI])
+    AC_SUBST([NDK_BUILD_SDK])
+    AC_SUBST([NDK_BUILD_CC])
+    AC_SUBST([NDK_BUILD_AR])
+    AC_SUBST([NDK_BUILD_MODULES])
+
+    AC_CONFIG_FILES([$ndk_DIR/Makefile])
+    AC_CONFIG_FILES([$ndk_DIR/ndk-build.mk])
+  fi
+])
diff --git a/src/Makefile.in b/src/Makefile.in
index 1b4a8a16c46..ddc0b89dda1 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -35,7 +35,7 @@ abs_top_srcdir=@abs_top_srcdir@
 VPATH = $(srcdir)
 
 # This is not empty if this is a Makefile that will be copied to
-# xcompile/src.
+# cross/src.
 XCONFIGURE = @XCONFIGURE@
 
 ifneq ($(XCONFIGURE),)
@@ -138,6 +138,8 @@ LIB_PTHREAD=@LIB_PTHREAD@
 
 LIBIMAGE=@LIBTIFF@ @LIBJPEG@ @LIBPNG@ @LIBGIF@ @LIBXPM@ @WEBP_LIBS@
 
+GIF_CFLAGS=@GIF_CFLAGS@
+
 XCB_LIBS=@XCB_LIBS@
 XFT_LIBS=@XFT_LIBS@
 XRENDER_LIBS=@XRENDER_LIBS@
@@ -384,6 +386,7 @@ HAIKU_CFLAGS = @HAIKU_CFLAGS@
 
 ANDROID_OBJ = @ANDROID_OBJ@
 ANDROID_LIBS = @ANDROID_LIBS@
+ANDROID_LDFLAGS = @ANDROID_LDFLAGS@
 ANDROID_CFLAGS = @ANDROID_CFLAGS@
 
 DUMPING=@DUMPING@
@@ -428,7 +431,7 @@ EMACS_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \
   $(LIBSYSTEMD_CFLAGS) $(JSON_CFLAGS) $(XSYNC_CFLAGS) $(TREE_SITTER_CFLAGS) \
   $(LIBGNUTLS_CFLAGS) $(NOTIFY_CFLAGS) $(CAIRO_CFLAGS) \
   $(WERROR_CFLAGS) $(HAIKU_CFLAGS) $(XCOMPOSITE_CFLAGS) $(XSHAPE_CFLAGS) \
-  $(ANDROID_CFLAGS)
+  $(ANDROID_CFLAGS) $(GIF_CFLAGS)
 ALL_CFLAGS = $(EMACS_CFLAGS) $(WARN_CFLAGS) $(CFLAGS)
 ALL_OBJC_CFLAGS = $(EMACS_CFLAGS) \
   $(filter-out $(NON_OBJC_CFLAGS),$(WARN_CFLAGS)) $(CFLAGS) \
@@ -745,10 +748,18 @@ ifeq ($(XCONFIGURE),android)
 ## is not dumped here.  Instead, it dumps itself the first time it
 ## starts on the user's device.
 
+# Include ndk-build.mk in order to build Emacs dependencies.
+old_top_builddir := $(top_builddir)
+top_builddir := $(old_top_builddir)/..
+include $(old_top_builddir)/ndk-build/ndk-build.mk
+top_builddir := $(old_top_builddir)
+
 libemacs.so: $(ALLOBJS) $(LIBEGNU_ARCHIVE) $(EMACSRES) \
-            $(MAKE_PDUMPER_FINGERPRINT)
+            $(MAKE_PDUMPER_FINGERPRINT) $(NDK_BUILD_SHARED) \
+            $(NDK_BUILD_STATIC)
        $(AM_V_CCLD)$(CC) -o $@ $(ALL_CFLAGS) $(TEMACS_LDFLAGS) \
-         $(LDFLAGS) -shared $(ALLOBJS) $(LIBEGNU_ARCHIVE) $(LIBES)
+         $(ANDROID_LDFLAGS) $(LDFLAGS) -shared $(ALLOBJS) \
+         $(LIBEGNU_ARCHIVE) $(LIBES)
        $(AM_V_at)$(MAKE_PDUMPER_FINGERPRINT) $@
 
 # There is also a binary named `android-emacs' which simply calls
diff --git a/src/android.c b/src/android.c
index a51d0518484..d788c6f19b4 100644
--- a/src/android.c
+++ b/src/android.c
@@ -1086,6 +1086,39 @@ android_hack_asset_fd (AAsset *asset)
   return fd;
 }
 
+/* Read two bytes from FD and see if they are ``PK'', denoting ZIP
+   archive compressed data.
+
+   If they are not, rewind the file descriptor to offset 0.
+
+   If either operation fails, return -1 and close FD.  Else, value is
+   FD.  */
+
+static int
+android_check_compressed_file (int fd)
+{
+  char bytes[2];
+
+  if (read (fd, bytes, 2) != 2)
+    goto lseek_back;
+
+  if (bytes[0] != 'P' || bytes[1] != 'K')
+    goto lseek_back;
+
+  /* This could be compressed data! */
+  return -1;
+
+ lseek_back:
+  /* Seek back to offset 0.  If this fails, return -1.  */
+  if (lseek (fd, 0, SEEK_SET) != 0)
+    {
+      close (fd);
+      return -1;
+    }
+
+  return fd;
+}
+
 /* `open' and such are modified even though they exist on Android,
    because Emacs treats "/assets/" as a special directory that must
    contain all assets in the application package.  */
@@ -1095,7 +1128,7 @@ android_open (const char *filename, int oflag, int mode)
 {
   const char *name;
   AAsset *asset;
-  int fd;
+  int fd, oldfd;
   off_t out_start, out_length;
   bool fd_hacked;
 
@@ -1118,8 +1151,12 @@ android_open (const char *filename, int oflag, int mode)
          return -1;
        }
 
+      /* If given AASSET_MODE_BUFFER (which is what Emacs probably
+        does, given that a file descriptor is not always available),
+        the framework fails to uncompress the data before it returns
+        a file descriptor.  */
       asset = AAssetManager_open (asset_manager, name,
-                                 AASSET_MODE_BUFFER);
+                                 AASSET_MODE_STREAMING);
 
       if (!asset)
        {
@@ -1132,6 +1169,11 @@ android_open (const char *filename, int oflag, int mode)
       fd = AAsset_openFileDescriptor (asset, &out_start,
                                      &out_length);
 
+      /* The platform sometimes returns a file descriptor to ZIP
+        compressed data.  Detect that and fall back to creating a
+        shared memory file descriptor.  */
+      fd = android_check_compressed_file (fd);
+
       if (fd == -1)
        {
          /* The asset can't be accessed for some reason.  Try to
@@ -1152,7 +1194,13 @@ android_open (const char *filename, int oflag, int mode)
         which will close the original file descriptor.  */
 
       if (!fd_hacked)
-       fd = fcntl (fd, F_DUPFD_CLOEXEC);
+       {
+         oldfd = fd;
+         fd = fcntl (oldfd, F_DUPFD_CLOEXEC);
+
+         /* Close the original file descriptor.  */
+         close (oldfd);
+       }
 
       if (fd >= ANDROID_MAX_ASSET_FD || fd < 0)
        {
@@ -1978,6 +2026,25 @@ NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject 
object,
   return event_serial;
 }
 
+extern JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendExpose) (JNIEnv *env, jobject object,
+                         jshort window, jint x, jint y,
+                         jint width, jint height)
+{
+  union android_event event;
+
+  event.xexpose.type = ANDROID_EXPOSE;
+  event.xexpose.serial = ++event_serial;
+  event.xexpose.window = window;
+  event.xexpose.x = x;
+  event.xexpose.y = y;
+  event.xexpose.width = width;
+  event.xexpose.height = height;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
 #ifdef __clang__
 #pragma clang diagnostic pop
 #else
@@ -4222,7 +4289,14 @@ android_readdir (struct android_dir *dir)
       dirent.d_ino = 0;
       dirent.d_off = 0;
       dirent.d_reclen = sizeof dirent;
-      dirent.d_type = DT_UNKNOWN;
+
+      /* If this is not a directory, return DT_UNKNOWN.  Otherwise,
+        return DT_DIR.  */
+
+      if (android_is_directory (dir->asset_dir))
+       dirent.d_type = DT_DIR;
+      else
+       dirent.d_type = DT_UNKNOWN;
 
       /* Note that dir->asset_dir is actually a NULL terminated
         string.  */
diff --git a/src/androidgui.h b/src/androidgui.h
index 3b9a74dc0b0..619c5e44f15 100644
--- a/src/androidgui.h
+++ b/src/androidgui.h
@@ -234,6 +234,7 @@ enum android_event_type
     ANDROID_ICONIFIED,
     ANDROID_DEICONIFIED,
     ANDROID_CONTEXT_MENU,
+    ANDROID_EXPOSE,
   };
 
 struct android_any_event
@@ -332,6 +333,15 @@ struct android_button_event
   unsigned int button;
 };
 
+struct android_expose_event
+{
+  enum android_event_type type;
+  unsigned long serial;
+  android_window window;
+  int x, y;
+  int width, height;
+};
+
 struct android_touch_event
 {
   /* Type of the event.  */
@@ -415,6 +425,7 @@ union android_event
   struct android_crossing_event xcrossing;
   struct android_motion_event xmotion;
   struct android_button_event xbutton;
+  struct android_expose_event xexpose;
 
   /* This has no parallel in X, since the X model of having
      monotonically increasing touch IDs can't work on Android.  */
diff --git a/src/androidselect.c b/src/androidselect.c
new file mode 100644
index 00000000000..4a71b376e28
--- /dev/null
+++ b/src/androidselect.c
@@ -0,0 +1,249 @@
+/* Communication module for Android terminals.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+#include <assert.h>
+
+#include "lisp.h"
+#include "blockinput.h"
+#include "coding.h"
+#include "android.h"
+#include "androidterm.h"
+
+/* Selection support on Android is confined to copying and pasting of
+   plain text from the clipboard.  There is no primary selection.
+
+   While newer versions of Android are supposed to have the necessary
+   interfaces for transferring other kinds of selection data, doing so
+   is too complicated, and involves registering ``content providers''
+   and all kinds of other stuff.  */
+
+
+
+/* Structure describing the EmacsClipboard class.  */
+
+struct android_emacs_clipboard
+{
+  jclass class;
+  jmethodID set_clipboard;
+  jmethodID owns_clipboard;
+  jmethodID clipboard_exists;
+  jmethodID get_clipboard;
+  jmethodID make_clipboard;
+};
+
+/* Methods associated with the EmacsClipboard class.  */
+static struct android_emacs_clipboard clipboard_class;
+
+/* Reference to the EmacsClipboard object.  */
+static jobject clipboard;
+
+
+
+static void
+android_init_emacs_clipboard (void)
+{
+  jclass old;
+
+  clipboard_class.class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "org/gnu/emacs/EmacsClipboard");
+  eassert (clipboard_class.class);
+
+  old = clipboard_class.class;
+  clipboard_class.class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!clipboard_class.class)
+    emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature)                   \
+  clipboard_class.c_name                                       \
+    = (*android_java_env)->GetMethodID (android_java_env,      \
+                                       clipboard_class.class,  \
+                                       name, signature);       \
+  assert (clipboard_class.c_name);
+
+  FIND_METHOD (set_clipboard, "setClipboard", "([B)V");
+  FIND_METHOD (owns_clipboard, "ownsClipboard", "()I");
+  FIND_METHOD (clipboard_exists, "clipboardExists", "()Z");
+  FIND_METHOD (get_clipboard, "getClipboard", "()[B");
+
+  clipboard_class.make_clipboard
+    = (*android_java_env)->GetStaticMethodID (android_java_env,
+                                             clipboard_class.class,
+                                             "makeClipboard",
+                                             "()Lorg/gnu/emacs/"
+                                             "EmacsClipboard;");
+  assert (clipboard_class.make_clipboard);
+
+#undef FIND_METHOD
+}
+
+
+
+
+DEFUN ("android-clipboard-owner-p", Fandroid_clipboard_owner_p,
+       Sandroid_clipboard_owner_p, 0, 0, 0,
+       doc: /* Return whether or not Emacs owns the clipboard.
+Alternatively, return the symbol `lambda' if that could not be
+determined.  */)
+  (void)
+{
+  jint rc;
+
+  block_input ();
+  rc = (*android_java_env)->CallIntMethod (android_java_env,
+                                          clipboard,
+                                          clipboard_class.owns_clipboard);
+  android_exception_check ();
+  unblock_input ();
+
+  /* If rc is 0 or 1, then Emacs knows whether or not it owns the
+     clipboard.  If rc is -1, then Emacs does not.  */
+
+  if (rc < 0)
+    return Qlambda;
+
+  return rc ? Qt : Qnil;
+}
+
+DEFUN ("android-set-clipboard", Fandroid_set_clipboard,
+       Sandroid_set_clipboard, 1, 1, 0,
+       doc: /* Set the clipboard text to STRING.  */)
+  (Lisp_Object string)
+{
+  jarray bytes;
+
+  CHECK_STRING (string);
+  string = ENCODE_UTF_8 (string);
+
+  bytes = (*android_java_env)->NewByteArray (android_java_env,
+                                            SBYTES (string));
+  android_exception_check ();
+
+  (*android_java_env)->SetByteArrayRegion (android_java_env, bytes,
+                                          0, SBYTES (string),
+                                          (jbyte *) SDATA (string));
+  (*android_java_env)->CallVoidMethod (android_java_env,
+                                      clipboard,
+                                      clipboard_class.set_clipboard,
+                                      bytes);
+  android_exception_check ();
+
+  ANDROID_DELETE_LOCAL_REF (bytes);
+  return Qnil;
+}
+
+DEFUN ("android-get-clipboard", Fandroid_get_clipboard,
+       Sandroid_get_clipboard, 0, 0, 0,
+       doc: /* Return the current contents of the clipboard.
+Value is a multibyte string containing decoded clipboard
+text.
+Alternatively, return nil if the clipboard is empty.  */)
+  (void)
+{
+  Lisp_Object string;
+  jarray bytes;
+  jmethodID method;
+  size_t length;
+  jbyte *data;
+
+  method = clipboard_class.get_clipboard;
+  bytes
+    = (*android_java_env)->CallObjectMethod (android_java_env,
+                                            clipboard,
+                                            method);
+
+  if (!bytes)
+    {
+      android_exception_check ();
+      return Qnil;
+    }
+
+  length = (*android_java_env)->GetArrayLength (android_java_env,
+                                               bytes);
+  data = (*android_java_env)->GetByteArrayElements (android_java_env,
+                                                   bytes, NULL);
+  android_exception_check ();
+
+  string = make_unibyte_string ((char *) data, length);
+
+  (*android_java_env)->ReleaseByteArrayElements (android_java_env,
+                                                bytes, data,
+                                                JNI_ABORT);
+  ANDROID_DELETE_LOCAL_REF (bytes);
+
+  /* Now decode the resulting string.  */
+  return code_convert_string_norecord (string, Qutf_8, Qnil);
+}
+
+DEFUN ("android-clipboard-exists-p", Fandroid_clipboard_exists_p,
+       Sandroid_clipboard_exists_p, 0, 0, 0,
+       doc: /* Return whether or not clipboard contents exist.  */)
+  (void)
+{
+  jboolean rc;
+  jmethodID method;
+
+  method = clipboard_class.clipboard_exists;
+  rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+                                              clipboard,
+                                              method);
+  android_exception_check ();
+
+  return rc ? Qt : Qnil;
+}
+
+
+
+void
+init_androidselect (void)
+{
+  jobject tem;
+  jmethodID make_clipboard;
+
+  android_init_emacs_clipboard ();
+
+  make_clipboard = clipboard_class.make_clipboard;
+  tem
+    = (*android_java_env)->CallStaticObjectMethod (android_java_env,
+                                                  clipboard_class.class,
+                                                  make_clipboard);
+  if (!tem)
+    emacs_abort ();
+
+  clipboard = (*android_java_env)->NewGlobalRef (android_java_env, tem);
+
+  if (!clipboard)
+    emacs_abort ();
+
+  ANDROID_DELETE_LOCAL_REF (tem);
+}
+
+void
+syms_of_androidselect (void)
+{
+  defsubr (&Sandroid_clipboard_owner_p);
+  defsubr (&Sandroid_set_clipboard);
+  defsubr (&Sandroid_get_clipboard);
+  defsubr (&Sandroid_clipboard_exists_p);
+}
diff --git a/src/androidterm.c b/src/androidterm.c
index 1141f50f266..d5dafe9b0d7 100644
--- a/src/androidterm.c
+++ b/src/androidterm.c
@@ -818,6 +818,28 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
 
       goto OTHER;
 
+    case ANDROID_EXPOSE:
+
+      f = any;
+
+      if (f)
+        {
+          if (!FRAME_VISIBLE_P (f))
+            {
+              f->output_data.android->has_been_visible = true;
+              SET_FRAME_GARBAGED (f);
+            }
+
+          if (!FRAME_GARBAGED_P (f))
+            {
+              expose_frame (f, event->xexpose.x, event->xexpose.y,
+                           event->xexpose.width, event->xexpose.height);
+             show_back_buffer (f);
+           }
+        }
+
+      goto OTHER;
+
     case ANDROID_BUTTON_PRESS:
     case ANDROID_BUTTON_RELEASE:
       /* If we decide we want to generate an event to be seen
diff --git a/src/androidterm.h b/src/androidterm.h
index 11b24d40b73..8cc31f1ab57 100644
--- a/src/androidterm.h
+++ b/src/androidterm.h
@@ -432,6 +432,15 @@ extern void sfntfont_android_shrink_scanline_buffer (void);
 extern void init_sfntfont_android (void);
 extern void syms_of_sfntfont_android (void);
 
+/* Defined in androidselect.c  */
+
+#ifndef ANDROID_STUBIFY
+
+extern void init_androidselect (void);
+extern void syms_of_androidselect (void);
+
+#endif
+
 
 
 #define RGB_TO_ULONG(r, g, b)   (((r) << 16) | ((g) << 8) | (b))
diff --git a/src/emacs.c b/src/emacs.c
index 994a4d1db93..02016e53c45 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -2409,6 +2409,7 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
       syms_of_fontset ();
 #if !defined ANDROID_STUBIFY
       syms_of_androidfont ();
+      syms_of_androidselect ();
       syms_of_sfntfont ();
       syms_of_sfntfont_android ();
 #endif /* !ANDROID_STUBIFY */
@@ -2514,6 +2515,7 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
 
 #if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
   init_androidfont ();
+  init_androidselect ();
   init_sfntfont ();
   init_sfntfont_android ();
 #endif



reply via email to

[Prev in Thread] Current Thread [Next in Thread]