emacs-diffs
[Top][All Lists]
Advanced

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

scratch/pkg 075dfe91c12 4/4: Merge remote-tracking branch 'origin/master


From: Gerd Moellmann
Subject: scratch/pkg 075dfe91c12 4/4: Merge remote-tracking branch 'origin/master' into scratch/pkg
Date: Mon, 7 Aug 2023 03:40:42 -0400 (EDT)

branch: scratch/pkg
commit 075dfe91c12c9f1b3e3406078a087e271f94eecf
Merge: 89c293dd9f3 7fb0248a33d
Author: Gerd Möllmann <gerd@gnu.org>
Commit: Gerd Möllmann <gerd@gnu.org>

    Merge remote-tracking branch 'origin/master' into scratch/pkg
---
 .dir-locals.el                                     |     2 +-
 .gitignore                                         |    46 +
 ChangeLog.4                                        |     4 +-
 ChangeLog.android                                  |  7961 ++++++++
 INSTALL                                            |     6 +-
 Makefile.in                                        |    62 +-
 README                                             |     5 +
 admin/merge-gnulib                                 |     9 +-
 admin/notes/unicode                                |     5 +-
 autogen.sh                                         |     6 +
 build-aux/makecounter.sh                           |    43 +
 build-aux/ndk-build-helper-1.mk                    |   112 +
 build-aux/ndk-build-helper-2.mk                    |   105 +
 build-aux/ndk-build-helper-3.mk                    |    28 +
 build-aux/ndk-build-helper-4.mk                    |    39 +
 build-aux/ndk-build-helper.mk                      |    81 +
 build-aux/ndk-module-extract.awk                   |    88 +
 configure.ac                                       |  1518 +-
 cross/Makefile.in                                  |   190 +
 cross/README                                       |     5 +
 cross/langinfo.h                                   |    20 +
 cross/ndk-build/Makefile.in                        |   144 +
 cross/ndk-build/README                             |   353 +
 cross/ndk-build/ndk-build-executable.mk            |    22 +
 cross/ndk-build/ndk-build-shared-library.mk        |   171 +
 cross/ndk-build/ndk-build-static-library.mk        |   142 +
 cross/ndk-build/ndk-build.mk.in                    |    68 +
 cross/ndk-build/ndk-clear-vars.mk                  |    57 +
 cross/ndk-build/ndk-prebuilt-shared-library.mk     |    24 +
 cross/ndk-build/ndk-prebuilt-static-library.mk     |    24 +
 cross/ndk-build/ndk-resolve.mk                     |   162 +
 src/verbose.mk.in => cross/verbose.mk.android      |    34 +-
 doc/emacs/Makefile.in                              |     2 +
 doc/emacs/android.texi                             |   791 +
 doc/emacs/dired.texi                               |    13 +
 doc/emacs/emacs.texi                               |    21 +
 doc/emacs/frames.texi                              |    24 +
 doc/emacs/input.texi                               |   179 +
 doc/emacs/windows.texi                             |     7 +
 doc/lispref/commands.texi                          |   260 +-
 doc/lispref/display.texi                           |    23 +-
 doc/lispref/elisp.texi                             |     1 +
 doc/lispref/frames.texi                            |   586 +-
 doc/lispref/keymaps.texi                           |    25 +
 doc/lispref/minibuf.texi                           |    10 +-
 doc/lispref/os.texi                                |     3 +
 doc/lispref/processes.texi                         |    18 +
 doc/misc/eshell.texi                               |     2 +-
 doc/misc/org.org                                   |     6 +-
 doc/misc/tramp.texi                                |     2 +-
 doc/misc/url.texi                                  |    10 +-
 etc/DEBUG                                          |    33 +
 etc/HISTORY                                        |     3 +-
 etc/MACHINES                                       |    12 +
 etc/NEWS                                           |    86 +
 etc/PROBLEMS                                       |    50 +
 etc/images/alt.pbm                                 |   Bin 0 -> 85 bytes
 etc/images/ctrl.pbm                                |   Bin 0 -> 104 bytes
 etc/images/hyper.pbm                               |   Bin 0 -> 123 bytes
 etc/images/last-page.pbm                           |   Bin 0 -> 81 bytes
 etc/images/last-page.xpm                           |   122 +
 etc/images/meta.pbm                                |   Bin 0 -> 104 bytes
 etc/images/shift.pbm                               |   Bin 0 -> 169 bytes
 etc/images/super.pbm                               |   Bin 0 -> 123 bytes
 exec/Makefile.in                                   |   140 +
 exec/README                                        |     3 +
 exec/config-mips.m4.in                             |    42 +
 exec/config.guess                                  |  1768 ++
 exec/config.h.in                                   |   358 +
 exec/config.sub                                    |  1890 ++
 exec/configure.ac                                  |   537 +
 exec/deps.mk                                       |    21 +
 exec/exec.c                                        |  1235 ++
 exec/exec.h                                        |   201 +
 exec/exec1.c                                       |    94 +
 exec/install-sh                                    |   541 +
 exec/loader-aarch64.s                              |   187 +
 exec/loader-armeabi.s                              |   204 +
 exec/loader-mips64el.s                             |   234 +
 exec/loader-mipsel.s                               |   236 +
 exec/loader-x86.s                                  |   203 +
 exec/loader-x86_64.s                               |   195 +
 exec/mipsel-user.h                                 |    43 +
 exec/mipsfpu.c                                     |   289 +
 exec/mipsfpu.h                                     |    82 +
 exec/test.c                                        |   105 +
 exec/trace.c                                       |  1432 ++
 java/AndroidManifest.xml.in                        |   225 +
 java/INSTALL                                       |   977 +
 java/Makefile.in                                   |   337 +
 java/README                                        |  1049 +
 java/debug.sh                                      |   368 +
 java/emacs.keystore                                |   Bin 0 -> 2776 bytes
 java/org/gnu/emacs/EmacsActivity.java              |   481 +
 java/org/gnu/emacs/EmacsApplication.java           |    92 +
 java/org/gnu/emacs/EmacsClipboard.java             |    47 +
 java/org/gnu/emacs/EmacsContextMenu.java           |   393 +
 java/org/gnu/emacs/EmacsCursor.java                |    47 +
 java/org/gnu/emacs/EmacsDialog.java                |   427 +
 java/org/gnu/emacs/EmacsDialogButtonLayout.java    |   152 +
 java/org/gnu/emacs/EmacsDirectoryEntry.java        |    33 +
 java/org/gnu/emacs/EmacsDocumentsProvider.java     |   578 +
 java/org/gnu/emacs/EmacsDrawLine.java              |    79 +
 java/org/gnu/emacs/EmacsDrawPoint.java             |    34 +
 java/org/gnu/emacs/EmacsDrawRectangle.java         |   120 +
 java/org/gnu/emacs/EmacsDrawable.java              |    32 +
 java/org/gnu/emacs/EmacsFillPolygon.java           |    80 +
 java/org/gnu/emacs/EmacsFillRectangle.java         |   116 +
 java/org/gnu/emacs/EmacsFontDriver.java            |   173 +
 java/org/gnu/emacs/EmacsGC.java                    |   121 +
 java/org/gnu/emacs/EmacsHandleObject.java          |    59 +
 java/org/gnu/emacs/EmacsHolder.java                |    30 +
 java/org/gnu/emacs/EmacsInputConnection.java       |   698 +
 .../emacs/EmacsLauncherPreferencesActivity.java    |    31 +
 java/org/gnu/emacs/EmacsMultitaskActivity.java     |    29 +
 java/org/gnu/emacs/EmacsNative.java                |   316 +
 java/org/gnu/emacs/EmacsNoninteractive.java        |   203 +
 java/org/gnu/emacs/EmacsOpenActivity.java          |   552 +
 java/org/gnu/emacs/EmacsPixmap.java                |   192 +
 java/org/gnu/emacs/EmacsPreferencesActivity.java   |   168 +
 java/org/gnu/emacs/EmacsSafThread.java             |  1687 ++
 java/org/gnu/emacs/EmacsSdk11Clipboard.java        |   290 +
 java/org/gnu/emacs/EmacsSdk23FontDriver.java       |   114 +
 java/org/gnu/emacs/EmacsSdk7FontDriver.java        |   539 +
 java/org/gnu/emacs/EmacsSdk8Clipboard.java         |   147 +
 java/org/gnu/emacs/EmacsService.java               |  1820 ++
 java/org/gnu/emacs/EmacsSurfaceView.java           |   223 +
 java/org/gnu/emacs/EmacsThread.java                |    82 +
 java/org/gnu/emacs/EmacsView.java                  |   777 +
 java/org/gnu/emacs/EmacsWindow.java                |  1445 ++
 .../gnu/emacs/EmacsWindowAttachmentManager.java    |   230 +
 java/res/drawable/emacs.png                        |   Bin 0 -> 13462 bytes
 java/res/drawable/emacs_wrench.png                 |   Bin 0 -> 24996 bytes
 java/res/values-v11/style.xml                      |    24 +
 java/res/values-v14/style.xml                      |    25 +
 java/res/values-v19/bool.xml                       |    22 +
 java/res/values-v24/bool.xml                       |    22 +
 java/res/values-v29/style.xml                      |    32 +
 java/res/values/bool.xml                           |    23 +
 java/res/values/strings.xml                        |    45 +
 java/res/values/style.xml                          |    26 +
 java/res/xml/preferences.xml                       |    30 +
 lib-src/Makefile.in                                |    23 +-
 lib-src/asset-directory-tool.c                     |   289 +
 lib-src/emacsclient.c                              |     2 +
 lib/Makefile.in                                    |    28 +-
 lib/getdelim.c                                     |   143 +
 lib/getline.c                                      |    27 +
 lib/gnulib.mk.in                                   |   140 +-
 lib/mini-gmp.c                                     |    11 +-
 lib/stdalign.in.h                                  |    49 +
 lib/stpncpy.c                                      |    92 +
 lisp/ChangeLog.16                                  |     3 +-
 lisp/align.el                                      |    29 +-
 lisp/battery.el                                    |    80 +
 lisp/bindings.el                                   |     9 +-
 lisp/button.el                                     |    30 +-
 lisp/calc/calc.el                                  |    96 +-
 lisp/cedet/semantic/db-ebrowse.el                  |     3 +-
 lisp/comint.el                                     |     3 +
 lisp/cus-edit.el                                   |    10 +-
 lisp/dired-aux.el                                  |    35 +-
 lisp/dired.el                                      |   160 +-
 lisp/doc-view.el                                   |    54 +-
 lisp/elec-pair.el                                  |    14 +
 lisp/emacs-lisp/advice.el                          |     1 +
 lisp/emacs-lisp/byte-opt.el                        |     7 +-
 lisp/emacs-lisp/bytecomp.el                        |    12 +-
 lisp/emacs-lisp/checkdoc.el                        |    10 +-
 lisp/emacs-lisp/eldoc.el                           |     3 +-
 lisp/emacs-lisp/lisp-mnt.el                        |    26 +-
 lisp/emacs-lisp/loaddefs-gen.el                    |     3 +-
 lisp/emacs-lisp/oclosure.el                        |     2 +-
 lisp/emacs-lisp/package.el                         |     2 +-
 lisp/emacs-lisp/rx.el                              |   218 +-
 lisp/emacs-lisp/shortdoc.el                        |    20 +-
 lisp/emacs-lisp/subr-x.el                          |     9 +-
 lisp/emulation/viper-cmd.el                        |    10 +-
 lisp/erc/erc-nicks.el                              |     2 +-
 lisp/eshell/em-hist.el                             |     2 +-
 lisp/eshell/em-smart.el                            |     4 +-
 lisp/eshell/em-term.el                             |     5 +-
 lisp/eshell/esh-util.el                            |    56 +-
 lisp/faces.el                                      |    78 +-
 lisp/files.el                                      |    29 +-
 lisp/find-dired.el                                 |     4 +-
 lisp/frame.el                                      |    88 +-
 lisp/gnus/gmm-utils.el                             |     3 +-
 lisp/gnus/gnus-score.el                            |    70 +-
 lisp/gnus/mail-source.el                           |     2 +-
 lisp/help-fns.el                                   |     4 +
 lisp/help-macro.el                                 |     9 +-
 lisp/hexl.el                                       |     2 +-
 lisp/htmlfontify.el                                |     5 +-
 lisp/ibuf-ext.el                                   |     6 +-
 lisp/ielm.el                                       |     2 +-
 lisp/image/image-dired-external.el                 |     3 +-
 lisp/image/image-dired-util.el                     |    29 +-
 lisp/image/image-dired.el                          |    14 +-
 lisp/image/wallpaper.el                            |     2 +
 lisp/international/characters.el                   |   217 +-
 lisp/international/fontset.el                      |    17 +-
 lisp/international/mule-cmds.el                    |     3 +-
 lisp/isearch.el                                    |    27 +
 lisp/language/chinese.el                           |     5 +
 lisp/language/japanese.el                          |     1 +
 lisp/language/korean.el                            |     1 +
 lisp/loadup.el                                     |   174 +-
 lisp/ls-lisp.el                                    |     2 +-
 lisp/mail/emacsbug.el                              |     6 +
 lisp/mail/footnote.el                              |     7 +-
 lisp/mail/rmail.el                                 |     6 +-
 lisp/menu-bar.el                                   |    49 +-
 lisp/minibuffer.el                                 |    72 +-
 lisp/mouse.el                                      |    12 +-
 lisp/mwheel.el                                     |    12 +-
 lisp/net/browse-url.el                             |    35 +
 lisp/net/eww.el                                    |     8 +-
 lisp/net/newst-backend.el                          |    12 +-
 lisp/net/newsticker.el                             |     4 +-
 lisp/net/tramp-archive.el                          |     6 +-
 lisp/net/tramp-cache.el                            |     9 +-
 lisp/net/tramp-cmds.el                             |     8 +
 lisp/net/tramp-compat.el                           |     3 +-
 lisp/net/tramp-crypt.el                            |     2 +-
 lisp/net/tramp-integration.el                      |     3 +
 lisp/net/tramp-message.el                          |    26 +-
 lisp/net/tramp-sh.el                               |   108 +-
 lisp/net/tramp-sudoedit.el                         |    33 +-
 lisp/net/tramp.el                                  |   124 +-
 lisp/nxml/nxml-maint.el                            |     2 +-
 lisp/nxml/rng-util.el                              |    23 +-
 lisp/obsolete/iswitchb.el                          |     2 +-
 lisp/obsolete/terminal.el                          |     4 +-
 lisp/org/org-ctags.el                              |     4 +-
 lisp/pcomplete.el                                  |     8 +-
 lisp/pixel-scroll.el                               |     2 +
 lisp/play/doctor.el                                |     5 +
 lisp/play/dunnet.el                                |     5 +
 lisp/play/gamegrid.el                              |     4 +-
 lisp/progmodes/cc-cmds.el                          |    35 +
 lisp/progmodes/cc-mode.el                          |    28 +-
 lisp/progmodes/cperl-mode.el                       |   117 +-
 lisp/progmodes/eglot.el                            |    12 +-
 lisp/progmodes/prog-mode.el                        |     2 +
 lisp/progmodes/project.el                          |     5 -
 lisp/progmodes/python.el                           |     4 +-
 lisp/progmodes/ruby-mode.el                        |     6 +-
 lisp/progmodes/typescript-ts-mode.el               |     2 +-
 lisp/saveplace.el                                  |    14 +-
 lisp/shell.el                                      |     7 +-
 lisp/simple.el                                     |   184 +-
 lisp/speedbar.el                                   |     2 +-
 lisp/sqlite-mode.el                                |     2 +-
 lisp/startup.el                                    |    13 +
 lisp/subr.el                                       |    72 +-
 lisp/tab-bar.el                                    |    85 +-
 lisp/tab-line.el                                   |   169 +-
 lisp/term.el                                       |    12 +-
 lisp/term/android-win.el                           |   237 +
 lisp/textmodes/artist.el                           |     4 +-
 lisp/textmodes/conf-mode.el                        |     7 +-
 lisp/textmodes/less-css-mode.el                    |     2 +-
 lisp/textmodes/reftex-global.el                    |     6 +-
 lisp/textmodes/remember.el                         |     2 +-
 lisp/textmodes/rst.el                              |     2 +-
 lisp/textmodes/sgml-mode.el                        |     1 -
 lisp/textmodes/text-mode.el                        |     6 +
 lisp/textmodes/toml-ts-mode.el                     |     4 +-
 lisp/tool-bar.el                                   |   273 +-
 lisp/touch-screen.el                               |  1579 ++
 lisp/use-package/bind-key.el                       |     1 +
 lisp/use-package/use-package-core.el               |     8 +-
 lisp/version.el                                    |    72 +-
 lisp/wid-edit.el                                   |   126 +-
 m4/getdelim.m4                                     |   114 +
 m4/getline.m4                                      |   111 +
 m4/gnulib-comp.m4                                  |    40 +
 m4/ndk-build.m4                                    |   480 +
 m4/printf-posix-rpl.m4                             |    26 +
 m4/stpncpy.m4                                      |   108 +
 make-dist                                          |     3 +-
 msdos/sed1v2.inp                                   |    12 +
 msdos/sed3v2.inp                                   |     1 +
 msdos/sedlibcf.inp                                 |     1 +
 msdos/sedlibmk.inp                                 |    18 +-
 nt/gnulib-cfg.mk                                   |    35 +-
 src/ChangeLog.12                                   |     2 +-
 src/Makefile.in                                    |   108 +-
 src/alloc.c                                        |   151 +-
 src/android-asset.h                                |   422 +
 src/android-emacs.c                                |   169 +
 src/android.c                                      |  6933 +++++++
 src/android.h                                      |   323 +
 src/androidfns.c                                   |  3266 +++
 src/androidfont.c                                  |  1104 ++
 src/androidgui.h                                   |   754 +
 src/androidmenu.c                                  |   844 +
 src/androidselect.c                                |   506 +
 src/androidterm.c                                  |  6523 ++++++
 src/androidterm.h                                  |   481 +
 src/androidvfs.c                                   |  7193 +++++++
 src/buffer.c                                       |    22 +
 src/buffer.h                                       |    11 +
 src/callint.c                                      |     6 +-
 src/callproc.c                                     |   110 +-
 src/character.c                                    |     8 +
 src/charset.c                                      |     7 +-
 src/coding.c                                       |    27 +-
 src/coding.h                                       |     4 +-
 src/conf_post.h                                    |    10 +
 src/dired.c                                        |    74 +-
 src/dispextern.h                                   |    68 +-
 src/dispnew.c                                      |    51 +-
 src/doc.c                                          |    77 +-
 src/editfns.c                                      |     8 +
 src/emacs-module.c                                 |   162 +-
 src/emacs.c                                        |   163 +-
 src/epaths.in                                      |    22 +
 src/fileio.c                                       |   375 +-
 src/filelock.c                                     |    55 +-
 src/fns.c                                          |     4 +
 src/font.c                                         |    38 +-
 src/font.h                                         |    14 +
 src/fontset.c                                      |    27 +
 src/frame.c                                        |    65 +-
 src/frame.h                                        |   126 +-
 src/fringe.c                                       |    23 +-
 src/image.c                                        |   738 +-
 src/inotify.c                                      |    19 +-
 src/keyboard.c                                     |   623 +-
 src/keyboard.h                                     |    21 +-
 src/keymap.c                                       |     6 +-
 src/lisp.h                                         |    33 +-
 src/lread.c                                        |   328 +-
 src/marker.c                                       |    26 +
 src/menu.c                                         |    20 +-
 src/pdumper.c                                      |    24 +-
 src/print.c                                        |    20 +-
 src/process.c                                      |    37 +-
 src/scroll.c                                       |     7 +
 src/sfnt.c                                         | 19768 +++++++++++++++++++
 src/sfnt.h                                         |  1994 ++
 src/sfntfont-android.c                             |   793 +
 src/sfntfont.c                                     |  3878 ++++
 src/sfntfont.h                                     |    79 +
 src/sound.c                                        |     2 +-
 src/sysdep.c                                       |   260 +-
 src/term.c                                         |   151 +-
 src/termhooks.h                                    |    22 +-
 src/terminal.c                                     |     2 +
 src/textconv.c                                     |  1947 +-
 src/textconv.h                                     |    52 +-
 src/verbose.mk.in                                  |    11 +
 src/w32.c                                          |     3 +-
 src/w32fns.c                                       |     4 +-
 src/w32font.c                                      |     2 +-
 src/w32proc.c                                      |     2 +-
 src/w32term.h                                      |     2 +-
 src/window.c                                       |     5 +-
 src/window.h                                       |    25 +-
 src/xdisp.c                                        |   291 +-
 src/xfaces.c                                       |    65 +-
 src/xfns.c                                         |     7 +-
 src/xterm.c                                        |    86 +-
 src/xterm.h                                        |     7 +
 test/lisp/align-resources/align-post.c             |     3 -
 test/lisp/align-resources/align-post.java          |     9 -
 test/lisp/align-resources/align-pre.c              |     3 -
 test/lisp/align-resources/align-pre.java           |     9 -
 test/lisp/align-resources/align-regexp.erts        |    13 +
 test/lisp/align-resources/c-mode.erts              |    23 +
 test/lisp/align-resources/conf-toml-mode.erts      |    45 +
 test/lisp/align-resources/css-mode.erts            |    23 +
 test/lisp/align-resources/java-mode.erts           |    23 +
 test/lisp/align-resources/latex-mode.erts          |    29 +
 test/lisp/align-resources/python-mode.erts         |    29 +
 test/lisp/align-tests.el                           |    51 +-
 test/lisp/emacs-lisp/bytecomp-tests.el             |    34 +
 test/lisp/emacs-lisp/nadvice-tests.el              |    34 +-
 test/lisp/emacs-lisp/rx-tests.el                   |    62 +-
 test/lisp/eshell/esh-util-tests.el                 |    31 +
 test/lisp/image/image-dired-util-tests.el          |    21 +-
 test/lisp/net/tramp-tests.el                       |    12 +-
 .../progmodes/cperl-mode-resources/sub-names.pl    |    25 +
 test/lisp/progmodes/cperl-mode-tests.el            |    33 +
 test/lisp/textmodes/conf-mode-tests.el             |    26 +-
 test/lisp/uniquify-tests.el                        |    15 -
 test/src/treesit-tests.el                          |     1 +
 389 files changed, 107193 insertions(+), 1937 deletions(-)

diff --git a/.dir-locals.el b/.dir-locals.el
index 0bcded4b5d1..f0f12db6717 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -12,7 +12,7 @@
  (c-mode . ((c-file-style . "GNU")
             (c-noise-macro-names . ("INLINE" "NO_INLINE" 
"ATTRIBUTE_NO_SANITIZE_UNDEFINED"
                                     "UNINIT" "CALLBACK" "ALIGN_STACK" 
"ATTRIBUTE_MALLOC"
-                                    "ATTRIBUTE_DEALLOC_FREE"))
+                                    "ATTRIBUTE_DEALLOC_FREE" "ANDROID_EXPORT"))
             (electric-quote-comment . nil)
             (electric-quote-string . nil)
             (indent-tabs-mode . t)
diff --git a/.gitignore b/.gitignore
index e3a628eecb8..b1bf157cb3d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,6 +52,23 @@ src/config.h
 src/epaths.h
 src/emacs-module.h
 
+# Built by recursive call to `configure'.
+*.android
+!INSTALL.android
+!verbose.mk.android
+
+# Built by `javac'.
+java/install_temp/*
+java/*.apk*
+java/*.dex
+java/org/gnu/emacs/*.class
+
+# Built by `aapt'.
+java/org/gnu/emacs/R.java
+
+# Built by `config.status'.
+java/AndroidManifest.xml
+
 # C-level sources built by 'make'.
 lib/alloca.h
 lib/assert.h
@@ -70,8 +87,10 @@ lib/limits.h
 lib/malloc/*.gl.h
 lib/signal.h
 lib/std*.h
+lib/math.h
 !lib/std*.in.h
 !lib/stdio-impl.h
+!lib/_Noreturn.h
 lib/string.h
 lib/sys/
 lib/time.h
@@ -81,6 +100,19 @@ src/globals.h
 src/lisp.mk
 src/verbose.mk
 
+# Stuff built during cross compilation
+cross/lib/*
+cross/src/*
+cross/lib-src/*
+cross/sys/*
+cross/config.status
+cross/*.bak
+cross/etc/DOC
+
+cross/ndk-build/Makefile
+cross/ndk-build/ndk-build.mk
+cross/ndk-build/*.o
+
 # Lisp-level sources built by 'make'.
 *cus-load.el
 *loaddefs.el
@@ -186,6 +218,7 @@ ID
 # Executables.
 *.exe
 a.out
+lib-src/asset-directory-tool
 lib-src/be-resources
 lib-src/blessmail
 lib-src/ctags
@@ -208,6 +241,7 @@ nextstep/GNUstep/Emacs.base/Resources/Info-gnustep.plist
 src/bootstrap-emacs
 src/emacs
 src/emacs-[0-9]*
+src/sfnt
 src/Emacs
 src/temacs
 src/dmpstruct.h
@@ -339,3 +373,15 @@ lib-src/seccomp-filter-exec.pfc
 .gdb_history
 _gdb_history
 *.desktop
+
+# Files ignored in exec/.
+exec/config.status
+exec/loader
+exec/test
+exec/exec1
+exec/deps/*
+exec/autom4te.cache
+exec/config.h
+exec/config-mips.m4
+exec/configure
+exec/*.s.s
diff --git a/ChangeLog.4 b/ChangeLog.4
index 9d64446d68e..372b03b32b2 100644
--- a/ChangeLog.4
+++ b/ChangeLog.4
@@ -116226,7 +116226,7 @@
 
        Do not quote lambda expressions
 
-       http://emacs.stackexchange.com/a/3596
+       https://emacs.stackexchange.com/a/3596
 
        Quoting lambda expressions is at best redundant and at worst
        detrimental; this commit removes all use of the sharp-quote to reduce
@@ -116960,7 +116960,7 @@
 
        This change follows the regexp for require on emacs truck. See line
        2327 on font-lock.el in the following patch.
-       http://bzr.savannah.gnu.org/lh/emacs/trunk/revision/111821
+       https://bzr.savannah.gnu.org/lh/emacs/trunk/revision/111821
 
 2013-09-04  John Wiegley  <johnw@newartisans.com>
 
diff --git a/ChangeLog.android b/ChangeLog.android
new file mode 100644
index 00000000000..3cb88b6f10c
--- /dev/null
+++ b/ChangeLog.android
@@ -0,0 +1,7961 @@
+2023-08-07  Po Lu  <luangruo@yahoo.com>
+
+       * nt/mingw-cfg.site: Remove additions for Gnulib printf.
+
+       * m4, lib: Update from Gnulib.
+
+       * msdos/sedlibmk.inp: Remove variables deleted as part of previous
+       change.
+
+       * admin/merge-gnulib (GNULIB_MODULES): Remove vasprintf and
+       printf-posix.
+
+2023-08-06  Po Lu  <luangruo@yahoo.com>
+
+       * java/org/gnu/emacs/EmacsService.java (readDirectoryEntry): Fix
+       potential NULL dereference.
+
+       * java/org/gnu/emacs/EmacsSafThread.java (openDocument1): If
+       initially opening with rwt, verify the file descriptor is really
+       writable; if not, resort to rw and truncating the file descriptor
+       by hand instead.
+
+       * src/androidvfs.c (NATIVE_NAME (ftruncate)): New function.
+       Truncate file descriptor and return whether that was successful.
+
+       * src/androidvfs.c (android_saf_tree_chmod): Repair file access
+       permissions allowed within FLAGS.
+
+2023-08-05  Po Lu  <luangruo@yahoo.com>
+
+       * doc/lispref/commands.texi (Touchscreen Events): Fix typo.
+
+       * lisp/subr.el (y-or-n-p): Don't call set-text-conversion-style
+       when not present.
+
+2023-08-04  Po Lu  <luangruo@yahoo.com>
+
+       * ChangeLog.android: New file.
+
+2023-08-04  Po Lu  <luangruo@yahoo.com>
+
+       Optimize creation of multibyte menu items on Android
+       * src/androidvfs.c (android_verify_jni_string): Move to
+       android.c.
+       * src/android.c (android_verify_jni_string): New function.
+       (android_build_string): Forgo encoding menu text if TEXT is a
+       multibyte string that's also a valid JNI string.
+       * src/android.h: Update prototypes.
+
+       Avoid encoding commonplace characters in tree names
+       * java/org/gnu/emacs/EmacsService.java (getDocumentTrees): Don't
+       encode some characters that need not be escaped within file
+       names.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-08-03  Po Lu  <luangruo@yahoo.com>
+
+       * src/fileio.c (check_vfs_filename): Revert earlier change.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Isolate fchmodat within the Android VFS layer
+       * src/android.h: Update prototypes.
+       * src/androidvfs.c (unix_vfs_ops, android_unix_chmod, afs_vfs_ops)
+       (android_afs_chmod, content_vfs_ops, android_content_chmod)
+       (authority_vfs_ops, android_authority_chmod, saf_root_vfs_ops)
+       (android_saf_root_chmod, saf_tree_vfs_ops, android_saf_tree_chmod)
+       (saf_file_vfs_ops, saf_new_vfs_ops, android_saf_new_chmod)
+       (root_vfs_ops): Add `chmod' to the list of functions implemented
+       by each vnode.
+       (android_fchmodat): New function.
+       * src/fileio.c (Fset_file_modes): Use `emacs_fchmodat'.
+       * src/lisp.h:
+       * src/sysdep.c (emacs_fchmodat): Delegate to android_fchmodat on
+       Android.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsSafThread.java (CacheToplevel):
+       (EmacsSafThread):
+       (DocIdEntry):
+       (getCache):
+       (pruneCache):
+       (cacheDirectoryFromCursor):
+       (run):
+       (documentIdFromName1):
+       (statDocument1):
+       (openDocumentDirectory1):
+       (openDocument1): Introduce a file status cache and populate
+       it with files within directories as they are opened.
+       * java/org/gnu/emacs/EmacsService.java (createDocument):
+       (createDirectory):
+       (moveDocument): Invalidate the file status cache wherever
+       needed.
+       * src/fileio.c (check_vfs_filename):
+       (Fset_file_modes): Permit `set-file-modes' to silently fail
+       on asset and content files.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-08-02  Po Lu  <luangruo@yahoo.com>
+
+       Fix reporting of key events containing SYM and META
+       * doc/emacs/android.texi (Android)::(What is Android?):
+       (Android Startup, Android File System, Android Environment)
+       (Android Windowing, Android Fonts, Android Troubleshooting):
+       Improve section titles.
+       (Android Windowing): Describe the relation between keyboard
+       modifiers reported by Android and those in key events.
+       * java/org/gnu/emacs/EmacsWindow.java (onKeyDown, onKeyUp):
+       Clear META_SYM_ON and META_META_MASK when retrieving ASCII
+       characters.
+       * src/androidgui.h: Add ANDROID_META_MASK.
+       * src/androidterm.c (android_android_to_emacs_modifiers)
+       (android_emacs_to_android_modifiers): Transform META to Alt, and
+       vice versa.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-08-01  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * doc/emacs/android.texi (Android File System): Describe how to
+       access real files named /assets or /contents if so required.
+       * java/org/gnu/emacs/EmacsService.java (validAuthority):
+       * src/android.c (android_init_emacs_service):
+       * src/android.h: New function.
+       * src/androidvfs.c (android_saf_valid_authority_p): New
+       function.  Wrap the Java function.
+       (android_saf_root_stat, android_saf_root_access): Don't return
+       success if no authority by vp->authority's name exists.
+       (android_saf_tree_from_name): Check validity of string data
+       before giving it to JNI.
+
+       Micro-optimize PUSHW/PUSHB
+       * src/sfnt.c (CHECK_STACK_AVAILABLE): New macro.
+       (PUSH):
+       (PUSH_UNCHECKED): Always define to unchecked versions,
+       even if TEST.
+       (PUSH2_UNCHECKED): New macro.
+       (NPUSHB):
+       (NPUSHW):
+       (PUSHB):
+       (PUSHW): Check the number of remaining stack elements
+       once.
+       (stack_overflow_test_args): Expect zero stack arguments.
+
+       * src/android.c (ANDROID_THROW): Remove unused macro.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-31  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * src/sfnt.c (ISECT): Micro-optimize this instruction.
+       * src/sfntfont.c (sfnt_parse_style): Avoid GC safety problem.
+
+       Update Android port
+       * src/sfntfont.c (sfnt_parse_style): Fix misworded commentary.
+
+       Initialize Android API level earlier
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative):
+       * java/org/gnu/emacs/EmacsNoninteractive.java (main):
+       * java/org/gnu/emacs/EmacsService.java (run):
+       * java/org/gnu/emacs/EmacsThread.java (run):
+       * src/android.c (initEmacs, setEmacsParams): Set
+       `android_api_level' within setEmacsParams, not in initEmacs.
+       * src/androidvfs.c: Pacify compiler warnings.
+
+       Implement cross-directory SAF rename operations
+       * java/org/gnu/emacs/EmacsService.java (renameDocument): Don't
+       catch UnsupportedOperationException; handle ENOSYS in
+       android_saf_rename_document instead.
+       (moveDocument): New function.
+       * lisp/subr.el (y-or-n-p): Always change the text conversion
+       style.
+       * src/android.c (android_init_emacs_service)
+       (android_exception_check_4): New function.
+       * src/android.h: Update Java function table.
+       * src/androidvfs.c (android_saf_rename_document): Handle ENOSYS
+       here by setting errno to EXDEV.
+       (android_saf_move_document): New function.
+       (android_document_id_from_name): Take const `dir_name'.
+       (android_saf_tree_rename): Use delete-move-rename to implement
+       cross-directory renames.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-30  Po Lu  <luangruo@yahoo.com>
+
+       Partially implement rename operations on SAF files
+       * java/org/gnu/emacs/EmacsSafThread.java
+       (postInvalidateCacheDir):
+       * java/org/gnu/emacs/EmacsService.java (renameDocument): New
+       functions.
+       * src/android.c (android_init_emacs_service):
+       * src/android.h (struct android_emacs_service): Link to new JNI
+       function.
+       * src/androidvfs.c (android_saf_rename_document): New function.
+       (android_saf_tree_rename): Implement in terms of that function
+       if possible.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-29  Po Lu  <luangruo@yahoo.com>
+
+       Correct directory permissions reported for VFS files
+       * java/org/gnu/emacs/EmacsSafThread.java (statDocument1):
+       * src/androidvfs.c (android_afs_stat, android_content_stat)
+       (android_saf_root_stat): Report search permissions for files.
+
+       Update Android port
+       * src/androidvfs.c: Improve commentary.
+
+2023-07-29  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsSafThread.java (postInvalidateCache):
+       New argument cacheName.  Remove that file from the cache.
+       (accessDocument1): Consult the storage cache as well.
+       * java/org/gnu/emacs/EmacsService.java (deleteDocument): New
+       argument NAME.
+
+       * src/android.c (android_init_emacs_service): Add new argument.
+       * src/androidvfs.c (android_saf_delete_document)
+       (android_saf_tree_rmdir, android_saf_file_unlink): Pass name of
+       file being deleted to `deleteDocument'.
+
+2023-07-29  Po Lu  <luangruo@yahoo.com>
+
+       ; Update Android port
+       * src/androidvfs.c (android_saf_exception_check): Describe
+       exceptions earlier.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsSafThread.java (DocIdEntry):
+       (getCacheEntry):
+       (CacheEntry):
+       (documentIdFromName1): Fix earlier change.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsSafThread.java (DocIdEntry)
+       (getCacheEntry, CacheEntry): Use `uptimeMillis' as the basis for
+       cache expiration.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsSafThread.java (EmacsSafThread, getCache)
+       (pruneCache1, pruneCache, cacheChild, cacheDirectoryFromCursor)
+       (documentIdFromName1, openDocumentDirectory1): Implement the
+       cache referred to by the commentary.
+       * java/org/gnu/emacs/EmacsService.java (deleteDocument):
+       Invalidate the cache upon document removal.
+       * src/androidvfs.c (android_saf_exception_check)
+       (android_document_id_from_name): Correctly preserve or set errno
+       in error cases.
+
+2023-07-28  Po Lu  <luangruo@yahoo.com>
+
+       Fix SAF query
+       * java/org/gnu/emacs/EmacsSafThread.java (documentIdFromName1):
+       Fix query argument placeholder string.
+
+       Update Android port
+       * src/androidvfs.c (android_document_id_from_name): Don't return
+       0 if an SAF exception occurs.
+
+       Avoid crashes when the primary clip is empty
+       * src/androidselect.c (Fandroid_get_clipboard): Don't return
+       data if clipboard is empty.  Reported by Johan Widén
+       <j.e.widen@gmail.com>.
+
+2023-07-28  Po Lu  <luangruo@yahoo.com>
+
+       Allow quitting from Android content provider operations
+       * doc/emacs/android.texi (Android Document Providers): Say that
+       quitting is now possible.
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New
+       functions `safSyncAndReadInput', `safync' and `safPostRequest'.
+       * java/org/gnu/emacs/EmacsSafThread.java: New file.  Move
+       cancel-able SAF operations here.
+       * java/org/gnu/emacs/EmacsService.java (EmacsService): Allow
+       quitting from most SAF operations.
+       * src/androidvfs.c (android_saf_exception_check): Return EINTR
+       if OperationCanceledException is received.
+       (android_saf_stat, android_saf_access)
+       (android_document_id_from_name, android_saf_tree_opendir_1)
+       (android_saf_file_open): Don't allow reentrant calls from async
+       input handlers.
+       (NATIVE_NAME): Implement new synchronization primitives for JNI.
+       (android_vfs_init): Initialize new class.
+
+       * src/dired.c (open_directory): Handle EINTR from opendir.
+       * src/sysdep.c: Describe which operations may return EINTR on
+       Android.
+
+2023-07-28  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsDirectoryEntry.java
+       (EmacsDirectoryEntry): Make class final.
+       * java/org/gnu/emacs/EmacsService.java (accessDocument)
+       (openDocumentDirectory, openDocument, createDocument): Throw
+       access and IO error exceptions instead of catching them.
+       (createDirectory, deleteDocument): New functions.
+       * src/android.c (android_init_emacs_service): Add new functions.
+       * src/android.h (struct android_emacs_service): Likewise.
+       * src/androidvfs.c (android_saf_exception_check): New function.
+       Translate between Java exceptions and errno values.
+       (android_saf_stat, android_saf_access, android_saf_delete_document)
+       (struct android_saf_tree_vnode, android_document_id_from_name)
+       (android_saf_tree_name, android_saf_tree_rmdir)
+       (android_saf_tree_opendir_1, android_saf_tree_opendir)
+       (android_saf_file_open, android_saf_file_unlink)
+       (android_saf_new_open, android_saf_new_mkdir): Implement missing
+       VFS operations and derive errno values from the type of any
+       exceptions thrown.
+       (android_vfs_init): Initialize exception classes.
+       (android_mkdir, android_fstat): Remove trailing whitespace.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-27  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * doc/emacs/android.texi (Android Document Providers): Improve
+       wording of paragraph clarifying limits on subprocesses.
+       * java/org/gnu/emacs/EmacsService.java (getDocumentTrees): Use
+       Java standard US-ASCII coding standard instead of the
+       undocumented ``ASCII'' alias.
+       (decodeFileName): Remove unused function.
+       (documentIdFromName):
+       * src/android.c (android_init_emacs_service): Take a String for
+       NAME instead of a byte array.
+       * src/androidvfs.c (android_verify_jni_string): New function.
+       (android_document_id_from_name): Verify that STRING is a valid
+       Modified UTF-8 string.
+
+       Update Android port
+       * src/androidvfs.c (android_afs_initial):
+       (android_content_get_directory_name):
+       (android_saf_tree_name):
+       (android_saf_tree_from_name):
+       (android_vfs_init): Silence compiler warnings.
+
+       Update Android port
+       * src/android.c (android_run_in_emacs_thread): Behave more
+       robustly if SIGIO arrives too late Emacs for Emacs to check for
+       signals, but too early to preempt a long running syscall.
+
+       Avoid crashes in some edge cases
+       * java/org/gnu/emacs/EmacsActivity.java (onActivityResult):
+       Avoid crashes in some edge cases.
+
+       Avoid dereference of a freed vnode's operations table
+       * src/androidvfs.c (android_renameat_noreplace):
+       (android_rename): Free vdst using vdst->ops, not vp->ops.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-27  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * configure.ac (ANDROID_STUBIFY): Add androidvfs.o when building
+       libemacs.so.
+       * doc/emacs/android.texi (Android): Add `Android Document Providers'.
+       (Android Startup): Update the location of the content identifier
+       directory.
+       (Android File System): Describe access to document provider
+       directories.
+       (Android Document Providers): New node.
+       * doc/emacs/emacs.texi (Top): Update the menu for the Android
+       appendix.
+       * java/Makefile.in (filename, install_temp/assets/build_info): Make
+       directory-tree depend on build_info.
+       * java/org/gnu/emacs/EmacsActivity.java (onActivityResult): New
+       function.  When a document tree is accepted, persist access to it.
+       * java/org/gnu/emacs/EmacsDirectoryEntry.java (EmacsDirectoryEntry):
+       New struct.
+       * java/org/gnu/emacs/EmacsOpenActivity.java (checkReadableOrCopy): Use
+       EmacsService.buildContentName.
+       * java/org/gnu/emacs/EmacsService.java (getEmacsView, openContentUri)
+       (checkContentUri): Remove excessive debug logging.
+       (buildContentName, getDocumentAuthorities, requestDirectoryAccess)
+       (getDocumentTrees, decodeFileName, documentIdFromName, getTreeUri)
+       (statDocument, accessDocument, openDocumentDirectory, 
readDirectoryEntry)
+       (openDocument, createDocument): New functions.
+
+       * lib-src/asset-directory-tool.c: Improve commentary by illustrating
+       the difference between directory and ordinary files.
+
+       * src/android.c (ANDROID_THROW, enum android_fd_table_entry_flags)
+       (struct android_emacs_service, android_extract_long)
+       (android_scan_directory_tree, android_is_directory)
+       (android_get_asset_name, android_url_encode, android_content_name_p)
+       (android_get_content_name, android_check_content_access, android_fstat)
+       (android_fstatat, android_file_access_p, android_hack_asset_fd_fallback)
+       (android_detect_ashmem, android_hack_asset_fd, android_close_on_exec)
+       (android_open, android_close, android_fclose, android_create_lib_link)
+       (android_faccessat, struct android_dir, android_opendir, android_dirfd)
+       (android_readdir, android_closedir, android_lookup_asset_directory_fd)
+       (android_exception_check_3, android_get_current_api_level)
+       (android_open_asset, android_close_asset, android_asset_read_quit)
+       (android_asset_read, android_asset_lseek, android_asset_fstat): Move
+       content and asset related functions to androidvfs.c.
+       (android_init_emacs_service): Obtain handles for new JNI functions.
+       (initEmacsParams): Initialize the VFS layer.
+       (android_request_directory_access): New function.
+       (android_display_toast): Remove unused function.
+
+       * src/android.h (android_get_current_api_level): Assume that
+       this function never returns less than __ANDROID_API__.
+       (struct android_emacs_service): Move `struct
+       android_emacs_service' here.
+
+       * src/androidfns.c (Fandroid_request_directory_access): New
+       interactive function.
+       (syms_of_androidfns): Register new subr.
+
+       * src/androidvfs.c (struct android_vdir, struct android_vops)
+       (struct android_vnode, struct android_special_vnode)
+       (enum android_vnode_type, struct android_cursor_class)
+       (struct emacs_directory_entry_class)
+       (struct android_parcel_file_descriptor_class)
+       (android_init_cursor_class, android_init_entry_class)
+       (android_init_fd_class, android_vfs_canonicalize_name)
+       (struct android_unix_vnode, struct android_unix_vdir, unix_vfs_ops)
+       (android_unix_name, android_unix_vnode, android_unix_open)
+       (android_unix_close, android_unix_unlink, android_unix_symlink)
+       (android_unix_rmdir, android_unix_rename, android_unix_stat)
+       (android_unix_access, android_unix_mkdir, android_unix_readdir)
+       (android_unix_closedir, android_unix_dirfd, android_unix_opendir)
+       (android_extract_long, android_scan_directory_tree)
+       (android_is_directory, android_init_assets)
+       (android_hack_asset_fd_fallback, android_detect_ashmem)
+       (android_hack_asset_fd, struct android_afs_vnode)
+       (struct android_afs_vdir, struct android_afs_open_fd, afs_vfs_ops)
+       (android_afs_name, android_afs_initial, android_close_on_exec)
+       (android_afs_open, android_afs_close, android_afs_unlink)
+       (android_afs_symlink, android_afs_rmdir, android_afs_rename)
+       (android_afs_stat, android_afs_access, android_afs_mkdir)
+       (android_afs_readdir, android_afs_closedir, android_afs_dirfd)
+       (android_afs_opendir, android_afs_get_directory_name)
+       (struct android_content_vdir, content_vfs_ops)
+       (content_directory_contents, android_content_name)
+       (android_content_open, android_content_close)
+       (android_content_unlink, android_content_symlink)
+       (android_content_rmdir, android_content_rename)
+       (android_content_stat, android_content_access)
+       (android_content_mkdir, android_content_readdir)
+       (android_content_closedir, android_content_dirfd)
+       (android_content_opendir, android_content_get_directory_name)
+       (android_content_initial, android_get_content_name)
+       (android_check_content_access, struct android_authority_vnode)
+       (authority_vfs_ops, android_authority_name, android_authority_open)
+       (android_authority_close, android_authority_unlink)
+       (android_authority_symlink, android_authority_rmdir)
+       (android_authority_rename, android_authority_stat)
+       (android_authority_access, android_authority_mkdir)
+       (android_authority_opendir, android_authority_initial)
+       (struct android_saf_root_vnode, struct android_saf_root_vdir)
+       (saf_root_vfs_ops, android_saf_root_name, android_saf_root_open)
+       (android_saf_root_close, android_saf_root_unlink)
+       (android_saf_root_symlink, android_saf_root_rmdir)
+       (android_saf_root_rename, android_saf_root_stat)
+       (android_saf_root_access, android_saf_root_mkdir)
+       (android_saf_root_readdir, android_saf_root_closedir)
+       (android_saf_root_dirfd, android_saf_root_opendir)
+       (android_saf_root_initial, android_saf_root_get_directory)
+       (android_saf_stat, android_saf_access)
+       (struct android_saf_tree_vnode, struct android_saf_tree_vdir)
+       (saf_tree_vfs_ops, android_document_id_from_name)
+       (android_saf_tree_name, android_saf_tree_open)
+       (android_saf_tree_close, android_saf_tree_unlink)
+       (android_saf_tree_symlink, android_saf_tree_rmdir)
+       (android_saf_tree_rename, android_saf_tree_stat)
+       (android_saf_tree_access, android_saf_tree_mkdir)
+       (android_saf_tree_opendir_1, android_saf_tree_readdir)
+       (android_saf_tree_closedir, android_saf_tree_dirfd)
+       (android_saf_tree_opendir, android_saf_tree_from_name)
+       (android_saf_tree_get_directory, android_saf_file_vnode)
+       (saf_file_vfs_ops, android_saf_file_name, android_saf_file_open)
+       (android_saf_file_unlink, android_saf_file_rmdir)
+       (android_saf_file_opendir, android_close_parcel_fd)
+       (android_saf_new_vnode, android_saf_new_name, android_saf_new_open)
+       (android_saf_new_unlink, android_saf_new_symlink)
+       (android_saf_new_rmdir, android_saf_new_rename)
+       (android_saf_new_stat, android_saf_new_access)
+       (android_saf_new_mkdir, android_saf_new_opendir, root_vfs_ops)
+       (special_vnodes, android_root_name, android_name_file)
+       (android_vfs_init, android_open, android_unlink, android_symlink)
+       (android_rmdir, android_mkdir, android_renameat_noreplace)
+       (android_rename, android_fstat, android_fstatat_1, android_fstatat)
+       (android_faccessat, android_fdopen, android_close, android_fclose)
+       (android_open_asset, android_close_asset, android_asset_read_quit)
+       (android_asset_read, android_asset_lseek, android_asset_fstat)
+       (android_opendir, android_dirfd, android_readdir)
+       (android_closedir): Move file system emulation routines here.
+       Introduce a new ``VFS'' layer for translating between
+       Emacs-specific file names and the various disparate interfaces
+       for accessing files on Android.
+
+       * src/callproc.c (delete_temp_file):
+       * src/charset.c (load_charset_map_from_file):
+       * src/dired.c:
+       * src/emacs.c (Fkill_emacs):
+       * src/fileio.c (check_mutable_filename, Fcopy_file)
+       (Fmake_directory_internal, Fdelete_directory_internal)
+       (Fdelete_file, Frename_file, Fadd_name_to_file)
+       (Fmake_symbolic_link, file_accessible_directory_p, Fset_file_modes)
+       (Fset_file_times, write_region):
+       * src/filelock.c (get_boot_time, rename_lock_file)
+       (create_lock_file, current_lock_owner, unlock_file):
+       * src/image.c (slurp_file, png_load_body, jpeg_load_body):
+       * src/keyboard.c (Fopen_dribble_file):
+       * src/lisp.h:
+       * src/lread.c (Fload):
+       * src/process.c (handle_child_signal):
+       * src/sysdep.c (init_standard_fds, emacs_fopen, emacs_fdopen)
+       (emacs_unlink, emacs_symlink, emacs_rmdir, emacs_mkdir)
+       (emacs_renameat_noreplace, emacs_rename):
+       * src/term.c (Fresume_tty, init_tty): Use and add new wrappers
+       for fopen, fdopen, unlink, symlink, rmdir, mkdir,
+       renameat_norepalce and rename.
+
+2023-07-26  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-24  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-23  Po Lu  <luangruo@yahoo.com>
+
+       Facilitate locating the app library directory
+       * doc/emacs/android.texi (Android File System): Document where
+       the app library directory can probably be found.
+       * src/android.c (android_create_lib_link): New function.  Try to
+       symlink `lib' in the directory holding the files directory to
+       the app library directory.
+       (setEmacsParams): Call that function if Emacs is being
+       initialized from an application context.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-22  Po Lu  <luangruo@yahoo.com>
+
+       Try harder to keep the initial word selected
+       * lisp/touch-screen.el (touch-screen-drag): If
+       touch-screen-word-select, also keep the initial word within the
+       region while scrolling.
+
+       Fix window box computation for menu bar windows
+       * src/window.h (WINDOW_MENU_BAR_P): Check for external menu bars
+       using HAVE_WINDOW_SYSTEM && HAVE_EXT_MENU_BAR instead of hard
+       coding X without Xt or GTK.
+
+       Update Android port
+       * doc/lispref/commands.texi (Key Sequence Input): Describe which
+       events receive imaginary prefix keys.
+       * lisp/touch-screen.el (touch-screen-translate-touch): Consider
+       `vertical-line' a virtual function key.
+       (function-key-map): Translate events on vertical window borders.
+
+       * etc/NEWS: Announce `current-key-remap-sequence'.
+
+       Fix default value of scroll bar frame parameters on Android
+       * src/androidfns.c (Fx_create_frame): Default
+       Qvertical_scroll_bars to Qnil, but set scroll-bar-width and
+       scroll-bar-height.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-21  Po Lu  <luangruo@yahoo.com>
+
+       Improve touch screen and text conversion behavior of many commands
+       * doc/lispref/commands.texi (Key Sequence Input): Document new
+       argument to `read-key-sequence' etc.
+       * lisp/help-macro.el (make-help-screen):
+       * lisp/subr.el (read-key, read-char-choice-with-read-key):
+       Disable text conversion and display the OSK before reading a key
+       sequence.
+       * lisp/touch-screen.el (touch-screen-window-selection-changed):
+       Only cancel the minibuffer OSK timer.
+       (touch-screen-handle-point-up): Update comment accordingly.
+       * src/keyboard.c (command_loop_1, read_menu_command)
+       (read_key_sequence, read_key_sequence_vs, Fread_key_sequence)
+       (Fread_key_sequence_vector): New arg DISABLE_TEXT_CONVERSION.
+       All callers changed.
+
+       Correctly translate touchscreen-up events outside a frame
+       * lisp/touch-screen.el (touch-screen-translate-touch): Check if
+       a prefix is specified separately from prefix being non-nil.
+       Accept `nil' as an imaginary prefix key.
+       (function-key-map): Register translation functions on the tab
+       bar, tab lines and internal border.
+
+       Improve touch screen scrolling support
+       * lisp/touch-screen.el (touch-screen-preview-select): Avoid
+       unnecessary redisplays.
+       (touch-screen-drag): Scroll at window margins using window
+       scrolling functions instead of relying on redisplay to recenter
+       the window around point.
+
+       Update Android port
+       * doc/emacs/input.texi (Touchscreens): Document
+       `touch-screen-preview-select'.
+       * doc/lispref/commands.texi (Touchscreen Events): Fix typo in
+       the descriptions of two touch screen events.
+       * lisp/dired.el (dired-insert-set-properties): Adjust for
+       changes to file end computation.
+       * lisp/minibuffer.el (clear-minibuffer-message): Don't clear
+       minibuffer message if dragging.
+       * lisp/touch-screen.el (touch-screen-current-tool): Fix doc
+       string.
+       (touch-screen-preview-select): New function.
+       (touch-screen-drag): Call it if point changes.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-20  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * exec/trace.c (handle_readlinkat): Adjust commentary to match
+       behavior.
+       * src/android.c (android_get_keysym_name): NULL terminate
+       *name_return.
+
+       Update some menu definitions for Android
+       * lisp/international/mule-cmds.el (set-coding-system-map): Don't
+       display `set-terminal-coding-system' on Android.
+
+       * lisp/cus-edit.el (custom-display): Add `android' display type.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * src/android.c (struct android_event_queue): Don't make
+       unnecessarily volatile.
+
+       Update Android port
+       * lisp/touch-screen.el (touch-screen-handle-touch): Don't
+       restart dragging if point is at ZV and the tap falls on a
+       different row.
+
+       Use context menu header titles on Android
+       * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu):
+       New field `title'.
+       (addSubmenu): New arg TITLE.  Set that field.
+       (expandTo): Set MENU's header title if it's a context menu.
+       * src/androidmenu.c (android_init_emacs_context_menu): Adjust
+       signature of `createContextMenu'.
+       (android_menu_show): Use TITLE instead of pane titles if there's
+       only one pane.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Introduce a `dired-click-select' mode
+       * doc/emacs/dired.texi (Marks vs Flags): Document command bound
+       to `touchscreen-hold'.
+       * doc/lispref/commands.texi (Touchscreen Events): Describe
+       `touch-screen-inhibit-drag'.
+       * etc/NEWS: Improve description of changes to touch screen
+       support.
+       * lisp/dired-aux.el (dired-do-chxxx, dired-do-chmod)
+       (dired-do-print, dired-do-shell-command, dired-do-compress-to)
+       (dired-do-create-files, dired-do-rename, dired-do-isearch)
+       (dired-do-isearch-regexp, dired-do-search)
+       (dired-do-query-replace-regexp, dired-do-find-regexp)
+       (dired-vc-next-action): Disable ``click to select'' after
+       running this command.
+       * lisp/dired.el (dired-insert-set-properties): Attach
+       click-to-select keymap to file names if necessary.
+       (dired-mode-map): Bind `touchscreen-hold' to click to select
+       mode.
+       (dired-post-do-command): New function.
+       (dired-do-delete): Call it.
+       (dired-mark-for-click, dired-enable-click-to-select-mode): New
+       functions.
+       (dired-click-to-select-mode): New minor mode.
+       * lisp/touch-screen.el (touch-screen-current-tool): Fix doc
+       string.
+       (touch-screen-inhibit-drag): New function.
+
+2023-07-19  Po Lu  <luangruo@yahoo.com>
+
+       * src/sfnt.c (sfnt_infer_deltas): Improve commentary.
+
+       Improve behavior of `restart-drag'
+       * lisp/touch-screen.el (touch-screen-handle-point-up): If what
+       is `restart-drag' (meaning that a drag has been restarted but
+       the touchpoint has not moved), translate the release into a
+       regular mouse click to deactivate the region.
+
+       Update Android port
+       * build-aux/makecounter.sh (curcount): Rename `counter' to
+       `emacs_shortlisp_counter'.
+       * doc/emacs/input.texi (Touchscreens): Document
+       `touch-screen-extend-selection'.
+       * doc/lispref/commands.texi (Touchscreen Events): Document
+       `touchscreen-restart-drag'.
+       * lisp/touch-screen.el (touch-screen-extend-selection): New user
+       option.
+       (touch-screen-restart-drag): New function.
+       (touch-screen-handle-point-update): Handle `restart-drag'
+       gestures.
+       (touch-screen-handle-touch): Check if the prerequisites for
+       extending a previous drag gesture are met, and generate such
+       events if so.
+       (touch-screen-translate-touch): Update doc string.
+       * src/Makefile.in (otherobj): Remove BUILD_COUNTER_OBJ.
+       ($(lispsource)/international/charprop.el):
+       (%.elc): Don't depend on bootstrap-emacs if cross configuring
+       for Android.
+       (libemacs.so): Directly depend on and link with
+       BUILD_COUNTER_OBJ.
+
+       Make sure Android builds are redumped upon changes to shortlisp
+       * build-aux/makecounter.sh: New script.
+       * src/Makefile.in (abs_top_builddir): New variable.
+       (BUILD_COUNTER_OBJ): Define to build-counter.o
+       if compiling for Android.
+       (build-counter.c): New target.  Generate this file using
+       makecounter.sh upon changes to lisp.mk or shortlisp.
+       (lisp.mk): Make and load relative to abs_top_builddir.
+       (emacs$(EXEEXT)): Adjust acordingly.
+       (mostlyclean): Remove build-counter.c.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-18  Po Lu  <luangruo@yahoo.com>
+
+       Fix typos in touch-screen.el
+       * lisp/touch-screen.el (touch-screen-handle-point-update)
+       (touch-screen-handle-point-up): Fix typos.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       * lisp/touch-screen.el (touch-screen-handle-point-update): Fix typo.
+
+       Avoid splurious menu-bar nil events
+       * src/keyboard.c (make_lispy_event): Return nil if no menu item
+       is found.
+
+       Update Android port
+       * lisp/touch-screen.el (touch-screen-hold)
+       (touch-screen-handle-point-up): Don't select inactive minibuffer
+       windows.
+       (touch-screen-handle-point-update): Improve detection of left
+       and right edges.
+
+       Update Android port
+       * lisp/touch-screen.el (touch-screen-handle-touch): Fix
+       treatment of stray update events.
+
+       Don't enable scroll-bar-mode by default on Android
+       * src/frame.c (syms_of_frame): Default to nil if HAVE_ANDROID.
+
+       * src/keyboard.c (make_lispy_event): Fix botched merge.
+
+       Update Android port
+       * doc/lispref/commands.texi (Touchscreen Events): Describe
+       treatment of canceled touch sequences during touch event
+       translation.
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Update JNI
+       prototypes.
+       * java/org/gnu/emacs/EmacsWindow.java (motionEvent): Set
+       cancelation flag in events sent where appropriate.
+       * lisp/touch-screen.el (touch-screen-handle-point-update):
+       Improve treatment of horizontal scrolling near window edges.
+       (touch-screen-handle-touch): Don't handle point up if the touch
+       sequence has been canceled.
+       * src/android.c (sendTouchDown, sendTouchUp, sendTouchMove): New
+       argument `flags'.
+       * src/androidgui.h (enum android_touch_event_flags): New enum.
+       (struct android_touch_event): New field `flags'.
+       * src/androidterm.c (handle_one_android_event): Report
+       cancelation in TOUCHSCREEN_END_EVENTs.
+       * src/keyboard.c (make_lispy_event): Fix botched merge.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-17  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * doc/lispref/commands.texi (Touchscreen Events): Document
+       meaning of `mouse-1-menu-command'.
+       * lisp/mouse.el (minor-mode-menu-from-indicator): New arg EVENT.
+       Give it to popup-menu.
+       (mouse-minor-mode-menu): Use posn specified within EVENT.
+       * lisp/touch-screen.el (touch-screen-handle-touch): Fix
+       interactive translation.  Treat commands labeled
+       `mouse-1-menu-command' like ordinary keymaps.
+
+       Update Android port
+       * doc/lispref/commands.texi (Touchscreen Events): Document
+       changes to simple translation.
+       * lisp/touch-screen.el (touch-screen-handle-point-up): Generate
+       `down-mouse-1' if the current gesture is `mouse-1-menu'.
+       (touch-screen-handle-touch): If binding is a keymap, set state
+       to `mouse-1-menu' and wait for point to be released before
+       generating down-mouse-1.
+
+       Improve word selection behavior
+       * lisp/tab-bar.el (tab-bar-map): Don't bind touch-screen-drag.
+       * lisp/touch-screen.el (touch-screen-drag): Extend the region
+       based on the position of the mark, not the position of point
+       relative to EVENT.
+       (touch-screen-translate-touch): Don't generate virtual function
+       keys for non-mouse events.
+       (function-key-map): Remove redundant definitions.
+       * src/keyboard.c (read_key_sequence): Don't generate *-bar
+       prefix keys for mock input (such as input from function key
+       translation.)
+
+       Improve touch screen support
+       * doc/emacs/input.texi (Touchscreens): Document the new feature
+       for people who have trouble dragging to word boundaries.
+       * lisp/touch-screen.el (touch-screen-word-select): New
+       defcustom.
+       (touch-screen-word-select-bounds)
+       (touch-screen-word-select-initial-word): New variable
+       definitions.
+       (touch-screen-hold): If `touch-screen-word-select', select the
+       word around EVENT.
+       (touch-screen-drag): If `touch-screen-word-select', extend the
+       region to the next word boundary if the character under point
+       constitutes a word.
+       (touch-screen-handle-point-update, touch-screen-handle-touch)
+       (touch-screen-translate-touch): Fix doc strings and fill
+       comments.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-16  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsService.java (displayToast):
+       * src/android.c (android_init_emacs_service): Remove unused
+       function.
+       * lisp/touch-screen.el (touch-screen-handle-point-up): Correctly
+       compute posn point.
+       (touch-screen-translate-touch): Update doc string.
+       (function-key-map): Define touch screen translation functions
+       within the internal border.
+
+       Update Android port
+       * doc/lispref/commands.texi (Touchscreen Events): Improve
+       documentation.
+       * lisp/tab-bar.el (tab-bar-map): Bind `[tab-bar
+       touchscreen-hold]'.
+       * lisp/touch-screen.el (touch-screen-hold, touch-screen-drag):
+       New functions.
+       (touch-screen-handle-timeout): Generate a `touchscreen-hold'
+       event instead.
+       (touch-screen-handle-point-update): Generate a
+       `touchscreen-drag' event upon dragging.
+       (touch-screen-translate-touch): Cancel touch screen timer upon
+       exit.
+       * src/keyboard.c (access_keymap_keyremap): Take unsigned int
+       start and end instead.
+
+       Improve touch-screen support
+       * doc/emacs/emacs.texi (Top):
+       * doc/emacs/input.texi (Other Input Devices): Correctly
+       capitalize subsection name.
+       (Touchscreens): Document additional translation.
+       * doc/lispref/commands.texi (Touchscreen Events): Document that
+       `touchscreen-end' events now have prefix keys.  Also, describe
+       mouse emulation and `touchscreen-scroll' events.
+       * doc/lispref/keymaps.texi (Translation Keymaps): Document
+       `current-key-remap-sequence'.
+       * lisp/touch-screen.el (touch-screen-translate-prompt): New
+       function.
+       (touch-screen-scroll): New command.  Bind to
+       `touchscreen-scroll'.
+       (touch-screen-handle-point-update, touch-screen-handle-point-up)
+       (touch-screen-handle-touch): Refactor to actually translate
+       touch screen event sequences, as opposed to looking up commands
+       and executing them.
+       (touch-screen-translate-touch): New function.  Bind in
+       function-key-map to all touch screen events.
+       (touch-screen-drag-mode-line-1, touch-screen-drag-mode-line)
+       (touch-screen-tap-header-line): Remove special commands for
+       dragging the mode line and clicking on the header line.
+       * lisp/wid-edit.el (widget-button-click): Adjust accordingly.
+       * src/keyboard.c (access_keymap_keyremap): Bind
+       `current-key-remap-sequence' to the key sequence being remapped.
+       (keyremap_step): Give fkey->start and fkey->end to
+       access_keymap_keyremap.
+       (head_table): Add imaginary prefix to touchscreen-end events as
+       well.
+       (syms_of_keyboard): New variable Vcurrent_key_remap_sequence.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-15  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * doc/emacs/android.texi (Android): Add new node to menu.
+       (Android Environment): Add footnote pointing to new node.
+       (Android Software): New node.
+       * doc/emacs/emacs.texi (Top): Add new node to menu.
+       * java/AndroidManifest.xml.in (manifest): Fix location of
+       sharedUserId property.
+       * java/INSTALL: Improve documentation of shared user ID
+       support.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-14  Po Lu  <luangruo@yahoo.com>
+
+       Make --with-shared-user-id work
+       * configure.ac (ANDROID_SHARED_USER_NAME): New variable.
+       Substitute it.
+       * java/AndroidManifest.xml.in: Set `sharedUserLabel' if a shared
+       user ID is enabled.
+       * java/res/values/strings.xml (shared_user_name): New string
+       resource.
+
+       Update Android port
+       * src/android.c (android_blit_copy): Don't check for overflow
+       where not required.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Clean up Android debug code
+       * java/org/gnu/emacs/EmacsInputConnection.java
+       (getSurroundingText): Don't print debug information if DEBUG_IC
+       is off.
+
+       * lisp/calc/calc.el (calc): Fix typo.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-13  Po Lu  <luangruo@yahoo.com>
+
+       Add a Doc View tool bar
+       * etc/NEWS: Announce the new tool bar.
+       * etc/images/last-page.xpm:
+       * etc/images/last-page.pbm: New images.  Mirrored from
+       next-page.xpm.
+       * lisp/doc-view.el (doc-view-menu): Use `doc-view-new-search'
+       instead of an anonymous function.
+       (doc-view-tool-bar-map): New keymap.
+       (doc-view-search): Update the tool bar after initiating a
+       search.
+       (doc-view-new-search): New command.
+       (doc-view-mode): Set the tool bar map appropriately.
+
+       Improve workaround for partial texture updates on Android
+       * java/AndroidManifest.xml.in:
+       * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog): Don't
+       change hardware acceleration state.
+       * java/org/gnu/emacs/EmacsNative.java (notifyPixelsChanged): New
+       function.
+       * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView):
+       New field `bitmapChanged'.
+       (copyToFrontBuffer): Signal that the bitmap has changed.
+       (onDraw): If the bitmap has changed, increment the generation
+       ID.
+       * src/android.c (JNICALL): Implement new function.
+
+2023-07-13  Po Lu  <luangruo@yahoo.com>
+
+       Disable hardware acceleration on Android
+       It serves no purpose and causes tearing.  Uploading the bitmap
+       to the GPU takes about as long as it does to incrementally
+       update the surface in software.
+
+       * java/AndroidManifest.xml.in: Disable hardware acceleration.
+       * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): Make
+       lastClosedMenu static.
+       * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog): Enable
+       hardware acceleration within alert dialogs.
+       * java/org/gnu/emacs/EmacsSurfaceView.java (onDraw): Describe
+       why hardware acceleration is disabled.
+       * java/org/gnu/emacs/EmacsWindow.java (run): Remove redundant
+       call.
+
+2023-07-13  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-12  Po Lu  <luangruo@yahoo.com>
+
+       * src/android.c (android_run_select_thread): Fix typo.
+
+       Update Android port
+       * src/android.c (android_run_select_thread): Correctly return
+       the set of ready read and write descriptors on __ANDROID_API__ <
+       16 systems.
+       (android_select): Clear the set of ready file descriptors if
+       events from the event queue are present despite pselect failing.
+
+       Fix keyboard state translation on Android
+       * src/androidterm.c (android_android_to_emacs_modifiers)
+       (android_emacs_to_android_modifiers): Fix statement precedence
+       bugs.
+
+       * src/doc.c (doc_close): Remove unused macro.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsWindow.java (whatButtonWasIt): Handle
+       back and forward buttons along with styluses.
+       * src/doc.c (close_file_unwind_android_fd): New function.
+       (get_doc_string, Fsnarf_documentation): Don't create a temporary
+       fd if it can be avoided.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-11  Po Lu  <luangruo@yahoo.com>
+
+       Fix doc file generation on Android
+       * .gitignore: Ignore cross/etc/DOC.
+       * configure.ac: Make the directory `cross/etc'.
+       * cross/Makefile.in (CLEAN_SUBDIRS): Clean files inside `etc' as
+       well.
+       * java/Makefile.in (install_temp): Copy cross/etc/DOC to the
+       package if it is available.
+       * src/Makefile.in (SOME_MACHINE_OBJECTS): Add androidselect.c,
+       sfntfont-android.c and sfntfont.c.
+       (libemacs.so): Depend on $(etc)/DOC.
+
+       Update Android port
+       * src/sfnt.c (sfnt_fill_span): Correctly clip span to raster
+       width, ensuring that the last pixel is filled.
+       (main): Adjust test sizes.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsView.java (onGenericMotionEvent): Call
+       onGenericMotionEvent.
+       * java/org/gnu/emacs/EmacsWindow.java (Coordinate): New fields
+       `button' and `id'.
+       (<init>): Add new arguments to the construtor.
+       (whatButtonWasIt): Return 0 if the button state has not changed.
+       (buttonForEvent): New function.
+       (figureChange): Return the Coordinate object associated to
+       EVENT.  Determine whether or not EVENT was accompanied by a
+       change to the button state, and ascertain which button that was.
+       (motionEvent): New function.
+       (onGenericMotionEvent, onTouchEvent): Factor out touch and mouse
+       event delivery to motionEvent.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-10  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsService.java (browseUrl): New argument
+       SEND.  Choose from a list of applications that want to share the
+       URL if true.
+       * lisp/net/browse-url.el (browse-url-android-share): New user
+       option.
+       (browse-url-default-android-browser): Respect said user option.
+       * src/android.c (android_init_emacs_service)
+       (android_browse_url): Expose new option.
+       * src/android.h: Update prototypes.
+       * src/androidselect.c (Fandroid_browse_url): Likewise.
+
+       ; Update from Gnulib
+       * lib/vasnprintf.c (VASNPRINTF):
+       * m4/printf.m4 (gl_SWPRINTF_WORKS):
+       (gl_SWPRINTF_DIRECTIVE_LA):
+       * m4/vasnprintf.m4 (gl_PREREQ_PRINTF_PARSE):
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-09  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsWindow.java (eventModifiers)
+       (motionEventModifiers): New functions.
+       (onKeyDown, onKeyUp, onFocusChanged, onSomeKindOfMotionEvent):
+       Don't record the previous modifier mask; instead, always use the
+       modifier state specified in the event.
+       * src/androidterm.c (handle_one_android_event): Don't dispatch
+       button release events when a popup is active.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsService.java (onStartCommand): Fix
+       typo in notification message.
+       * java/org/gnu/emacs/EmacsWindow.java (onFocusChanged): Reset
+       the recorded modifier state upon a change to the window focus.
+
+       * java/org/gnu/emacs/EmacsService.java (onCreate): Fix typo.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsDrawPoint.java (perform): Don't fill
+       an extra pixel.
+       * java/org/gnu/emacs/EmacsService.java (onCreate): Make sure
+       scaledDensity is always at least 160 dpi.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-08  Po Lu  <luangruo@yahoo.com>
+
+       Fix EmacsDrawLine again
+       * java/org/gnu/emacs/EmacsDrawLine.java (perform): Symmetrically
+       adjust coordinates to cover the last pixel.  Then, fill the left
+       most pixel of the line.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsService.java (DEBUG_IC)
+       (DEBUG_THREADS): Improve commentary.
+       * src/androidterm.c (handle_one_android_event): Signal
+       completion of IME events that have lost their frames.
+       (requestCursorUpdates): Don't set an edit counter as this event
+       won't be passed to the text conversion machinery.
+
+       ; Fix whitespace
+       * src/android.c (android_blit_xor):
+       (android_check_query_urgent):
+       (android_run_in_emacs_thread):
+       (android_update_extracted_text): Fix whitespace.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-07  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsSurfaceView.java (copyToFrontBuffer):
+       Use fallback bit blit function on Android 7.0 as well, as
+       crashes have been observed in drawBitmap.
+
+2023-07-07  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * lisp/tool-bar.el (modifier-bar-modifier-list): New variable.
+       (modifier-bar-button): New function.
+       (tool-bar-event-apply-alt-modifier)
+       (tool-bar-event-apply-super-modifier)
+       (tool-bar-event-apply-hyper-modifier)
+       (tool-bar-event-apply-shift-modifier)
+       (tool-bar-event-apply-control-modifier)
+       (tool-bar-event-apply-meta-modifier): Factor out modifier bar
+       logic to that function, and redisplay the tool bar where
+       necessary.
+       (modifier-bar-available-p): New function.
+       (modifier-bar-mode): Disable tool bar items corresponding to
+       modifier keys that've already been pressed.
+
+       * etc/images/shift.pbm: Regenerate from correct font.
+
+2023-07-07  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-06  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsNative.java (scaledDensity): Announce
+       new argument `scaledDensity'.
+       * java/org/gnu/emacs/EmacsNoninteractive.java (main): Specify
+       new argument.
+       * java/org/gnu/emacs/EmacsService.java (onCreate): Compute an
+       adjusted DPI for the font size based on the ratio between
+       density and scaledDensity.
+       (run): Specify that adjusted density.
+       * src/android.c (setEmacsParams): Set
+       `android_scaled_pixel_density'.
+       * src/android.h: (android_scaled_pixel_density: New variable.
+       * src/androidterm.c (android_term_init): Set `font_resolution'.
+       * src/androidterm.h (struct android_display_info): New field.
+       * src/font.c (font_pixel_size, font_find_for_lface)
+       (font_open_for_lface, Ffont_face_attributes, Fopen_font): Use
+       FRAME_RES instead of FRAME_RES_Y.
+       * src/frame.h (FRAME_RES): New macro.  Use this to convert
+       between font point and pixel sizes as opposed to FRAME_RES_Y.
+       * src/w32font.c (fill_in_logfont):
+       * src/xfaces.c (Fx_family_fonts, set_lface_from_font): Use
+       FRAME_RES instead of FRAME_RES_Y.  (bug#64444)
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-05  Po Lu  <luangruo@yahoo.com>
+
+       Fix bug#64445
+       * doc/emacs/android.texi (Android Environment): Document that
+       Emacs also receives READ_EXTERNAL_STORAGE by default on old
+       versions of Android.
+       * java/AndroidManifest.xml.in: Request READ_EXTERNAL_STORAGE.
+       (bug#64445)
+
+       * doc/emacs/emacs.texi (Emacs and Android): Fix menu.
+
+       ; Update Android port
+       * doc/emacs/android.texi (Android): Fix discrepancies between
+       menu and sectioning.
+
+       Fix crash between Android 4.0 and Android 5.1
+       * java/org/gnu/emacs/EmacsService.java (detectMouse): Don't use
+       function that is not present on Android 4.0.
+
+       Update Android port
+       * doc/lispref/commands.texi (Misc Events): Correctly index
+       `set-text-conversion-style'.
+       * lisp/tool-bar.el (tool-bar-event-apply-alt-modifier)
+       (tool-bar-event-apply-super-modifier)
+       (tool-bar-event-apply-hyper-modifier)
+       (tool-bar-event-apply-shift-modifier)
+       (tool-bar-event-apply-control-modifier)
+       (tool-bar-event-apply-meta-modifier): Pass t when restoring text
+       conversion style.
+       * src/keyboard.c (restore_reading_key_sequence): New function.
+       (read_key_sequence): Set `reading_key_sequence' where necessary.
+       * src/keyboard.h: Declare variable.
+       * src/textconv.c (check_postponed_buffers): New function.
+       (Fset_text_conversion_style): New argument.  If set, and a key
+       sequence is being read, postpone resetting the IME until the key
+       sequence is read.
+       (syms_of_textconv): Clear new variable and add staticpro.
+       * src/textconv.h: Update prototypes.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-04  Po Lu  <luangruo@yahoo.com>
+
+       Implement a tool bar containing modifier keys
+       * doc/emacs/frames.texi (Tool Bars): Describe modifier bars.
+       * doc/lispref/keymaps.texi (Extended Menu Items, Tool Bar):
+       Document changes to tool bar menu item handling and secondary
+       tool bars.
+       * etc/NEWS: Announce changes.
+       * lisp/simple.el (event-apply-modifier): Correctly apply Ctrl
+       and Shift modifiers to lower case ASCII key events that already
+       have other modifiers applied.
+       * lisp/tool-bar.el (tool-bar--cache-key)
+       (tool-bar--secondary-cache-key): New defsubsts.
+       (tool-bar--flush-cache): Flush secondary tool bar cache.
+       (tool-bar-make-keymap): Include secondary tool bar if necessary.
+       (tool-bar-make-keymap-1): New arg MAP.  Generate a keymap for
+       that map if specified, else default to tool-bar-map.
+       (set-text-conversion-style, tool-bar-apply-modifiers)
+       (overriding-text-conversion-style)
+       (tool-bar-event-apply-alt-modifier)
+       (tool-bar-event-apply-super-modifier)
+       (tool-bar-event-apply-hyper-modifier)
+       (tool-bar-event-apply-shift-modifier)
+       (tool-bar-event-apply-control-modifier)
+       (tool-bar-event-apply-meta-modifier, modifier-bar-mode): New
+       functions.
+       * src/dispextern.h (enum tool_bar_item_idx): Add
+       TOOL_BAR_ITEM_WRAP.
+       * src/frame.c (make_frame): Clear new field `tool_bar_wraps_p'.
+       * src/frame.h (struct frame): New field `tool_bar_wraps_p'.
+       * src/keyboard.c (parse_tool_bar_item): Handle QCwrap properties
+       in tool bar menu items.
+       (syms_of_keyboard): New defsym QCwrap.
+       * src/xdisp.c (build_desired_tool_bar_string): Clear
+       f->tool_bar_wraps_p and set it appropriately.  Insert new line
+       characters in the tool bar string upon encountering a wrap
+       character.
+       (display_tool_bar_line): Stop at EOB, not line end.  Reseat on
+       the next line upon encountering EOL characters.
+       (redisplay_tool_bar): Allow rows to be different heights if
+       explicit new lines are present upon the tool bar string.
+
+       * src/sfnt.c (sfnt_decompose_compound_glyph): Pacify warning.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-03  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-02  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-07-01  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-30  Po Lu  <luangruo@yahoo.com>
+
+       * src/android.c (android_query_tree): Correctly return children.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-29  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-28  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-27  Po Lu  <luangruo@yahoo.com>
+
+       * doc/emacs/android.texi (Android Environment): Improve wording.
+
+       * doc/emacs/android.texi (Android Environment): Fix typos.
+
+       Update Android port
+       * src/android.c (android_exception_check)
+       (android_exception_check_1)
+       (android_exception_check_2)
+       (android_exception_check_nonnull)
+       (android_exception_check_nonnull_1): Tell the compiler to expect
+       that the object is non-NULL, or that no exception has been
+       thrown.
+
+       * exec/loader-mips64el.s (rest_of_exec): Fix typo in comment.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-26  Po Lu  <luangruo@yahoo.com>
+
+       * doc/lispref/commands.texi (Touchscreen Events): Fix typo.
+
+2023-06-26  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * lisp/calc/calc.el (calc-mode, calc): Make sure the on-screen
+       keyboard is not hidden when a Calc buffer is created or a Calc
+       Trail window is being created for the first time.
+       * lisp/touch-screen.el (touch-screen-window-selection-changed):
+       Take touch-screen-display-keyboard in to account.
+
+       * src/sfnt.c (sfnt_decompose_compound_glyph)
+       (sfnt_interpret_compound_glyph_1): Reset `defer_offsets' before
+       processing each component.
+       (sfnt_lerp_half): Avoid undefined shift of negative value.
+       (sfnt_compute_tuple_scale): Pacify compiler warning.
+
+2023-06-26  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-25  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-24  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-23  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsDrawRectangle.java (perform):
+       * java/org/gnu/emacs/EmacsSdk7FontDriver.java (Sdk7FontEntity):
+       (hasChar): Clean up dead stores.
+
+       * src/android.c (android_wc_lookup_string): Fix typo.
+
+       Correctly check result of string lookup
+       * src/android.c (android_wc_lookup_string): Check that
+       GetStringChars returns non-NULL, not if it throws an exception.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-22  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-21  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * src/androidfns.c (android_set_tool_bar_position):
+       (frame_geometry):
+       * src/androidterm.c (android_flash):
+       (android_clear_under_internal_border): Synchronize with X.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-20  Po Lu  <luangruo@yahoo.com>
+
+       * src/androidfns.c (android_frame_parm_handlers): Fix typo.
+
+       Synchronize tool bar position code with X
+       * src/androidfns.c (android_set_tool_bar_position): New
+       function.
+       (android_frame_parm_handlers): Add new frame param handler.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-19  Po Lu  <luangruo@yahoo.com>
+
+       * lib-src/Makefile.in (seccomp-filter$(EXEEXT)): Link with Gnulib.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsView.java (EmacsView, dimensionsLock):
+       New field.
+       (<init>): Create new lock object.
+       (handleDirtyBitmap, onLayout, onAttachedToWindow): Make sure
+       measuredWidth and measuredHeight are extracted and set
+       atomically.  Send Expose upon layout changes if the view has
+       grown.
+
+       * exec/Makefile.in (clean): Add `exec1'.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-18  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * src/window.h (GCALIGNED_STRUCT): Improve documentation of
+       `last_mark'.
+       * src/xdisp.c (mark_window_display_accurate_1): Don't set
+       `last_mark' to -1 if the mark is inactive.
+
+       Enable text conversion in conf-modes
+       * lisp/textmodes/conf-mode.el (conf-mode-initialize): Set
+       text-conversion-style.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-17  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Initialize signal mask earlier
+       * java/org/gnu/emacs/EmacsService.java (onCreate, run): Don't
+       initialize signal mask here.
+       * java/org/gnu/emacs/EmacsApplication.java (onCreate): Do it
+       here instead.
+       * src/android.c (JNICALL): Restore previous signal masks.
+
+       * java/README: More documentation.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-16  Po Lu  <luangruo@yahoo.com>
+
+       Fix quitting after changes to signal delivery
+       * src/android.c (android_write_event, JNICALL)
+       (android_run_in_emacs_thread): Don't rely on raise to call
+       deliver_process_signal.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity):
+       * java/org/gnu/emacs/EmacsApplication.java (findDumpFile):
+       * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu)
+       (addSubmenu, display):
+       * java/org/gnu/emacs/EmacsDocumentsProvider.java
+       (getNotificationUri, queryChildDocuments, deleteDocument):
+       * java/org/gnu/emacs/EmacsDrawRectangle.java (perform):
+       * java/org/gnu/emacs/EmacsFillRectangle.java (perform):
+       * java/org/gnu/emacs/EmacsOpenActivity.java (readEmacsClientLog)
+       (checkReadableOrCopy):
+       * java/org/gnu/emacs/EmacsSdk7FontDriver.java
+       (EmacsSdk7FontDriver):
+       * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView):
+       * java/org/gnu/emacs/EmacsView.java (EmacsView):
+       * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, onKeyUp):
+       * java/org/gnu/emacs/EmacsWindowAttachmentManager.java
+       (EmacsWindowAttachmentManager): Remove various unused arguments
+       and variables, dead stores, and make minor cleanups and
+       performance improvements.
+       * src/androidmenu.c (FIND_METHOD_STATIC, android_menu_show):
+       Adjust accordingly.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-15  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsInputConnection.java
+       (EmacsInputConnection, beginBatchEdit, reset, endBatchEdit):
+       Keep track of the number of batch edits and return an
+       appropriate value.
+       (takeSnapshot): Implement function.
+       * java/org/gnu/emacs/EmacsNative.java (takeSnapshot): New
+       function.
+       * java/org/gnu/emacs/EmacsService.java (resetIC): Improve
+       debugging output.
+       * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection):
+       Call `reset' to clear the UI side batch edit count.
+       * src/androidterm.c (struct
+       android_get_surrounding_text_context): New fields
+       `conversion_start' and `conversion_end'.
+       (android_get_surrounding_text): Return the conversion region.
+       (android_get_surrounding_text_internal, NATIVE_NAME): Factor out
+       `getSurroundingText'.
+       (takeSnapshot): New function.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-14  Po Lu  <luangruo@yahoo.com>
+
+       Improve IM synchronization on Android
+       * java/org/gnu/emacs/EmacsInputConnection.java
+       (EmacsInputConnection): Reimplement as an InputConnection, not
+       BaseInputConnection.
+       * src/androidterm.c (performEditorAction): Sync prior to sending
+       keyboard events.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-13  Po Lu  <luangruo@yahoo.com>
+
+       Improve behavior of Gnus on Android
+       * etc/NEWS: Fix typo.
+       * lisp/gnus/gnus-score.el (gnus-read-char): New function.
+       (gnus-summary-increase-score): Use it to display a dialog box on
+       Android, where input methods have trouble with plain old
+       read-char.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-12  Po Lu  <luangruo@yahoo.com>
+
+       Improve appearance of custom dialog buttons on Android
+       * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog): Resolve
+       dialog button style and use it instead.
+
+       Fix deadlocks
+       * java/org/gnu/emacs/EmacsView.java (EmacsView)
+       (showOnScreenKeyboard, hideOnScreenKeyboard): Don't synchronize.
+       * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow)
+       (toggleOnScreenKeyboard): Revert to calling IMM functions from
+       the main thread.
+       * src/android.c (struct android_event_container)
+       (android_pselect_nfds, android_pselect_readfds)
+       (android_pselect_writefds, android_pselect_exceptfds)
+       (android_pselect_timeout): Don't make volatile.
+       (android_wait_event): Run queries if necessary.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-11  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * lisp/net/tramp.el (tramp-encoding-shell):
+       * lisp/obsolete/terminal.el (terminal-emulator):
+       * lisp/term.el (term-exec-1):
+       * lisp/textmodes/artist.el (artist-figlet-get-font-list):
+       * src/android.c (JNICALL): Where /bin/sh was previously used,
+       use /system/bin/sh on Android.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView):
+       Document member variables.
+       (onDraw): Use separate Paint object on the UI thread.
+       * src/textconv.c (really_commit_text, really_set_composing_text)
+       (really_delete_surrounding_text): Run modification hooks when
+       deleting text.
+
+       Avoid extraneous calls to the UI thread
+       * java/org/gnu/emacs/EmacsView.java (EmacsView)
+       (showOnScreenKeyboard, hideOnScreenKeyboard)
+       (onCheckIsTextEditor): Make synchronized.
+       * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow)
+       (toggleOnScreenKeyboard): Don't post to the main thread.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-10  Po Lu  <luangruo@yahoo.com>
+
+       ; Update Android port
+       * src/keyboard.c (handle_input_available_signal): Don't generate
+       instructions not available in arm mode.
+
+       Update Android port
+       * src/android.c (android_select, android_check_query)
+       (android_check_query_urgent, android_answer_query)
+       (android_answer_query_spin, android_begin_query, android_end_query)
+       (android_run_in_emacs_thread): Use finer grained memory
+       synchronization semantics.
+       * src/androidterm.c (android_get_selection): Use the current
+       selection, not its value at the time of the last redisplay.
+       * src/keyboard.c (handle_input_available_signal): Place memory
+       barrier.
+
+       Inherit surrounding text properties when inserting conversion text
+       * src/textconv.c (really_commit_text)
+       (really_set_composing_text): Improve behavior of certain
+       fontification mechanisms by inheriting text properties from
+       surrounding text.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Prevent hangs from IM requests with the main thread busy
+       * src/android.c (android_select): Clear `android_urgent_query'.
+       (android_check_query): Make static.  Clear
+       `android_urgent_query'.
+       (android_check_query_urgent): New function; work like
+       `android_check_query', but only answer urgent queries.
+       (android_answer_query, android_end_query): Clear urgent query
+       flag.
+       (android_run_in_emacs_thread): Initially wait two seconds for
+       the query to run from the keyboard loop; upon a timeout, set
+       `android_urgent_query' to true and wait for it to run while
+       reading async input.
+       * src/android.h: Update prototypes.
+       * src/keyboard.c (handle_async_input): Call
+       `android_check_query_urgent'.
+
+2023-06-09  Po Lu  <luangruo@yahoo.com>
+
+       ; Fix typos
+       * src/textconv.c (really_commit_text):
+       (handle_pending_conversion_events): Fix minor typos.
+
+       Avoid responding to input method queries asynchronously
+       * src/androidterm.c (handle_one_android_event): Don't answer
+       queries here; just rely on the event interrupting
+       android_select.  This avoids exposing buffer contents to input
+       methods while a command is being executed.
+       * src/textconv.c (TEXTCONV_DEBUG, really_commit_text)
+       (really_finish_composing_text, really_set_composing_text)
+       (really_set_composing_region, really_delete_surrounding_text)
+       (really_set_point_and_mark, get_extracted_text): Add debugging
+       printouts.
+
+       Initialize text conversion hooks for each C Mode buffer
+       * lisp/progmodes/cc-mode.el (c-initialize-cc-mode): Always add
+       text conversion hooks.
+
+       * src/android.c (android_get_gc_values): Remove redundancy.
+
+       Block profiling signals in the Android UI thread
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New
+       function `setupSystemThread'.
+       * java/org/gnu/emacs/EmacsService.java (onCreate): Block all
+       signals except for SIGBUS and SIGSEGV in the UI thread.
+       * src/android.c (setupSystemThread): New function.
+
+       Fix crash starting Emacs to open file
+       * java/org/gnu/emacs/EmacsThread.java (run): Correct check
+       against extraStartupArgument when an initial file is specified.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-08  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Correctly display popup dialogs from Emacsclient
+       * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu):
+       Make subclasses final.
+       * java/org/gnu/emacs/EmacsDialog.java (display1): Check if an
+       instance of EmacsOpenActivity is open; if it is, try using it to
+       display the pop up dialog.
+       * java/org/gnu/emacs/EmacsDialogButtonLayout.java
+       (EmacsDialogButtonLayout): Make final.
+       * java/org/gnu/emacs/EmacsHolder.java (EmacsHolder<T>):
+       Likewise.
+       * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity):
+       New field `currentActivity'.
+       (onCreate, onDestroy, onWindowFocusChanged, onPause): Set that
+       field as appropriate.
+
+       Update Android port
+       * src/android.c (android_is_special_directory): New function.
+       (android_get_asset_name, android_content_name_p)
+       (android_get_content_name):
+       * src/android.h (android_is_special_directory)
+       (JNI_STACK_ALIGNMENT_PROLOGUE):
+       * src/fileio.c (check_mutable_filename):
+       * src/filelock.c (WTMP_FILE, make_lock_file_name):
+       * src/inotify.c (IN_ONLYDIR, Finotify_add_watch): Factor out
+       checks against asset and content directories to that function.
+
+       ; Update from Gnulib
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-07  Po Lu  <luangruo@yahoo.com>
+
+       ; Update Android port
+       * doc/emacs/android.texi (Android Startup): Fix reference to
+       non existent node.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsInputConnection.java (beginBatchEdit)
+       (endBatchEdit, commitCompletion, commitText, deleteSurroundingText)
+       (finishComposingText, getSelectedText, getTextAfterCursor)
+       (getTextBeforeCursor, setComposingText, setComposingRegion)
+       (performEditorAction, performContextMenuAction, getExtractedText)
+       (setSelection, sendKeyEvent, deleteSurroundingTextInCodePoints)
+       (requestCursorUpdates): Ensure that the input connection is up
+       to date.
+       (getSurroundingText): New function.
+       * java/org/gnu/emacs/EmacsNative.java (getSurroundingText):
+       Export new C function.
+       * java/org/gnu/emacs/EmacsService.java (resetIC): Invalidate
+       previously created input connections.
+       * java/org/gnu/emacs/EmacsView.java (EmacsView)
+       (onCreateInputConnection): Signify that input connections are
+       now up to date.
+       * src/androidterm.c (struct
+       android_get_surrounding_text_context): New structure.
+       (android_get_surrounding_text, NATIVE_NAME):
+       * src/textconv.c (get_surrounding_text):
+       * src/textconv.h: New functions.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-06  Po Lu  <luangruo@yahoo.com>
+
+       * lisp/simple.el (analyze-text-conversion): Remove old workaround.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-06  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsContextMenu.java (display): Use
+       `EmacsHolder' instead of `Holder'.
+       * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog): Use
+       `EmacsDialogButtonLayout' to ensure that buttons are wrapped
+       properly.
+       (display): Adjust for new holder class.
+       * java/org/gnu/emacs/EmacsDialogButtonLayout.java
+       (EmacsDialogButtonLayout, onMeasure, onLayout): New functions.
+
+       * java/org/gnu/emacs/EmacsDrawLine.java:
+       * java/org/gnu/emacs/EmacsFillPolygon.java: Remove redundant
+       imports.
+       * java/org/gnu/emacs/EmacsHolder.java (EmacsHolder<T>):
+       * java/org/gnu/emacs/EmacsService.java (class Holder<T>)
+       (getEmacsView, EmacsService): Rename `Holder' to `EmacsHolder'
+       and make it public.
+
+2023-06-06  Po Lu  <luangruo@yahoo.com>
+
+       Improve undo behavior on Android
+       * lisp/simple.el (undo-auto-amalgamate): Update doc string to
+       describe new amalgamating commands.
+       (analyze-text-conversion): Make this an amalgamating command by
+       default, unless a new line has been inserted.  Also, shorten the
+       undo boundary timer.
+       * src/textconv.c (really_commit_text)
+       (really_set_composing_text): Correctly report ephemeral
+       deletions.
+       (syms_of_textconv): Fix doc strings.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-05  Po Lu  <luangruo@yahoo.com>
+
+       Clear batch edit state once a new input connection is established
+       * src/androidterm.c (android_handle_ime_event): Clear batch edit
+       state, in case the previous input method forgot to do so.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New
+       function clearInputFlags.
+       * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection):
+       Stop reporting changes after a new input method connection is
+       established.
+       * src/androidterm.c (android_handle_ime_event): Implement that
+       change.
+       (JNICALL): New function.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-04  Po Lu  <luangruo@yahoo.com>
+
+       * src/keyboard.c: Fix build without window system
+
+       * configure.ac: Tune pty detection for Android.
+
+       Fix input method synchronization problems
+       * java/debug.sh (gdbserver_cmd, is_root): Prefer TCP again.
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New
+       function `queryAndSpin'.
+       * java/org/gnu/emacs/EmacsService.java (EmacsService)
+       (icBeginSynchronous, icEndSynchronous, viewGetSelection): New
+       synchronization functions.
+       (resetIC, updateCursorAnchorInfo): Call those instead.
+       * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection):
+       Call viewGetSelection.
+       * src/android.c (JNICALL, android_answer_query_spin): New
+       functions.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-03  Po Lu  <luangruo@yahoo.com>
+
+       Fix typos in Android port
+       * lisp/bindings.el (global-map): Bind cut, copy and paste.
+       * src/androidterm.c (JNICALL): Use key.
+
+       Behave correctly when IMEs commit or compose text with active mark
+       * src/textconv.c (really_commit_text)
+       (really_set_composing_text): Delete text between mark and point
+       if the mark is active.  Don't record changes if the text is
+       empty.
+
+       Update Android port
+       * src/androidterm.c (struct android_get_extracted_text_context):
+       New field `mark_active'.
+       (android_get_extracted_text): Set that field.
+       (struct android_extracted_text_class): New field `flags'.
+       (android_build_extracted_text): New argument `mark_active'.  Set
+       flags appropriately.
+       (NATIVE_NAME, android_update_selection): Likewise.
+       * src/textconv.c (get_extracted_text): New argument
+       `mark_active'.  Set it if the mark is active.
+       * src/textconv.h: Update prototypes.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       * etc/MACHINES: Fix reference to obsolete file.
+
+2023-06-02  Po Lu  <luangruo@yahoo.com>
+
+       Improve Eldoc text conversion support
+       * lisp/emacs-lisp/eldoc.el: ("back-to-indentation"): Register
+       touch screen and text conversion commands.
+
+       Improve CC Mode support for text conversion
+       * lisp/progmodes/cc-cmds.el (c-post-text-conversion): New
+       function.
+       * lisp/progmodes/cc-mode.el (c-initialize-cc-mode): Add it as
+       the `post-texxt-conversion-hook'.
+       * lisp/simple.el (post-text-conversion-hook): New hook.
+       (analyze-text-conversion): Run it until success before trying
+       post insert functions.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsInputConnection.java
+       (EmacsInputConnection): Apply workarounds on Vivo devices as
+       well.
+       * src/android.c (sendKeyPress, sendKeyRelease): Clear counter.
+       * src/androidgui.h (struct android_key_event): New field
+       `counter'.
+       * src/androidterm.c (handle_one_android_event): Generate
+       barriers as appropriate.
+       (JNICALL): Set `counter'.
+       * src/frame.h (enum text_conversion_operation):
+       * src/textconv.c (detect_conversion_events)
+       (really_set_composing_text, handle_pending_conversion_events_1)
+       (handle_pending_conversion_events, textconv_barrier):
+       * src/textconv.h: Implement text conversion barriers and fix
+       various typos.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-06-01  Po Lu  <luangruo@yahoo.com>
+
+       Correctly export file:// URIs on Android
+       * java/org/gnu/emacs/EmacsService.java (browseUrl): If uri's
+       scheme is `file', rewrite it into a content URI.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsInputConnection.java
+       (EmacsInputConnection, performContextMenuAction): New function.
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative)
+       (performContextMenuAction): New function.
+       * src/android.c (android_get_gc_values): Implement more
+       efficiently.
+       * src/androidterm.c (android_handle_ime_event): Pass through
+       `update' argument to `finish_composing_text'.  Fix thinko.
+       * src/textconv.c (really_finish_composing_text)
+       (really_set_composing_text, really_set_composing_region)
+       (handle_pending_conversion_events_1, finish_composing_text): New
+       argument `update'.  Notify IME of conversion region changes if
+       set.
+       * src/textconv.h: Update structs and prototypes.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsInputConnection.java
+       (EmacsInputConnection): Add compatibility adjustments for
+       Samsung devices.
+
+       Correctly report start and end in extracted text
+       * src/androidterm.c (struct android_get_extracted_text_context):
+       New field `start_offset' and `end_offset'.  Delete `offset'.
+       (android_get_extracted_text, android_build_extracted_text):
+       Replace `offset' with new args `start_offset' and `end_offset'.
+       (NATIVE_NAME): Set `start_offset' and `end_offset'.
+       (android_update_selection): Likewise.
+       * src/textconv.c (get_extracted_text): Likewise.
+       * src/textconv.h: Update prototypes.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-31  Po Lu  <luangruo@yahoo.com>
+
+       Fix build with Lisp_Object type checking
+       * configure.ac: Pass through `--enable-check-lisp-object-type'
+       on Android.
+       * src/alloc.c (android_make_lisp_symbol):
+       * src/android.c:
+       * src/androidfns.c (android_set_no_focus_on_map)
+       (android_set_no_accept_focus):
+       * src/androidfont.c (androidfont_match, androidfont_open_font):
+       * src/androidselect.c (Fandroid_get_clipboard)
+       (Fandroid_get_clipboard_targets):
+       * src/keyboard.c (make_lispy_event, syms_of_keyboard):
+       * src/sfntfont.c (sfnt_enum_font_1, sfntfont_list_1):
+       * src/textconv.c (really_set_point_and_mark): Fix Lisp_Object
+       and integer screw-ups.
+
+       Update Android port
+       * doc/emacs/input.texi (Other Input Devices, Touchscreens)
+       (On-Screen Keyboards):
+       * doc/lispref/commands.texi (Misc Events):
+       * src/android.c (android_faccessat): Improve word choices and
+       commentary.
+       * lisp/touch-screen.el (touch-screen-handle-scroll): Make
+       precision scrolling work better with horizontal movement.
+
+       * src/android.c (android_copy_area): Pacify compiler warning.
+
+       Update Android port
+       * exec/exec.c (insert_args): New argument `arg3'.  Replace
+       argv[1] with that argument.
+       (exec_0): Pass file name of script to `insert_args'.
+
+       Update android.texi
+       * doc/emacs/android.texi (What is Android?):
+       (Android Startup):
+       (Android File System):
+       (Android Environment):
+       (Android Windowing):
+       (Android Troubleshooting): Improve wording and various other
+       issues.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * java/debug.sh (is_root): Go back to using unix sockets; allow
+       adb to forward them correctly.
+       * java/org/gnu/emacs/EmacsInputConnection.java
+       (getExtractedText): Don't print text if NULL.
+       * java/org/gnu/emacs/EmacsService.java (EmacsService): New field
+       `imSyncInProgress'.
+       (updateIC): If an IM sync might be in progress, avoid deadlocks.
+       * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection):
+       Set `imSyncInProgress' across synchronization point.
+       * src/android.c (android_check_query): Use __atomic_store_n.
+       (android_answer_query): New function.
+       (android_begin_query): Set `android_servicing_query' to 2.
+       Check once, and don't spin waiting for query to complete.
+       (android_end_query): Use __atomic_store_n.
+       (android_run_in_emacs_thread): Compare-and-exchange flag.  If
+       originally 1, fail.
+       * src/textconv.c (really_set_composing_text): Clear conversion
+       region if text is empty.
+
+2023-05-30  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-29  Po Lu  <luangruo@yahoo.com>
+
+       * src/android.c: Fix typos.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * src/android.c (android_blit_copy):
+       (android_blit_xor): Fix typos.
+
+       * src/android.c (android_blit_copy): Fix typos.
+
+       Work around more problems with Bitmaps
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New
+       function `blitRect'.
+       * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView):
+       Use it on Android 8.x.
+       * src/android.c (blitRect): Implement new function.
+
+       Update Android port
+       * src/android.c (android_neon_mask_line): Fix iteration
+       over remainder.
+       (android_blit_copy): Be more paranoid.
+
+       Implement android_copy_area in C
+       * java/org/gnu/emacs/EmacsCopyArea.java: Remove file.
+       * java/org/gnu/emacs/EmacsService.java (EmacsService, copyArea):
+       Delete function.
+       * src/android.c (struct android_emacs_service)
+       (android_init_emacs_service): Remove `copy_area'.
+       (android_create_gc, android_change_gc, android_get_gc_values):
+       Record new GC values.
+       (android_neon_mask_line): New function.
+       (android_blit_copy, android_blit_xor): New functions.
+       (android_copy_area): Implement in C.
+       (android_lock_bitmap): Accept drawables instead of windows.
+       * src/android.h: Adjust prototype for `android_lock_bitmap'.
+       * src/androidgui.h (struct android_gc): Record last known GC
+       values.
+
+2023-05-27  Po Lu  <luangruo@yahoo.com>
+
+       Add extra thread-related checking
+       * java/org/gnu/emacs/EmacsService.java (EmacsService)
+       (checkEmacsThread): New function.
+       (fillPolygon, drawRectangle, drawLine, drawPoint, copyArea)
+       (clearArea):
+       * java/org/gnu/emacs/EmacsThread.java (EmacsThread):
+       * java/org/gnu/emacs/EmacsView.java (EmacsView, swapBuffers):
+       Call where appropriate.
+
+       Remove synchronization around `damageRegion'
+       * java/org/gnu/emacs/EmacsView.java (EmacsView, swapBuffers):
+       Remove unnecessary documentation.  `damageRegion' is only
+       changed from the Emacs thread.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-26  Po Lu  <luangruo@yahoo.com>
+
+       Allow starting Emacs --debug-init on Android
+       * doc/emacs/android.texi (Android Troubleshooting): Document
+       `debug-init' option.
+       * java/AndroidManifest.xml.in
+       (EmacsLauncherPreferencesActivity): New activity.  Export on
+       systems older than Android 7.0.
+       * java/org/gnu/emacs/EmacsActivity.java (onCreate): Adjust for
+       string startup argument.
+       * java/org/gnu/emacs/EmacsLauncherPreferencesActivity.java: New
+       file.
+       * java/org/gnu/emacs/EmacsPreferencesActivity.java
+       (EmacsPreferencesActivity): Don't make final.
+       (startEmacsQ): Give start-up argument as an argument, not as a
+       boolean.
+       (startEmacsDebugInit): New function.
+       (onCreate): Register new listener; make final.
+       * java/org/gnu/emacs/EmacsService.java (onCreate): Pass
+       extraStartupArgument.
+       * java/org/gnu/emacs/EmacsThread.java (EmacsThread): Rename
+       startDashQ to extraStartupArgument.
+       (run): Adjust accordingly.
+       * java/res/values-v24/bool.xml:
+       * java/res/values/bool.xml:
+       * java/res/values/strings.xml: New files.
+       * java/res/xml/preferences.xml: Add new option.  Move string
+       resources around.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-25  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-24  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * src/sfnt.c (sfnt_decompose_compound_glyph): Allow decomposing
+       up to 16 nested components.
+       (CALL, LOOPCALL): Correctly error if no fdef storage exists.
+       (sfnt_interpret_run): New label `next_instruction', for CALL.
+       (sfnt_interpret_compound_glyph_1): Allow decomposing up to 16
+       nested components.  Prevent crash if there are no end points or
+       points.
+       (sfnt_read_cvar_table): Prevent assigning uninitialized values.
+       (sfnt_vary_simple_glyph): Update commentary.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-23  Po Lu  <luangruo@yahoo.com>
+
+       * exec/exec.c (exec_0): Use strcpy.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-22  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-21  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-20  Po Lu  <luangruo@yahoo.com>
+
+       Remove arbitrary process count limit
+       * exec/trace.c (handle_clone_prepare):
+       (handle_clone): When !REENTRANT, use malloc to allocate
+       tracees after running out of static ones.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsView.java (swapBuffers): Restore
+       missing damage rect code.
+       (onDetachedFromWindow): Remove redundant synchronization.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-19  Po Lu  <luangruo@yahoo.com>
+
+       Make tapping on header lines behave reasonably
+       * lisp/touch-screen.el (touch-screen-tap-header-line): New
+       function.
+       ([header-line touchscreen-begin]): Define to
+       `touch-screen-tap-header-line'.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-18  Po Lu  <luangruo@yahoo.com>
+
+       * make-dist (possibly_non_vc_files): Add Android-specific files.
+
+       Allow interacting with the tab line from a touch screen
+       * doc/emacs/frames.texi (Tab Bars): Explain how to interact with
+       the tab bar from a touch screen.
+       * doc/emacs/input.texi (Touchscreens): Document exactly what a
+       ``long press'' is.
+       * doc/emacs/windows.texi (Tab Line): Likewise.
+       * lisp/tab-line.el (tab-line-tab-map, tab-line-add-map)
+       (tab-line-tab-close-map, tab-line-left-map, tab-line-right-map):
+       Bind `touchscreen-begin' to each command.
+       (tab-line-track-tap, tab-line-event-start): New functions.
+       (tab-line-hscroll-right, tab-line-hscroll-left, tab-line-new-tab)
+       (tab-line-select-tab, tab-line-close-tab): Use them.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-17  Po Lu  <luangruo@yahoo.com>
+
+       ; Update from Gnulib
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-16  Po Lu  <luangruo@yahoo.com>
+
+       Add touchscreen support to the tab bar
+       * lisp/menu-bar.el (popup-menu-normalize-position): Normalize
+       `touchscreen-begin' events correctly.
+       * lisp/tab-bar.el (tab-bar-mouse-context-menu): New argument
+       POSN.  Use it if specified.
+       (touch-screen-track-tap, tab-bar-handle-timeout)
+       (tab-bar-touchscreen-begin): New functions.
+       (tab-bar-map): Bind [tab-bar touchscreen-begin].
+       * lisp/touch-screen.el (touch-screen-track-drag): Fix doc
+       string.
+       * src/dispextern.h: Export `get_tab_bar_item_kbd'.
+       * src/keyboard.c (coords_in_tab_bar_window): New function.
+       (make_lispy_event): Adjust touchscreen begin event mouse
+       position list for tab bar.
+       * src/xdisp.c (tab_bar_item_info): Allow CLOSE_P to be NULL.
+       (get_tab_bar_item): Adjust doc string.
+       (get_tab_bar_item_kbd): New function.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-15  Po Lu  <luangruo@yahoo.com>
+
+       Fix year 2038 code for Android 4.4 and earlier
+       * configure.ac: Also disable enable_year2038.
+
+       Fix the MS-DOS build
+       * msdos/sed1v2.inp: Fix removal of ANDROID_BUILD_CFLAGS.
+       * msdos/sedlibmk.inp: Clear DIR_HAS_FD_MEMBER and
+       LOCALE_FR_UTF8.
+
+       ; Update from Gnulib
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-14  Po Lu  <luangruo@yahoo.com>
+
+       Implement document moving on Android
+       * java/org/gnu/emacs/EmacsDocumentsProvider.java
+       (notifyChangeByName): New function.
+       (queryDocument1): Set FLAG_SUPPORTS_MOVE where necessary.
+       (moveDocument): Implement new function.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-13  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-12  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-11  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-10  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-09  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-08  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/Makefile.in (install_temp/assets/version): Fix generation
+       in out of tree builds.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-07  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsInputConnection.java
+       (requestCursorUpdates):
+       * java/org/gnu/emacs/EmacsNative.java (requestCursorUpdates):
+       * java/org/gnu/emacs/EmacsService.java (updateCursorAnchorInfo):
+       New functions.
+       * src/android.c (struct android_emacs_service)
+       (android_init_emacs_service): Add new method.
+       (android_update_cursor_anchor_info): New function.
+       * src/androidfns.c (android_set_preeditarea): New function.
+       * src/androidgui.h (enum android_ime_operation): New operation
+       `REQUEST_CURSOR_UPDATES'.
+       (struct android_ime_event): Document new meaning of `length'.
+       * src/androidterm.c (android_request_cursor_updates): New
+       function.
+       (android_handle_ime_event): Handle new operations.
+       (handle_one_android_event, android_draw_window_cursor): Update
+       the preedit area if needed, like on X.
+       (requestCursorUpdates): New function.
+       * src/androidterm.h (struct android_output): New field
+       `need_cursor_updates'.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-06  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * configure.ac (LIBGMP_CFLAGS): Avoid non portable test
+       expression.
+
+       Update Android port
+       * cross/verbose.mk.android: Get rid of badly aligned ANDROID_CC
+       messages.
+       * java/org/gnu/emacs/EmacsInputConnection.java (syncAfterCommit)
+       (extractAbsoluteOffsets): Add workarounds for several kinds of
+       machines.
+       (commitText, getExtractedText): Likewise.
+       * src/textconv.c (really_commit_text): Improve definition of
+       POSITION.
+       (get_extracted_text): Default to providing at least 4
+       characters.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-05  Po Lu  <luangruo@yahoo.com>
+
+       Fix execution of /proc/self/exe within child processes
+       * exec/exec.h (struct exec_tracee): New field `new_child'.
+       Also, make `waiting_for_syscall' a bitfield.
+       * exec/trace.c (PTRACE_GETEVENTMSG): New declaration.
+       (MAX_TRACEES): Bump to 4096.
+       (handle_clone_prepare): New function.
+       (handle_clone): If required, set `new_child' and wait for a
+       ptrace event describing the parent to arrive.
+       (after_fork): Clear new field.
+       (exec_waitpid): Upon a ptrace event describing a clone, create
+       the child's tracee if it doesn't already exist.  Otherwise, copy
+       over the parent's cmdline and start running it.
+
+       Update Android port
+       * doc/emacs/android.texi (Android Environment): Document lossage
+       with SIGSTOP.
+       * exec/exec.c (exec_0): Check X_OK on file being opened.  Also
+       handle /proc/self/exe.
+
+       Update Android port
+       * exec/trace.c (SYS_SECCOMP): Define when not present.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-04  Po Lu  <luangruo@yahoo.com>
+
+       Document another misfeature of Android
+       * doc/emacs/android.texi (Android Environment): Describe how to
+       turn off process killing.
+
+       Update Android port
+       * exec/trace.c (check_signal): New function.
+       (handle_exec, process_system_call): Handle signal-delivery-stop
+       while waiting synchronously for syscall completion.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-03  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * exec/config.h.in: Autoheader.
+       * exec/configure.ac: Check for siginfo_t.si_syscall.
+       * exec/trace.c (exec_waitpid): If SIGSYS is received, and caused by
+       seccomp, drop it should the call number be the invalid system call
+       used by Emacs.
+
+       Update Android port
+       * exec/config.h.in: Autoheader.
+       * exec/configure.ac: Use system extensions.
+       (HAVE_PROCESS_VM): Define if process_vm_readv and
+       process_vm_writev are available.
+       * exec/trace.c (read_memory, user_copy): Implement in terms of
+       process_vm if possible.
+
+       Remove extra debugging code
+       * exec/loader-mipsel.s (__start): Remove extraneous debugging
+       code.
+
+       Update Android port
+       * exec/Makefile.in: (.PHONY): Add `bootstrap-clean' and
+       `extraclean'.
+       (bootstrap-clean): New rule.
+
+       Update Android port
+       * java/Makefile.in (FIND_DELETE): New substitution.
+       (clean): Use FIND_DELETE.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-02  Po Lu  <luangruo@yahoo.com>
+
+       * doc/emacs/android.texi (Android Environment): Improve doc.
+
+       Update Android port
+       * exec/config.h.in (__bool_true_false_are_defined):
+       * exec/configure.ac (REENTRANT): New definition.
+       (READLINKAT_SYSCALL, READLINK_SYSCALL): New defines.  Set on all
+       hosts.
+       * exec/exec.c (MIN, MAX): Remove redundant declarations.  Move
+       to config.h.
+       (exec_0): Copy name of executable into NAME when !REENTRANT.
+       * exec/exec.h (struct exec_tracee): New struct `exec_file'.
+       * exec/trace.c (remove_tracee, handle_exec, handle_readlinkat)
+       (process_system_call, after_fork): Handle readlinkat system
+       calls.
+
+       Fix ps name in Android subprocesses
+       * exec/Makefile.in (.SUFFIXES): Include ., then `srcdir'.
+       * exec/loader-aarch64.s (_start):
+       * exec/loader-armeabi.s (_start):
+       * exec/loader-mips64el.s (__start):
+       * exec/loader-mipsel.s (__start):
+       * exec/loader-x86.s (_start):
+       * exec/loader-x86_64.s (_start): Get basename of opened exec
+       file and make it the command name.  Fix envp skipping on x86
+       and various leaks.
+
+       Port Android port to older Android systems
+       * exec/config.h.in: Autoheader.
+       * exec/configure.ac: Check for declarations of stpcpy and
+       stpncpy.
+       * exec/exec.c (stpcpy, stpncpy): Use replacements if
+       declarations are not present; this happens when a new Android
+       NDK is building for an old version of Android.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-05-01  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * exec/config.h.in: Update config.h.in.
+       * exec/configure.ac: Check for stpcpy and stpncpy.
+       * exec/exec.c (rpl_stpcpy, rpl_stpncpy): Define replacements
+       when they are not present on the system.
+       (process_program_header): Fill comment.
+
+       * src/term.c (syms_of_term): Pretend Android uses TERMINFO.
+
+       Fix cwd relative process execution on Android
+       * exec/exec.c (format_pid): New function.
+       (exec_0): Make cwd relative file names relative to
+       /proc/pid/cwd.
+       * exec/trace.c (handle_exec): Handle EINTR.
+       (process_system_call): Report failure without clobbering x0.
+
+       * README: Describe `exec' directory.
+
+       Fix use dialog box regression on Android
+       * lisp/subr.el (use-dialog-box-p): Always prefer dialog boxes.
+
+       Make it easier to quit on Android
+       * src/android.c (android_write_event):
+       (JNICALL): Raise SIGIO on key press and window action events.
+
+       Fix syscall error reporting on aarch64
+       * exec/trace.c (process_system_call): Save and restore x0, x1
+       and x2 regs after replacing them with an invalid file
+       descriptor.
+
+       Update Android port
+       * Makefile.in (extraclean): Clean in exec as well.
+       * configure.ac: Fix detection of absolute srcdir.  Also, pass
+       CFLAGS.
+       * exec/Makefile.in: (.c.o): Add -I. so config.h can be
+       found.:(.s.o): Don't create m4 temporary in srcdir.
+       * exec/config-mips.m4.in (DADDI2, DADDI3): New macros.  Define
+       to substitute if as cannot assemble daddi.
+       * exec/configure.ac (user_h): Look for user.h in asm/ as well.
+       Use new user.h.  Also look in ptrace.h on arm systems.  Check if
+       as supports daddi on mips64.
+       * exec/exec.c (check_interpreter): Fix char signedness bug.
+       * exec/loader-mips64el.s (__start): Use DADDI2 and DADDI3 for
+       two- and 3-operand daddi.
+       * exec/mipsel-user.h: Don't include sgidefs.h.
+       * java/INSTALL: Document that m4 is now required.
+       * src/android.c (android_rewrite_spawn_argv): Add missing NULL.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Work around system restrictions regarding exec
+       * doc/emacs/android.texi (Android Environment): Document
+       `android-use-exec-loader'.
+       * exec/exec1.c (main): Set program group of child process.
+       * src/android.c (android_rewrite_spawn_argv): New function.
+       * src/android.h: Update prototypes.
+       * src/androidfns.c (syms_of_androidfns): New variable
+       `android_use_exec_loader'.
+       * src/callproc.c (emacs_spawn): Rewrite the argument vector to
+       use exec1 if necessary.
+
+       Remove exec/configure
+       * exec/configure: Remove file.
+       * .gitignore: Add exec/configure.
+
+2023-04-30  Po Lu  <luangruo@yahoo.com>
+
+       Add helper binary `exec1'
+       * .gitignore: New files.
+       * Makefile.in (mostlyclean_dirs): Add libexec, if its Makefile
+       exists.
+       * autogen.sh (do_git): Autoreconf in exec as well.
+       * configure.ac: Configure libexec on Android.
+       * exec/Makefile.in:
+       * exec/README:
+       * exec/config-mips.m4.in:
+       * exec/config.guess:
+       * exec/config.h.in:
+       * exec/config.sub:
+       * exec/configure:
+       * exec/configure.ac:
+       * exec/deps.mk:
+       * exec/exec.c (MIN, struct exec_open_command)
+       (struct exec_map_command, struct exec_jump_command)
+       (write_open_command, write_load_command, process_interpreter_1)
+       (process_interpreter, process_program_header, insert_args)
+       (exec_0):
+       * exec/exec.h (_EXEC_H_, struct elf_header_32)
+       (struct program_header_32, struct dt_entry_32)
+       (struct elf_header_64, struct program_header_64)
+       (struct dt_entry_64, struct exec_tracee):
+       * exec/exec1.c (main):
+       * exec/install-sh (scriptversion):
+       * exec/loader-aarch64.s (_start):
+       * exec/loader-armeabi.s (_start):
+       * exec/loader-mips64el.s (__start):
+       * exec/loader-mipsel.s (__start):
+       * exec/loader-x86.s (_start):
+       * exec/loader-x86_64.s (_start):
+       * exec/mipsel-user.h (_MIPSEL_USER_H_):
+       * exec/mipsfpu.c (MIPS_ABI_FP_ANY, fpu_reqs, valid_abi_p)
+       (fp_mode_for_abi, cpu_supports_fr0_p, determine_fpu_mode):
+       * exec/mipsfpu.h (_MIPSFPU_H_, FP_FR0):
+       * exec/test.c (print_usage, main):
+       * exec/trace.c (MAX_TRACEES, aarch64_set_regs, read_memory)
+       (user_alloca, user_copy, remove_tracee, handle_clone)
+       (syscall_trap_p, handle_exec, process_system_call, tracing_execve)
+       (after_fork, find_tracee, exec_waitpid, exec_init): New files.
+       * java/Makefile.in (CROSS_EXEC_BINS): Add exec1 and
+       loader.
+       ($(CROSS_EXEC_BINS) &): New target.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-29  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * build-aux/ndk-build-helper.mk (TARGET_ARCH): Define variable.
+       * configure.ac (ENABLE_CHECKING, CHECK_STRUCTS)
+       (GC_CHECK_STRING_OVERRUN, GC_CHECK_STRING_FREE_LIST, GLYPH_DEBUG)
+       (GC_CHECK_STRING_BYTES): Enable checking correctly on Android.
+       * java/README: Fix typos.
+       * m4/ndk-build.m4 (ndk_run_test): Pass target arch.
+       * src/android.c (android_get_content_name, android_close)
+       (android_fclose, android_check_string): Fix various typos caught
+       by checking.
+       * src/charset.c (load_charset_map_from_file): Call emacs_fclose,
+       not fclose.
+       * src/image.c (image_set_transform): Fix thinko.
+       (png_load_body, jpeg_load_body, gif_load): Call emacs_fclose,
+       not fclose.  Use open instead of fdopen.
+       * src/xfaces.c (Fx_load_color_file): Likewise.
+
+2023-04-28  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-27  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * src/image.c (image_create_bitmap_from_data): Fix typo in
+       preprocessor conditionals.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * doc/emacs/android.texi (Android File System):
+       (Android Windowing): Make Emacs manual more portable.
+
+       Update Android port
+       * doc/lispref/commands.texi (Misc Events):
+       * doc/lispref/frames.texi (Accessing Selections):
+       (X Selections): Fix pieces of the Info manual.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-26  Po Lu  <luangruo@yahoo.com>
+
+       Make two well known amusements work on Android
+       * lisp/play/doctor.el (text-conversion-style):
+       (doctor-mode):
+       * lisp/play/dunnet.el (text-conversion-style):
+       (dun-mode): Set `text-conversion-style' to `action'.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-25  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-24  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-23  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-22  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-21  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-20  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-19  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-18  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-17  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-16  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-15  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-14  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-13  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * doc/emacs/android.texi (Android Fonts): Update documentation.
+       * doc/lispref/frames.texi (Accessing Selections, X Selections):
+       Fix typos.
+       * src/sfntfont-android.c (system_font_directories)
+       (init_sfntfont_android): Add `/product/fonts'.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-12  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-11  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-10  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-09  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-08  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Document selections on Android more thoroughly
+       * doc/lispref/elisp.texi (Top):
+       * doc/lispref/frames.texi (Frames): Add ``Accessing Selections''
+       to menu.
+       (Accessing Selections, X Selections, Other Selections): New
+       nodes.
+
+2023-04-07  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-06  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-06  Po Lu  <luangruo@yahoo.com>
+
+       Implement `yank-media' on Android
+       * doc/emacs/android.texi (Android Windowing): Update selection
+       restrictions.
+       * java/org/gnu/emacs/EmacsClipboard.java (EmacsClipboard): New
+       functions `getClipboardTargets' and `getClipboardData'.
+       * java/org/gnu/emacs/EmacsSdk11Clipboard.java
+       (EmacsSdk11Clipboard, getClipboardTargets, getClipboardData):
+       Implement.
+       * java/org/gnu/emacs/EmacsSdk8Clipboard.java: Stub out new
+       functions.
+
+       * lisp/term/android-win.el (android-get-clipboard-1): Implement
+       MIME type targets.
+       * src/android.c (android_exception_check)
+       (android_exception_check_1, android_exception_check_2): Fix
+       punctuation in warning message.
+       (android_exception_check_nonnull_1): New function.
+       * src/android.h: Update prototypes.
+       * src/androidselect.c (struct android_emacs_clipboard): New
+       methods.
+       (android_init_emacs_clipboard): Initialize new methods.
+       (Fandroid_get_clipboard_targets, android_xfree_inside)
+       (Fandroid_get_clipboard_data): New functions.
+       (syms_of_androidselect): Define new subrs.
+
+2023-04-05  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-04  Po Lu  <luangruo@yahoo.com>
+
+       * lisp/subr.el (read-char-from-minibuffer): Fix typo.
+
+       Update Android port
+       * lisp/subr.el (read-char-from-minibuffer): Don't use undefined
+       variable.  Reported by Robert Pluim.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-03  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       ; * src/androidselect.c (Fandroid_clipboard_exists_p): Add check.
+
+       Update Android port
+       * src/sfnt.c (sfnt_normalize_vector): Don't rely on undefined
+       sign extension semantics.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-02  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-04-01  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-31  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-30  Po Lu  <luangruo@yahoo.com>
+
+       Tweak outline cache stuff
+       * src/sfntfont.c: Adjust font cache size constants.
+
+       * src/sfnt.c (GETINFO): Fix typo.
+
+       * src/sfnt.h: Fix typo.
+
+       Update Android port
+       * src/sfnt.c (sfnt_make_interpreter): New argument `fvar'.  Set
+       axis count.
+       (SCANCTRL): Implement selector bit 8.
+       (GXAXIS): New instruction.
+       (SFVTPV): Validate graphics state after changing freedom vector.
+       (sfnt_line_to_vector): Implement `original'.
+       (sfnt_move): Remove redundant division.
+       (sfnt_interpret_run): Implement distortable font related GXAXIS
+       instruction (0x91).
+       (sfnt_vary_interpreter): Set naxis and norm_coords.
+       (sfnt_make_test_interpreter, pushb_test_args, pushw_test_args)
+       (sfnt_name_instruction, main): Adjust accordingly.
+       * src/sfnt.h (struct sfnt_interpreter, PROTOTYPE):
+       * src/sfntfont.c (sfntfont_setup_interpreter, sfntfont_open):
+       Set up distortion information.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-29  Po Lu  <luangruo@yahoo.com>
+
+       Improve rules for enumerating user fonts
+       * doc/emacs/android.texi (Android Fonts): Document distortable
+       font replacement rules.
+       * src/sfntfont.c (sfnt_replace_fonts_p): New function.
+       (sfnt_enum_font_1): Call it.
+
+       Fix optimized move functions
+       * src/sfnt.c (sfnt_move_x):
+       (sfnt_move_y):
+       (sfnt_move): Set N flags and don't forget to
+       set N points too.
+
+       * src/sfnt.c (sfnt_read_avar_table): Fix sequencing problem.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * src/sfntfont.c (sfntfont_setup_interpreter): Don't create
+       interpreter for blatently broken fonts.
+
+       Update Android port
+       * src/sfntfont.c (sfntfont_open): Avoid specifying redundant
+       blends.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * src/sfnt.c (sfnt_validate_gs): Fix validation of projection
+       vector.
+
+2023-03-28  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * src/sfnt.c (sfnt_vary_compound_glyph):
+       * src/sfntfont.c (sfntfont_get_glyph)
+       (sfntfont_get_glyph_outline): Avoid clobbering offset size flag
+       when varying compound glyph.
+
+       Update Android port
+       * src/sfnt.c (sfnt_vary_simple_glyph, sfnt_vary_compound_glyph):
+       Fix application of intermediate tuples.
+       * src/sfntfont.c (sfntfont_open): Set xlfd name after applying
+       distortion.
+
+       Correctly round lbearing values
+       * src/sfnt.h (SFNT_ROUND_FIXED):
+       * src/sfntfont.c (sfntfont_probe_widths):
+       (sfntfont_measure_pcm): Round lbearing properly.
+
+       Update Android port
+       * src/sfntfont.c (sfnt_open_tables): Fix typos in non-HarfBuzz
+       code.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * src/androidterm.c (android_draw_image_glyph_string): Restore
+       potentially clobbered GC clipping.
+       * src/sfnt.c (sfnt_large_integer_add, sfnt_multiply_divide_round)
+       (sfnt_mul_fixed_round): New functions.
+       (sfnt_build_glyph_outline): Take unscaled glyph metrics.
+       (sfnt_prepare_raster, sfnt_vary_simple_glyph)
+       (sfnt_vary_compound_glyph, sfnt_vary_interpreter): Use rounding
+       multiplication to scale deltas.
+       (main): Adjust tests.
+       * src/sfntfont.c (sfntfont_get_glyph_outline)
+       (sfntfont_probe_widths, sfntfont_open, sfntfont_measure_pcm)
+       (sfntfont_draw): More minor fixes to variable fonts.
+
+2023-03-27  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * src/sfnt.c (sfnt_normalize_blend): Don't crash when axis
+       variations are not present.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * configure.ac (HAVE_OTF_GET_VARIATION_GLYPHS): Check for
+       `hb_font_set_var_named_instance'.
+       * src/sfnt.c (main): Update tests.
+       * src/sfntfont-android.c (Fandroid_enumerate_fonts): Blacklist
+       bad font.
+       * src/sfntfont.c (struct sfnt_font_tables, struct sfnt_font_desc)
+       (sfnt_decode_instance_name, sfnt_weight_descriptions)
+       (sfnt_enum_font_1, sfntfont_list_1, sfntfont_desc_to_entity)
+       (sfntfont_list, struct sfntfont_get_glyph_outline_dcontext)
+       (sfntfont_get_glyph, sfntfont_get_glyph_outline)
+       (struct sfnt_font_info, sfnt_close_tables, sfnt_open_tables)
+       (sfntfont_open, sfntfont_measure_pcm, sfntfont_close)
+       (sfntfont_draw, sfntfont_begin_hb_font, syms_of_sfntfont)
+       (mark_sfntfont): Handle variable fonts correctly.
+
+       Refactor sfntfont.c
+       * src/sfnt.c (sfnt_build_glyph_outline): Take scale, not head
+       and pixel size.
+       (sfnt_scale_metrics_to_pixel_size): Delete function.
+       (sfnt_get_scale): New function.
+       (main): Update tests.
+       * src/sfnt.h (PROTOTYPE): Update prototypes.
+       * src/sfntfont.c (struct sfnt_outline_cache)
+       (sfntfont_get_glyph_outline, struct sfnt_font_info)
+       (sfntfont_open): Save scale in font information and use it.
+       (sfntfont_measure_instructed_pcm): Delete function.
+       (sfntfont_measure_pcm): Make this the only ``measure pcm''
+       function.
+       (sfntfont_draw): Rely on sfntfont_get_glyph_outline for the
+       scale.
+
+       Refactor sfntfont.c
+       * src/sfntfont.c (struct sfnt_font_tables): New structure.
+       (struct sfnt_font_desc): New field `tables'.
+       (struct sfnt_font_info): New field `desc'.
+       (sfntfont_setup_interpreter): Drop fd arguments and don't try to
+       load interpreter tables.
+       (sfnt_open_tables, sfnt_close_tables): New functions.
+       (sfnt_reference_font_tables, sfnt_dereference_font_tables): New
+       functions.
+       (sfntfont_open, sfntfont_close): Implement in terms of those
+       functions in order to share tables.
+
+2023-03-26  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-26  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * src/sfnt.c (sfnt_table_names): Add avar.
+       (sfnt_read_glyph): Clear distortion fields.
+       (sfnt_build_glyph_outline): Calculate the outline origin point.
+       (sfnt_prepare_raster): Apply the origin point to the X axis
+       offset.
+       (sfnt_scale_metrics_to_pixel_size): New function.
+       (sfnt_build_instructed_outline): Use instructed origin phantom
+       point to determine the outline origin.
+       (sfnt_compute_phantom_points): Apply origin and advance
+       distortion.
+       (struct sfnt_variation_axis, struct sfnt_instance)
+       (struct sfnt_fvar_table, sfnt_read_fvar_table)
+       (struct sfnt_gvar_table, sfnt_read_gvar_table)
+       (sfnt_read_avar_table, struct sfnt_blend, sfnt_init_blend)
+       (sfnt_free_blend, sfnt_normalize_blend, struct sfnt_tuple_header)
+       (struct sfnt_gvar_glyph_header, sfnt_read_packed_deltas)
+       (sfnt_compute_tuple_scale, sfnt_read_cvar_table)
+       (sfnt_infer_deltas_1, sfnt_vary_simple_glyph, sfnt_infer_deltas)
+       (sfnt_vary_glyph, sfnt_vary_compound_glyph)
+       (sfnt_vary_interpreter): New functions.  Add structs to
+       sfntfont.h.
+       (struct sfnt_test_dcontext, sfnt_test_get_glyph, main): Test
+       distortable font handling.
+
+       * src/sfnt.h (SFNT_ENABLE_HINTING):
+       (enum sfnt_table):
+       (struct sfnt_glyph):
+       (struct sfnt_glyph_outline):
+       (struct sfnt_raster):
+       (struct sfnt_default_uvs_table):
+       (struct sfnt_unicode_value_range):
+       (struct sfnt_nondefault_uvs_table):
+       (struct sfnt_uvs_mapping):
+       (struct sfnt_mapped_variation_selector_record):
+       (struct sfnt_table_offset_rec):
+       (struct sfnt_uvs_context):
+       (struct sfnt_mapped_table):
+       (struct sfnt_variation_axis):
+       (struct sfnt_instance):
+       (struct sfnt_fvar_table):
+       (struct sfnt_short_frac_correspondence):
+       (struct sfnt_short_frac_segment):
+       (struct sfnt_avar_table):
+       (struct sfnt_tuple_variation):
+       (struct sfnt_cvar_table):
+       (struct sfnt_gvar_table):
+       (struct sfnt_blend):
+       (struct sfnt_metrics_distortion):
+       (PROTOTYPE): Update prototypes.
+
+       * src/sfntfont.c (sfntfont_get_glyph_outline):
+       (sfntfont_measure_pcm): Adjust calls.
+
+2023-03-24  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * src/sfnt.c (sfnt_table_names): Add fvar, gvar, cvar.
+       (sfnt_read_maxp_table): Call xmalloc, not malloc.
+       (sfnt_read_simple_glyph): Avoid use-after-free if simple is
+       invalid.
+       (sfnt_fill_span): Fix max coverage.
+       (sfnt_normalize_vector): Fail if magnitude is zero.
+       (sfnt_measure_distance): Fix opcode order.
+       (sfnt_dot_fix_14): Fix implementation.
+       (struct sfnt_variation_axis, struct sfnt_instance)
+       (struct sfnt_fvar_table, struct sfnt_gvar_table)
+       (sfnt_read_fvar_table, sfnt_read_gvar_table, struct sfnt_blend)
+       (sfnt_init_blend, sfnt_free_blend, sfnt_normalize_blend)
+       (struct sfnt_tuple_header, struct sfnt_gvar_glyph_header)
+       (sfnt_read_packed_points, sfnt_read_packed_deltas)
+       (sfnt_compute_tuple_scale, sfnt_infer_deltas_1, sfnt_infer_deltas)
+       (sfnt_vary_glyph): Add WIP variation glyph implementation.
+       * src/sfnt.h (enum sfnt_table, struct sfnt_simple_glyph):
+       Likewise.
+
+2023-03-20  Po Lu  <luangruo@yahoo.com>
+
+       * java/INSTALL: Fix typo.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * configure.ac: Add support for HarfBuzz on Android.
+       * java/INSTALL: Document where to get Emacs with HarfBuzz.
+       * lisp/subr.el (overriding-text-conversion-style, y-or-n-p):
+       Correctly set text conversion style if y-or-n-p is called inside
+       the minibuffer.
+       * src/sfnt.c (sfnt_read_cmap_format_8)
+       (sfnt_read_cmap_format_12): Fix typos.
+       (sfnt_read_24, sfnt_read_cmap_format_14): New function.
+       (sfnt_read_cmap_table_1, sfnt_read_cmap_table): Handle format 14
+       cmap tables.
+       (sfnt_read_default_uvs_table, sfnt_read_nondefault_uvs_table)
+       (sfnt_compare_table_offsets, sfnt_create_uvs_context)
+       (sfnt_free_uvs_context, sfnt_compare_uvs_mapping)
+       (sfnt_variation_glyph_for_char, sfnt_map_table, sfnt_unmap_table)
+       (sfnt_read_table, sfnt_test_uvs): New functions.
+       (main): Add UVS tests.
+       * src/sfnt.h (struct sfnt_cmap_format_14)
+       (struct sfnt_variation_selector_record)
+       (struct sfnt_default_uvs_table, struct sfnt_unicode_value_range)
+       (struct sfnt_nondefault_uvs_table, struct sfnt_uvs_mapping)
+       (struct sfnt_mapped_variation_selector_record)
+       (struct sfnt_table_offset_rec, struct sfnt_uvs_context)
+       (struct sfnt_mapped_table): New structures.  Update prototypes.
+       * src/sfntfont-android.c (android_sfntfont_driver): Register
+       HarfBuzz callbacks where required.
+       * src/sfntfont.c (sfntfont_select_cmap): Look for a format 14
+       table.  Save it in new arg FORMAT14.
+       (sfntfont_read_cmap): Adjust accordingly.
+       (struct sfnt_font_info): New field `uvs'.  New fields `hb_font',
+       `fd' and `directory'.
+       (sfntfont_open): Open uvs context.  Under HarfBuzz, don't close
+       the fd or subtable, but save them in the font info instead.
+       (sfntfont_close): Free UVS context.  Close font fd and table
+       directory and HarfBuzz font.
+       (sfntfont_draw): Handle case where s->padding_p.
+       (sfntfont_get_variation_glyphs): New function.
+       (sfntfont_unmap_blob, sfntfont_get_font_table)
+       (sfntfont_begin_hb_font): New functions.
+       * src/sfntfont.h: Update prototypes.
+       * src/textconv.c (Fset_text_conversion_style): Fix doc string.
+
+2023-03-19  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-18  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsView.java (onAttachedToWindow): Send
+       measured width and height in exposures again.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Remove extraneous debugging code
+       * src/androidterm.c (handle_one_android_event): Don't log expose
+       events.
+
+       Work around pselect lossage on Android
+       * src/android.c (android_run_select_thread): New flag.  Use it
+       rather than the rc of pselect and errno to determine whether or
+       not it has been interrupted.
+       (android_handle_sigusr1): Set said flag.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsView.java (EmacsView)
+       (prepareForLayout): New function.  Call this prior to mapping
+       the view.
+       (onGlobalLayout): New function.  Register as global layout
+       listener.
+       * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow)
+       (notifyContentRectPosition): New function.  Use specified
+       xPosition and yPosition when reporting the offsets of children
+       of the root window.
+       * java/org/gnu/emacs/EmacsWindowAttachmentManager.java
+       (registerWindow): Specify activity launch bounds if necessary.
+       * src/androidterm.c (handle_one_android_event): Send
+       MOVE_FRAME_EVENT where necessary.
+
+2023-03-17  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Include more information in Android bug reports
+       * src/androidfns.c (Fx_server_vendor, Fx_server_version): New
+       functions.
+       (syms_of_androidfns): Define new functions.
+       * src/androidterm.c (android_set_build_fingerprint)
+       (syms_of_androidterm): Set new variable
+       Vandroid_build_manufacturer.
+       * src/xfns.c (Fx_server_vendor, Fx_server_version): Update doc
+       strings.
+
+       Fix WINDOWSNT build of fileio.c and image.c
+       * src/fileio.c (emacs_fd_to_int): Don't define on WINDOWSNT.
+       * src/image.c (image_create_bitmap_from_data): Don't abort if
+       !defined HAVE_ANDROID.
+
+       Update Android port
+       * configure.ac:
+       * m4/ndk-build.m4 (ndk_INIT):
+       (ndk_LATE): Avoid AC_REQUIRE magic.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Improve radio button appearance in Android menus
+       * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu):
+       New field `lastGroupId'.
+       (Item): New field `isRadio'.
+       (addItem): New arg `isRadio'.
+       (inflateMenuItems): Apply an empty radio button group if
+       required.
+       * src/androidmenu.c (android_init_emacs_context_menu): Adjust
+       accordingly.
+       (android_menu_show): Likewise.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsView.java (cancelPopupMenu): Dismiss
+       context menu correctly.
+       (isOpaque): New function.
+       * java/org/gnu/emacs/EmacsWindowAttachmentManager.java: Make
+       consumer list public.
+
+       * configure.ac: Add missing precious variable.
+
+2023-03-16  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * lisp/frame.el (android-detect-mouse):
+       * lisp/term/android-win.el (android-get-connection): Add
+       function declarations.
+
+       * configure.ac: Remove unnecessary escape.
+
+       Make ANDROID_CC and SDK_BUILD_TOOLS precious variables
+       * configure.ac (AUTO_DEPEND, ANDROID_STUBIFY, ANDROID_LDFLAGS):
+       * lib/Makefile.in (ANDROID_CFLAGS, ANDROID_BUILD_CFLAGS)
+       (ALL_CFLAGS):
+       * lib/gnulib.mk.in (AM_DEFAULT_VERBOSITY):
+       * msdos/sed1v2.inp:
+       * msdos/sedlibmk.inp:
+       * src/Makefile.in (ANDROID_OBJ, EMACS_CFLAGS): Make those
+       variables precious.  Rename ANDROID_CFLAGS substitution to
+       ANDROID_BUILD_CFLAGS.
+
+       Update Android port
+       * nt/mingw-cfg.site: Suppress build of gnulib printf.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsDocumentsProvider.java (queryRoots): Add
+       icon to document root.
+
+       Update Android port
+       * lisp/loadup.el (current-load-list): Set to empty load list
+       after startup.
+       * src/lread.c (build_load_history): Revert earlier changes.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-15  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * configure.ac: Improve portability.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-15  Robert Pluim  <rpluim@gmail.com>
+
+       Fix typos in Android port
+       * src/fileio.c (Finsert_file_contents):
+       * src/window.c (replace_buffer_in_windows): Call Fboundp, not
+       boundp.
+
+2023-03-15  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * cross/Makefile.in (lib/gnulib.mk): Edit out
+       build-aux stuff.
+       * m4/ndk-build.m4: Also look for cross ranlib.
+
+       * src/sfntfont.c (sfntfont_close): Fix warning w/o mmap.
+
+       Port to systems without endian.h
+       * lib-src/asset-directory-tool.c (main_2): Port to systems
+       without htole32.
+
+2023-03-15  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * configure.ac (XCONFIGURE): Disable NS.
+       * cross/Makefile.in (lib-src/config.h):
+
+       (lib/libgnu.a):
+       (src/android-emacs): Port sed invocation to Mac OS without GNU
+       sed.
+
+2023-03-15  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * doc/lispref/commands.texi (Misc Events): Document variable
+       `disable-inhibit-text-conversion'.
+       * java/org/gnu/emacs/EmacsDialog.java (display1): Try an
+       activity that is certain to be focused first.
+       * lisp/touch-screen.el (touch-screen-track-tap)
+       (touch-screen-track-drag): Bind
+       `disable-inhibit-text-conversion'.
+       * src/keyboard.c (read_key_sequence): Only disable text
+       conversion if an actual function or numeric key is found in the
+       key sequence.
+       (syms_of_keyboard): New variable
+       `disable-inhibit-text-conversion'.
+       * src/lread.c (read_filtered_event): Check new variable.
+       * src/textconv.c (textconv_query): Remove unused label.
+
+       Omit gnulib modules added by Android port on MinGW
+       * nt/gnulib-cfg.mk: Omit new gnulib modules.
+
+2023-03-14  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * lisp/minibuffer.el (minibuffer-setup-on-screen-keyboard):
+       Handle cases where last-event-frame is a kbd macro.
+       * src/keyboard.c (lispy_function_keys): Remove duplicates.
+
+       Fix the MS-DOS build
+       * msdos/sed1v2.inp:
+       * msdos/sed3v2.inp:
+       * msdos/sedlibcf.inp:
+       * msdos/sedlibmk.inp: Update for Android port and new Gnulib
+       modules.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsWindow.java (figureChange): Detect
+       mice on up events as well.
+       (onSomeKindOfMotionEvent): Work past framework bug.
+       * src/androidterm.c (android_perform_conversion_query):
+       * src/textconv.c (textconv_query):
+       * src/textconv.h (TEXTCONV_SKIP_ACTIVE_REGION): Remove unused
+       code.
+
+       Update Android port
+       * doc/emacs/android.texi (Android Windowing): Document how to
+       display dialogs when Emacs is in the background.
+       * java/org/gnu/emacs/EmacsDialog.java (display1): Use system
+       dialogs if possible.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-13  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * etc/NEWS: Announce new option.
+       * lisp/menu-bar.el (menu-bar-close-window): New option.
+       (kill-this-buffer):
+       (kill-this-buffer-enabled-p): Adjust accordingly.
+       * src/keyboard.c (lispy_function_keys): Add more silly
+       keys.
+
+       Update Android port
+       * src/android.c (android_check_string, android_build_string):
+       Reduce consing when building menu bar strings.
+
+       * etc/MACHINES (Android): Update with more recent information.
+
+       Update Android port
+       * doc/emacs/android.texi (Android Startup): Document changes to
+       emacsclient wrapper.
+       * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity)
+       (startEmacsClient): Open EmacsActivity if the service is not
+       running.
+       * java/org/gnu/emacs/EmacsService.java (onCreate):
+       * java/org/gnu/emacs/EmacsThread.java (EmacsThread, run): Pass
+       any file to open to Emacs.
+       * lisp/term/android-win.el (handle-args-function): Implement.
+
+       Update Android port
+       * src/image.c (image_create_bitmap_from_file, image_find_image_fd)
+       (close_android_fd, slurp_file): Return and use `struct
+       android_fd_or_asset' on Android.
+       (xbm_load, xpm_load, pbm_load, png_load_body, jpeg_load_body)
+       (webp_load, svg_load): Adjust accordingly.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-12  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * src/android.c (android_get_screen_width):
+       (android_get_screen_height):
+       (android_get_mm_width):
+       (android_get_mm_height):
+       (android_detect_mouse): Correctly handle Java exceptions.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * src/android.c (android_check_if_event):
+       * src/androidgui.h: New function.
+       * src/androidterm.c (android_event_is_for_frame): New function.
+       (android_reset_conversion): Free and unqueue all text conversion
+       events for the given frame.
+
+       Update Android port
+       * src/androidterm.c (NATIVE_NAME, JNICALL)
+       (android_build_extracted_text, android_update_selection): Use
+       0-based indices for Android buffer positions.  Also, report
+       surrounding text relative to the region, not to the cursor.
+       * src/textconv.c (textconv_query): Accept new values of
+       position.
+       (really_set_composing_text): Use ephemeral last point.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity)
+       (onCancel): New function.
+       (displayFailureDialog): Handle dialog cancellation.
+       * src/sfntfont.c (sfnt_parse_languages): Look for SLNG tag if
+       DLNG is not present.
+
+       Add Super modifier support to Android port
+       * src/androidgui.h (enum android_modifier_mask): New modifier
+       ANDROID_SUPER_MASK.
+       * src/androidterm.c (android_android_to_emacs_modifiers)
+       (android_emacs_to_android_modifiers): Add new modifier.
+
+       Fix crash during androidterm init
+       * src/androidterm.c (syms_of_androidterm): Initialize
+       Vandroid_build_fingerprint in case GC happens.
+
+       * src/emacs-module.c (module_reset_handlerlist): Fix macro conflict.
+
+       Clean up emacs-module.c
+       * src/emacs-module.c (MODULE_HANDLE_NONLOCAL_EXIT)
+       (module_make_global_ref, module_free_global_ref)
+       (module_make_function, module_get_function_finalizer)
+       (module_set_function_finalizer, module_make_interactive)
+       (module_funcall, module_intern, module_type_of)
+       (module_extract_integer, module_make_integer, module_extract_float)
+       (module_make_float, module_copy_string_contents)
+       (module_make_string, module_make_unibyte_string)
+       (module_make_user_ptr, module_get_user_ptr, module_set_user_ptr)
+       (module_get_user_finalizer, module_set_user_finalizer)
+       (module_vec_set, module_vec_get, module_vec_size)
+       (module_process_input, module_extract_time, module_make_time)
+       (module_extract_big_integer, module_make_big_integer)
+       (module_open_channel, module_reset_handlerlist): Adjust as
+       recommended by Paul Eggert <eggert@cs.ucla.edu>.
+
+       Update Android port
+       * configure.ac: Take option `--with-shared-user-id' and give it
+       to AndroidManifest.xml.in.
+       * java/AndroidManifest.xml.in: Substitute that into the
+       application info.
+       * java/INSTALL (BUILDING WITH A SHARED USER ID): New section.
+
+2023-03-11  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Improve default value of `with_mailutils' on Android
+       * configure.ac: Default to off on Android.
+
+       * configure.ac: Fix typo.
+
+       Update Android port
+       * configure.ac (HAVE_MAILUTILS, with_mailutils)
+       (ANDROID_SDK_8_OR_EARLIER, XCONFIGURE): Fix POP and mailutils
+       configuration on Android.
+       * java/Makefile.in:
+       * src/callproc.c (syms_of_callproc): Avoid using built-in
+       movemail when --with-mailutils.
+
+       Update Android port
+       * src/android.c (android_resolve_handle)
+       (android_resolve_handle2): Don't perform checking done by
+       CheckJNI by default.
+       (android_copy_area): Check for errors here because CopyArea can
+       perform a lot of consing.
+       (android_define_cursor): Remove redundant code.
+
+       Fix problems with the menu bar on large screen Android devices
+       * java/org/gnu/emacs/EmacsActivity.java (onContextMenuClosed):
+       Process submenu closing normally if it happens more than 300 ms
+       after a submenu item was selected.
+       * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu)
+       (onMenuItemClick, display1): Give `wasSubmenuSelected' different
+       values depending on how the submenu was selected.
+
+       * lib/gnulib.mk.in: Update from gnulib.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-11  Po Lu  <luangruo@yahoo.com>
+
+       Don't use GCC extensions in src/emacs-module.c
+       * configure.ac: Default modules to on.  Remove check for
+       __attribute__((cleanup)).  However, keep the new `ifavailable'
+       value for systems without dlopen.
+
+       * src/emacs-module.c (MODULE_HANDLE_NONLOCAL_EXIT): Don't rely
+       on cleanup attribute and correctly reset handlerlist upon
+       longjmp.
+       (MODULE_INTERNAL_CLEANUP): New macro.
+       (module_make_global_ref, module_free_global_ref)
+       (module_make_function, module_get_function_finalizer)
+       (module_set_function_finalizer, module_make_interactive)
+       (module_funcall, module_intern, module_type_of)
+       (module_extract_integer, module_make_integer, module_extract_float)
+       (module_make_float, module_copy_string_contents)
+       (module_make_string, module_make_unibyte_string)
+       (module_make_user_ptr, module_get_user_ptr, module_set_user_ptr)
+       (module_get_user_finalizer, module_set_user_finalizer)
+       (module_vec_set, module_vec_get, module_vec_size)
+       (module_process_input, module_extract_time, module_make_time)
+       (module_extract_big_integer, module_make_big_integer)
+       (module_open_channel): Call MODULE_INTERNAL_CLEANUP prior to
+       returning.
+
+2023-03-11  Po Lu  <luangruo@yahoo.com>
+
+       Implement hourglass cursor on Android
+       * lisp/term/android-win.el (x-pointer-arrow, x-pointer-left-ptr)
+       (x-pointer-left-side, x-pointer-sb-h-double-arrow)
+       (x-pointer-sb-v-double-arrow, x-pointer-watch, x-pointer-xterm)
+       (x-pointer-invisible): New constants.
+       * src/androidterm.c (android_show_hourglass)
+       (android_hide_hourglass): New functions.
+       (android_toggle_visible_pointer, android_define_frame_cursor):
+       Define or don't define hourglass cursor if x->hourglass.
+       (android_redisplay_interface): Add new functions.
+       * src/androidterm.h (struct android_output): New field
+       `hourglass'.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-10  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * doc/emacs/android.texi (Android Windowing): Document how to
+       pass multimedia keys to the system.
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New
+       function.
+       * java/org/gnu/emacs/EmacsView.java (onKeyDown, onKeyMultiple)
+       (onKeyUp): Check that function.
+       * java/org/gnu/emacs/EmacsWindow.java (defineCursor): Handle
+       cases where cursor is NULL.
+       * src/android.c (NATIVE_NAME): New function.
+       * src/androidfns.c (syms_of_androidfns): New variable.
+       * src/keyboard.c (lispy_function_keys): Add volume keys.
+
+       * java/org/gnu/emacs/EmacsCursor.java: New file.
+
+       Implement mouse cursors on Android 7.0 and later
+       * java/org/gnu/emacs/EmacsWindow.java (defineCursor): New
+       function.
+       * src/android.c (struct android_emacs_cursor): New struct.
+       (android_init_emacs_cursor): New function.
+       (JNICALL): Call it.
+       (android_create_font_cursor, android_define_cursor)
+       (android_free_cursor): New functions.
+       * src/android.h (enum android_handle_type): Add cursor handle
+       type.
+       * src/androidfns.c (Fx_create_frame, android_create_tip_frame)
+       (enum mouse_cursor, struct mouse_cursor_types, mouse_cursor_types)
+       (struct mouse_cursor_data, android_set_mouse_color)
+       (syms_of_androidfns):
+       * src/androidgui.h (enum android_cursor_shape):
+       * src/androidterm.c (make_invisible_cursor)
+       (android_toggle_invisible_pointer, android_free_frame_resources)
+       (android_define_frame_cursor):
+       * src/androidterm.h (struct android_display_info)
+       (struct android_output): Port mouse cursor code over from X.
+
+       * java/org/gnu/emacs/EmacsNative.java: Add missing dependency.
+
+       Avoid using Linux sysfs APIs to access battery state on Android
+       * lisp/battery.el (battery-status-function): Don't look for /sys,
+       /proc* on Android.  Explain why.
+
+       Port Android battery status to Android 4.4 and earlier
+       * java/org/gnu/emacs/EmacsService.java (EmacsService)
+       (queryBattery19): New function.
+       (queryBattery): Call it on old systems.  Also, return AC line
+       status and temperature.
+       * lisp/battery.el (battery-android): Implement more format
+       directives.
+       * src/android.c (android_query_battery): Handle new status
+       fields.
+       * src/android.h (struct android_battery_state): Add `plugged'
+       and `temperature'.
+       * src/androidfns.c (Fandroid_query_battery): Return new fields.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-09  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * src/android.c (android_destroy_handle): Handle OOM errors in
+       android_destroy_handle.
+
+       * textconv.c: Remove out-of-date comment.
+
+2023-03-09  Po Lu  <luangruo@yahoo.com>
+
+       Fix menu and popup race conditions on Android
+       * java/org/gnu/emacs/EmacsActivity.java (onContextMenuClosed):
+       * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu)
+       (onMenuItemClick, run):
+       * java/org/gnu/emacs/EmacsDialog.java (EmacsDialog, onClick)
+       (createDialog, onDismiss): Take menu event serial, and pass it
+       along in context menu events.
+       * java/org/gnu/emacs/EmacsNative.java (sendContextMenu): New
+       argument.
+       * src/android.c (sendContextMenu): Pass serial number in event.
+
+       * src/androidgui.h (struct android_menu_event): New field
+       `menu_event_serial'.
+       * src/androidmenu.c (FIND_METHOD_STATIC)
+       (android_init_emacs_context_menu): Adjust method declarations.
+       (android_menu_show, android_dialog_show):
+       * src/androidterm.c (handle_one_android_event): Expect serial in
+       context menu events.
+       * src/androidterm.h: Update prototypes.
+
+2023-03-09  Po Lu  <luangruo@yahoo.com>
+
+       Fix webp test for Android
+       * configure.ac (HAVE_WEBP): Disable WebPGetInfo check when
+       REALLY_ANDROID.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * java/debug.sh (is_root): Port to android versions which don't
+       support `chmod +x'.
+       * src/android.c (android_content_name_p): Disable before API
+       level 19.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu)
+       (addItem): New argument `tooltip'.
+
+       Update Android port
+       * src/android.c (android_build_string): Convert the text to
+       UTF-16, and create the Java string using that.
+       (android_build_jstring): Update comment.
+       * src/androidmenu.c (android_init_emacs_context_menu): Add
+       String argument to `addItem'.
+       (android_menu_show): Correctly pass help strings in regular menu
+       items.
+       * src/sfnt.c (_sfnt_swap16, _sfnt_swap32): Avoid reserved names.
+
+       Fix crash upon restoring desktop
+       * src/android.c (android_set_input_focus): Don't call method on
+       window using service class.
+
+       * src/sfnt.c (ODD): Use PUSH_UNCHECKED.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-08  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * src/fileio.c (Fcopy_file): On Android, ignore ENOSYS and
+       ENOTSUP when restoring file times, as the system call used is
+       supported by many kernels.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Fix occasional crash
+       * src/androidterm.c (android_build_extracted_text): Return NULL
+       if text class not initialized.
+       (android_update_selection): Check that EXTRACTED is not NULL.
+
+       Update Android port
+       * doc/emacs/android.texi (Android File System): Document what
+       `temp~unlinked' means in the temporary files directory.
+       * java/org/gnu/emacs/EmacsService.java (updateExtractedText):
+       New function.
+       * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection):
+       Ask the input method nicely to not display the extracted text
+       UI.
+       * src/android.c (struct android_emacs_service): New method
+       `updateExtractedText'.
+       (android_hack_asset_fd_fallback): Improve naming convention.
+       Fix typo.
+       (android_init_emacs_service): Add new method.
+       (android_update_extracted_text): New function.
+       (android_open_asset): Fix typo.
+       * src/androidgui.h: Update prototypes.
+       * src/androidterm.c (struct android_get_extracted_text_context):
+       New field `flags'.
+       (android_get_extracted_text): Set flags on the frame's output
+       data.
+       (android_build_extracted_text): New function.
+       (getExtractedText): Move out class structures.
+       (android_update_selection): Send updates to extracted text if
+       the input method asked for them.
+       (android_reset_conversion): Clear extracted text flags.
+       * src/androidterm.h (struct android_output): New fields for
+       storing extracted text data.
+
+       Fix double free upon encountering invalid font
+       * src/sfnt.c (sfnt_read_cmap_table): Don't allocate too big
+       data.  Also, free elements of (*data), not offsets into data
+       itself.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-07  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Save build timestamps in Android builds
+       * java/Makefile.in (install_temp/assets/build_info): New
+       rule.:(emacs.apk-in): Depend on that file.
+       * lisp/version.el (android-read-build-system)
+       (android-read-build-time): New functions.
+       (emacs-build-system, emacs-build-time): Use those functions on
+       Android, as dumping is done after installation on Android.
+       * src/fileio.c (Finsert_file_contents):
+       * src/window.c (replace_buffer_in_windows): Don't call functions
+       if they are not defined, which can happen during loadup.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsWindow.java (onSomeKindOfMotionEvent):
+       Dismiss splurious LeaveNotify events from button presses.
+       * src/android.c (android_change_window_attributes)
+       (android_change_gc, android_set_clip_rectangles)
+       (android_reparent_window, android_clear_window, android_map_window)
+       (android_unmap_window, android_resize_window, android_move_window)
+       (android_swap_buffers, android_fill_rectangle, android_copy_area)
+       (android_fill_polygon, android_draw_rectangle, android_draw_point)
+       (android_draw_line, android_clear_area, android_bell)
+       (android_set_input_focus, android_raise_window)
+       (android_lower_window, android_set_dont_focus_on_map)
+       (android_set_dont_accept_focus, android_get_keysym_name)
+       (android_toggle_on_screen_keyboard, android_restart_emacs)
+       (android_display_toast, android_update_ic, android_reset_ic)
+       (android_set_fullscreen): Optimize by specifying the class
+       explicitly when calling a method.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * src/lread.c (lread_fd, file_tell, infile, skip_dyn_bytes)
+       (skip_dyn_eof, readbyte_from_stdio, safe_to_load_version)
+       (close_infile_unwind, close_file_unwind_android_fd): New
+       function.
+       (Fload, Flocate_file_internal, openp): New argument PLATFORM.
+       All callers changed.
+       (skip_lazy_string): Add optimized versions of various functions
+       for accessing Android assets.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-06  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New
+       function requestSelectionUpdate.
+       * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection):
+       Call it instead of losing if getting the current selection
+       fails.
+       * src/android-asset.h (AAsset_seek): Define stub.
+       * src/android.c (android_open): Take mode_t.
+       (android_open_asset, android_close_asset, android_asset_read_quit)
+       (android_asset_read, android_asset_lseek, android_asset_fstat):
+       New functions.
+       * src/android.h (struct android_fd_or_asset): Update prototypes.
+       * src/androidgui.h (enum android_ime_operation): Add new
+       operation to update the selection position.
+       * src/androidterm.c (android_handle_ime_event): Handle new
+       operation.
+       (requestSelectionUpdate): New function.
+       * src/fileio.c (close_file_unwind_emacs_fd): New function.
+       (Fcopy_file, union read_non_regular, read_non_regular)
+       (Finsert_file_contents): Use optimized codepath to insert
+       Android asset files.
+       * src/frame.h (enum text_conversion_operation): New operation.
+       * src/textconv.c (really_request_point_update)
+       (handle_pending_conversion_events_1, request_point_update): New
+       functions.
+       * src/textconv.h: Update prototypes.
+
+       * src/conf_post.h: Avoid macro redeclaration.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsService.java (sync): Delete function.
+       * java/org/gnu/emacs/EmacsView.java (handleDirtyBitmap): Erase
+       with window background.
+       (onDetachedFromWindow): Only recycle bitmap if non-NULL.
+       * java/org/gnu/emacs/EmacsWindow.java (background): New field.
+       (changeWindowBackground): Set it.
+       * src/android.c (struct android_emacs_service): Remove `sync'.
+       (android_init_emacs_service): Likewise.
+       (android_sync): Delete function.
+       * src/androidfns.c (android_create_tip_frame): Set frame
+       background color correctly.
+       (Fx_show_tip): Make the tip frame visible.
+       * src/androidgui.h: Update prototypes.
+       * src/androidterm.c (handle_one_android_event): Handle tooltip
+       movement correctly.
+
+2023-03-05  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsActivity.java (onCreate):
+       * java/org/gnu/emacs/EmacsContextMenu.java:
+       * java/org/gnu/emacs/EmacsDocumentsProvider.java (getMimeType):
+       * java/org/gnu/emacs/EmacsDrawLine.java (perform):
+       * java/org/gnu/emacs/EmacsDrawRectangle.java (perform):
+       * java/org/gnu/emacs/EmacsFillPolygon.java:
+       * java/org/gnu/emacs/EmacsFontDriver.java:
+       * java/org/gnu/emacs/EmacsHandleObject.java:
+       * java/org/gnu/emacs/EmacsInputConnection.java:
+       * java/org/gnu/emacs/EmacsMultitaskActivity.java
+       (EmacsMultitaskActivity):
+       * java/org/gnu/emacs/EmacsNative.java:
+       * java/org/gnu/emacs/EmacsNoninteractive.java
+       (EmacsNoninteractive, main):
+       * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity)
+       (startEmacsClient):
+       * java/org/gnu/emacs/EmacsSdk7FontDriver.java:
+       * java/org/gnu/emacs/EmacsSdk8Clipboard.java:
+       * java/org/gnu/emacs/EmacsService.java (EmacsService, onCreate):
+       * java/org/gnu/emacs/EmacsView.java (EmacsView, onLayout):
+       * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow):
+       * java/org/gnu/emacs/EmacsWindowAttachmentManager.java
+       (EmacsWindowAttachmentManager): Remove redundant includes.
+       Reorganize some functions around, remove duplicate `getLibDir'
+       functions, and remove unused local variables.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsOpenActivity.java (onCreate): Don't
+       set the style here.
+       * java/res/values-v11/style.xml:
+       * java/res/values-v14/style.xml:
+       * java/res/values-v29/style.xml:
+       * java/res/values/style.xml: Define styles for the emacsclient
+       wrapper.
+       * src/keyboard.c (read_key_sequence): Don't disable text
+       conversion if use_mouse_menu or if a menu bar prefix key is
+       being displayed.
+
+       Update Android port
+       * etc/PROBLEMS: Document problem with default monospace font.
+       * src/fileio.c (check_mutable_filename): Check /content as well.
+       (Fcopy_file, Fdelete_directory_internal, Fdelete_file)
+       (Frename_file, Fadd_name_to_file, Fmake_symbolic_link)
+       (Fset_file_modes, Fset_file_times, Ffile_newer_than_file_p)
+       (write_region): Adjust accordingly.
+       (Fset_visited_file_modtime): Remove unnecessary restriction.
+       * src/filelock.c (make_lock_file_name): Don't interlock files
+       under /assets and /content.
+       * src/inotify.c (Finotify_add_watch): Fix typo.
+
+       Fix cross compilation of cross/lib in some cases
+       * cross/Makefile.in: (config.status): Depend on
+       top_builddir/config.status instead.
+
+       * configure.ac: Fix another typo.
+
+       * cross/Makefile.in (builddir): Define.
+
+       * cross/README: Update.
+
+       Remove redundant gnulib files
+       * cross/lib: Delete.  Make configure generate it instead.
+
+       Remove redundant second copy of gnulib
+       * .gitignore: Simplify cross/lib rule.
+       * admin/merge-gnulib (avoided_flags): Stop copying to cross/lib.
+       * configure.ac: Link gnulib source and header files to
+       cross/lib.
+       * cross/Makefile.in (LIB_SRCDIR): Make relative to builddir.
+       (maintainer-clean): Merge with distclean.  Remove links created
+       by configure.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-04  Po Lu  <luangruo@yahoo.com>
+
+       Fix x86_64 builds of libjpeg on Android
+       * cross/ndk-build/ndk-build-shared-library.mk:
+       * cross/ndk-build/ndk-build-static-library.mk: Specify right ELF
+       format for 64 bit executables.
+
+       Fix calls to nasm in cross/ndk-build
+       * cross/ndk-build/ndk-build-shared-library.mk:
+       * cross/ndk-build/ndk-build-static-library.mk: Ensure nasm
+       generates ELF objects.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Fix out of bound write after poly of single pixel span
+       * src/sfnt.c (sfnt_fill_span): Specifically handle spans that
+       span a single pixel by computing the coverage in the center.
+
+       Improve context menus on old versions of Android
+       * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): New
+       field `lastClosedMenu'.
+       (onContextMenuClosed): Don't send event if a menu is closed
+       twice in a row.  Also, clear wasSubmenuSelected immediately.
+       * java/org/gnu/emacs/EmacsContextMenu.java: Display submenus
+       manually in Android 6.0 and earlier.
+       * java/org/gnu/emacs/EmacsView.java (onCreateContextMenu)
+       (popupMenu): Adjust accordingly.
+
+       Port to broken Android NDK version
+       * configure.ac: Check for __ctype_get_mb_cur_max.
+       Then see if MB_CUR_MAX is defined to it, and define
+       REPLACEMENT_MB_CUR_MAX if so and it does not link.
+       * java/INSTALL: Update documentation.
+       * src/conf_post.h (MB_CUR_MAX): Define replacement if
+       necessary.
+
+       * m4/ndk-build.m4 (ndk_INIT): Fix typo.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Improve support for building Android C++ dependencies
+       * configure.ac: Call ndk_LATE after gl_EARLY.
+       * cross/ndk-build/Makefile.in (NDK_BUILD_CXX): New variable.
+       * cross/ndk-build/ndk-build-shared-library.mk:
+       * cross/ndk-build/ndk-build-static-library.mk: Use it.
+       * java/INSTALL: Describe how to build C++ dependencies.
+       * m4/ndk-build.m4 (ndk_LATE): New macro.
+       (ndk_INIT): Try to find a suitable C++ compiler.
+       (ndk_CHECK_MODULES): Make sure the C++ compiler works before
+       allowing C++ dependencies.
+
+       Fix cross-compilation of C++ code with old NDK versions
+       * cross/ndk-build/Makefile.in (NDK_BUILD_CFLAGS_CXX): New variable.
+       * cross/ndk-build/ndk-build-shared-library.mk
+       ($(call objname,$(LOCAL_MODULE),$(basename $(1)))):
+       * cross/ndk-build/ndk-build-static-library.mk
+       ($(call objname,$(LOCAL_MODULE),$(basename $(1)))): Use it to build
+       C++ code.
+
+2023-03-03  Po Lu  <luangruo@yahoo.com>
+
+       Minor fixes to configury
+       * configure.ac (ANDROID_SDK_8_OR_EARLIER): Pass through
+       `--with-ndk-cxx-shared'.
+       * m4/ndk-build.m4: Fix quoting of $CC.
+
+       Fix out-of-tree builds with native dependencies
+       * cross/ndk-build/ndk-build-shared-library.mk:
+       * cross/ndk-build/ndk-build-static-library.mk: Include
+       ndk-resolve.mk in srcdir.
+
+       * cross/ndk-build/README: Update accordingly.
+
+       Improve ndk-build implementation
+       * build-aux/ndk-build-helper.mk: Define in terms of BUILD_AUXDIR.
+       * m4/ndk-build.m4 (ndk_INIT): Find right build-aux directory.
+       Remove uses of unportable shell constructs.
+
+       Fix visiting and saving writable content provider files
+       * java/org/gnu/emacs/EmacsService.java (checkContentUri):
+       Improve debug output.
+       * lisp/files.el (basic-save-buffer): Check whether or not file
+       itself exists before checking for the existence of the directory
+       containing it.
+       * src/android.c (android_open): Don't forget to set errno after
+       open_content_uri fails.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-03  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity)
+       (onCreate): Add view tree observer.
+       (onGlobalLayout): Sync fullscreen state.
+       (syncFullscreenWith): Improve visibility flag setting.
+
+       * src/textconv.c (select_window): New function.
+       (textconv_query):
+       (restore_selected_window):
+       (really_commit_text):
+       (really_set_composing_text):
+       (really_set_composing_region):
+       (really_delete_surrounding_text):
+       (really_set_point_and_mark):
+       (get_extracted_text): Call it instead of Fselect_window
+       to avoid selecting the mini window if it is no longer active.
+
+2023-03-03  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-02  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       * doc/emacs/input.texi (On-Screen Keyboards): Fix indexing.
+
+       Summary: Update Android port
+       * INSTALL: Document where to find Android installation
+       instructions.
+       * configure.ac (CHECK_LISP_OBJECT_TYPE): Pacify
+       -Wsuggest-attribute=noreturn only on Android.
+       * cross/ndk-build/README: New file.
+       * doc/emacs/android.texi (Android):
+       * doc/emacs/emacs.texi (Top):
+       * doc/emacs/input.texi (Other Input Devices): Untabify menus.
+       * etc/NEWS: Move INSTALL.android to java/INSTALL.
+       * java/INSTALL: New file.
+       * java/README:
+       * src/coding.c (from_unicode_buffer): Make Android specific code
+       only build on Android.
+
+       * INSTALL.android: Remove file.
+
+       Fix Makefile race conditions
+       * configure.ac: Make cross/* and related directories.
+       * cross/Makefile.in (src/verbose.mk, lib/libgnu.a)
+       (src/config.h): Stop making directories here.
+       (lib-src/config.h): New config.h rule.
+       ($(LIBSRC_BINARIES)): Add it.
+       (clean): Don't remove CLEAN_SUBDIRS, but clean inside.
+
+       Fix Android handle wraparound
+       * src/android.c (android_alloc_id): Return correct values upon
+       wraparound.
+
+2023-03-02  Po Lu  <luangruo@yahoo.com>
+
+       Improve criteria for restoring fullscreen state on Android
+       * java/Makefile.in ($(CLASS_FILES) &): Touch all class files,
+       even those javac chose not to rebuild.
+
+       * java/org/gnu/emacs/EmacsActivity.java (onWindowFocusChanged):
+       Restore fullscreen state here.
+       (onResume): And not here.
+
+2023-03-02  Po Lu  <luangruo@yahoo.com>
+
+       Fix sectioning of android texi files
+       * doc/emacs/android.texi (Android):
+       * doc/emacs/emacs.texi (Top, GNU Free Documentation License):
+       Rearrange menu and sectioning.
+
+       Update Android port
+       * doc/emacs/android.texi (Android Windowing): Reword
+       documentation.
+       * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity):
+       * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu):
+       * java/org/gnu/emacs/EmacsFontDriver.java (EmacsFontDriver):
+       * java/org/gnu/emacs/EmacsSdk7FontDriver.java
+       (EmacsSdk7FontDriver):
+       * java/org/gnu/emacs/EmacsService.java (queryBattery):
+       * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): Make
+       functions final and classes static where necessary.
+       * src/android.c (struct android_emacs_service): New method
+       `display_toast'.
+       (android_init_emacs_service): Load new method.
+       (android_display_toast): New function.
+       * src/android.h: Export.
+       * src/androidfns.c (Fandroid_detect_mouse):
+       * src/androidselect.c (Fandroid_clipboard_owner_p)
+       (Fandroid_set_clipboard, Fandroid_get_clipboard)
+       (Fandroid_browse_url): Prevent crashes when called from
+       libandroid-emacs.so.
+       * src/androidterm.c (handle_one_android_event): Fix out of date
+       commentary.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-03-01  Po Lu  <luangruo@yahoo.com>
+
+       Fix out-of-tree Android builds
+       * configure.ac (JAVA_PUSH_LINT): Push to WARN_JAVAFLAGS instead
+       of JAVAFLAGS.
+       (cross/lib): Always AS_MKDIR_P.
+       * cross/Makefile.in (srcdir): New variable.
+       (LIB_SRCDIR): Take realpath relative to srcdir, not
+       .:(src/verbose.mk): Depend on verbose.mk.android in srcdir.
+       (lib/Makefile): Edit srcdir and VPATH to LIB_SRCDIR.
+       (src/Makefile): Edit -I$$(top_srcdir) to -I../$(srcdir)/lib,
+       instead of ommitting it.
+       (clean): Allow ndk-build clean to fail.
+
+       * java/Makefile.in (builddir): New variable.
+       (WARN_JAVAFLAGS): Likewise.
+       (JAVAFLAGS): Define in terms of WARN_JAVAFLAGS.
+       (SIGN_EMACS, SIGN_EMACS_V2): Use emacs.keystore relative to
+       srcdir.  Allow inclusion of ndk-build.mk to fail.
+       (install_temp, emacs.apk-in)
+       (../config.status): Depend relative to top_srcdir.
+       (AndroidManifest.xml, $(APK_NAME)): Likewise.
+       (RESOURCE_FILE, CLASS_FILES, classes.dex): Output class files
+       to $(srcdir); these are arch independents, so this is okay.
+
+2023-03-01  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Fix mostlyclean rules
+       * cross/Makefile.in: Remove outdated comment.
+       * src/Makefile.in: (.PHONY): Clean android-emacs and
+       libemacs.so, not emacs.so and aemacs.
+
+       Update Android port
+       * doc/emacs/android.texi (Android File System): Document new
+       behavior of starting a subprocess from /assets.
+       * java/org/gnu/emacs/EmacsWindow.java (onSomeKindOfMotionEvent):
+       Don't use isFromSource where not present.
+       * src/androidterm.c (android_scroll_run): Avoid undefined
+       behavior writing to bitfields.
+       * src/callproc.c (get_current_directory): When trying to run a
+       subprocess inside /assets, run it from the home directory
+       instead.
+
+       Update Android port
+       * java/AndroidManifest.xml.in: Specify @style/EmacsStyle.
+       * java/org/gnu/emacs/EmacsActivity.java (onCreate): Stop setting
+       the theme here.
+       * java/res/values-v11/style.xml:
+       * java/res/values-v14/style.xml:
+       * java/res/values-v29/style.xml:
+       * java/res/values/style.xml: Extract style resources into
+       res/values.
+
+       Update Android port
+       * java/Makefile.in (ETAGS, clean): New rules to generate tags.
+       * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity):
+       * java/org/gnu/emacs/EmacsApplication.java (EmacsApplication):
+       * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu):
+       * java/org/gnu/emacs/EmacsCopyArea.java (EmacsCopyArea):
+       * java/org/gnu/emacs/EmacsDialog.java (EmacsDialog)::(dialog.
+       Then):
+       * java/org/gnu/emacs/EmacsDocumentsProvider.java
+       (EmacsDocumentsProvider):
+       * java/org/gnu/emacs/EmacsDrawLine.java (EmacsDrawLine):
+       * java/org/gnu/emacs/EmacsDrawPoint.java (EmacsDrawPoint):
+       * java/org/gnu/emacs/EmacsDrawRectangle.java
+       (EmacsDrawRectangle):
+       * java/org/gnu/emacs/EmacsFillPolygon.java (EmacsFillPolygon):
+       * java/org/gnu/emacs/EmacsFillRectangle.java
+       (EmacsFillRectangle):
+       * java/org/gnu/emacs/EmacsGC.java (EmacsGC):
+       * java/org/gnu/emacs/EmacsInputConnection.java
+       (EmacsInputConnection):
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative):
+       * java/org/gnu/emacs/EmacsNoninteractive.java
+       (EmacsNoninteractive):
+       * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity):
+       * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap):
+       * java/org/gnu/emacs/EmacsPreferencesActivity.java
+       (EmacsPreferencesActivity):
+       * java/org/gnu/emacs/EmacsSdk11Clipboard.java
+       (EmacsSdk11Clipboard):
+       * java/org/gnu/emacs/EmacsSdk23FontDriver.java
+       (EmacsSdk23FontDriver):
+       * java/org/gnu/emacs/EmacsSdk8Clipboard.java
+       (EmacsSdk8Clipboard):
+       * java/org/gnu/emacs/EmacsService.java (EmacsService):
+       * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView)
+       (buffers):
+       * java/org/gnu/emacs/EmacsView.java (EmacsView, ViewGroup):
+       * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, drawables):
+       * java/org/gnu/emacs/EmacsWindowAttachmentManager.java
+       (EmacsWindowAttachmentManager): Make classes final where
+       appropriate.
+
+       More fixes to JNI error checking
+       * src/android.c (android_query_tree, android_get_geometry)
+       (android_translate_coordinates, android_query_battery):
+       Correctly handle result of GetTArrayElements.
+       (android_exception_check_nonnull): New function.
+       * src/android.h:
+       * src/androidselect.c (Fandroid_get_clipboard): Likewise.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-02-28  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * src/sfnt.c (main):
+       * src/sfntfont.c (sfntfont_get_glyph_outline): Remove outdated
+       comment.
+
+2023-02-26  Po Lu  <luangruo@yahoo.com>
+
+       Get rid of android_lookup_method
+       * src/android.c (struct android_emacs_window): New methods.
+       (android_init_emacs_window): Add new methods.
+       (android_lookup_method): Delete now-unused function.
+       (android_change_window_attributes, android_reparent_window)
+       (android_map_window, android_unmap_window, android_resize_window)
+       (android_move_window, android_set_input_focus)
+       (android_raise_window, android_lower_window, android_get_geometry)
+       (android_translate_coordinates, android_set_dont_focus_on_map)
+       (android_set_dont_accept_focus): Don't look up the class and
+       method each time when calling a function; that's just waste.
+
+       Update from gnulib
+       * cross/lib/unistd.in.h:
+       * lib/gnulib.mk.in (INT64_MAX_EQ_LONG_MAX):
+       * m4/gnulib-comp.m4 (gl_EARLY): Update from gnulib.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * doc/lispref/commands.texi (Misc Events): Update documentation.
+       * java/org/gnu/emacs/EmacsService.java (EmacsService)
+       (onStartCommand): Improve notification message.
+       * src/android.c (android_hack_asset_fd): Detect if ashmem is
+       available dynamically.
+       (android_detect_ashmem): New function.
+       * src/textconv.c (record_buffer_change): Use markers to
+       represent BEG and END instead.
+       (syms_of_textconv): Update doc string.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-02-25  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/debug.sh (is_root): Fix tee detection again for old
+       systems which don't return exit codes from adb shell.
+       * src/android.c (android_run_select_thread, NATIVE_NAME,
+       JNICALL):
+       * src/android.h (NATIVE_NAME):
+       * src/androidterm.c (JNICALL, NATIVE_NAME): Apply stack
+       alignment to all JNICALL functions.
+
+       * src/android.c (android_open): Clean up unused variables.
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsNoninteractive.java (main): Port to
+       Android 2.2.
+       * src/android-asset.h (AAsset_openFileDescriptor): Delete stub
+       function.
+       * src/android.c (android_check_compressed_file): Delete
+       function.
+       (android_open): Stop trying to find compressed files or to use
+       the system provided file descriptor.  Explain why.
+
+       Update Android port
+       * doc/emacs/android.texi (Android Startup, Android File System)
+       (Android Environment, Android Windowing, Android
+       Troubleshooting): Improve documentation; fix typos.
+       * doc/lispref/commands.texi (Misc Events): Likewise.
+       * java/org/gnu/emacs/EmacsService.java (queryBattery): New
+       function.
+       * lisp/battery.el (battery-status-function): Set appropriately
+       for Android.
+       (battery-android): New function.
+       * src/android.c (struct android_emacs_service): New method
+       `query_battery'.
+       (android_check_content_access): Improve exception checking.
+       (android_init_emacs_service): Look up new method.
+       (android_destroy_handle, android_create_window)
+       (android_init_android_rect_class, android_init_emacs_gc_class)
+       (android_set_clip_rectangles)
+       (android_create_pixmap_from_bitmap_data, android_fill_polygon)
+       (android_get_image, android_put_image, android_bell)
+       (android_set_input_focus, android_raise_window)
+       (android_lower_window, android_query_tree, android_get_geometry)
+       (android_translate_coordinates, android_wc_lookup_string)
+       (android_damage_window, android_build_string)
+       (android_build_jstring, android_exception_check_1)
+       (android_exception_check_2): New functions.
+       (android_browse_url): Improve exception handling.  Always use
+       android_exception_check and don't leak local refs.
+       (android_query_battery): New function.
+       * src/android.h (struct android_battery_state): New struct.
+       * src/androidfns.c (Fandroid_query_battery, syms_of_androidfns):
+       New function.
+       * src/androidfont.c (androidfont_from_lisp, DO_SYMBOL_FIELD)
+       (DO_CARDINAL_FIELD, androidfont_list, androidfont_match)
+       (androidfont_draw, androidfont_open_font)
+       (androidfont_close_font):
+       * src/androidselect.c (Fandroid_set_clipboard)
+       (Fandroid_get_clipboard):
+       * src/sfnt.c (sfnt_map_glyf_table):
+       * src/sfntfont.c (sfntfont_free_outline_cache)
+       (sfntfont_free_raster_cache, sfntfont_close): Allow font close
+       functions to be called twice.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-02-24  Po Lu  <luangruo@yahoo.com>
+
+       Improve Android configury
+       * configure.ac (JAVA_PUSH_LINT): New macro.
+       (JAVAFLAGS): New variable.  Check for various lint flags and
+       macros and enable them.
+       * java/Makefile.in (ANDROID_ABI):
+       * java/org/gnu/emacs/EmacsSdk7FontDriver.java: Remove compiler
+       warning.
+
+       Enable normal-erase-is-backspace on Android
+       * lisp/frame.el (display-symbol-keys-p):
+       * lisp/simple.el (normal-erase-is-backspace-setup-frame): Return
+       appropriate values on Android.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Fix auto-revert-mode on Android
+       * src/inotify.c (Finotify_add_watch): Handle asset files.
+
+       * src/keyboard.c (lispy_function_keys): Add missing delete key.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-02-23  Po Lu  <luangruo@yahoo.com>
+
+       Make sure scroll-bar.el is loaded on Android
+       * lisp/loadup.el: Update commentary.
+       * src/androidterm.c (syms_of_androidterm): Define
+       Vx_toolkit_scroll_bars.
+       * src/xterm.c (syms_of_xterm): Update doc string.xf64
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Fix ImageMagick build on Android
+       * INSTALL.android (-linux_arm_sources):
+       * build-aux/ndk-build-helper-1.mk:
+       (NDK_$(LOCAL_MODULE)_STATIC_LIBRARIES)::(NDK_CXX_FLAG_$(LOCAL_MODULE)):
+       * build-aux/ndk-build-helper-2.mk:
+       (NDK_$(LOCAL_MODULE)_STATIC_LIBRARIES)::(NDK_CXX_FLAG_$(LOCAL_MODULE)):
+       * cross/ndk-build/ndk-build-shared-library.mk (objname)::($(call
+       objname,$(LOCAL_MODULE),$(basename
+       $(1))))::(ALL_OBJECT_FILES$(LOCAL_MODULE)):
+       * cross/ndk-build/ndk-build-static-library.mk (objname)::($(call
+       objname,$(LOCAL_MODULE),$(basename
+       $(1))))::(ALL_OBJECT_FILES$(LOCAL_MODULE)):
+       (ALL_SOURCE_FILES): Update ImageMagick build instructions and
+       C++ module detection.
+
+       * src/android.c (android_run_select_thread): Fix typos.
+
+       Make android_select more robust
+       * src/android.c (android_run_select_thread): Lock select_mutex
+       before signalling condition variable.
+       (android_select): Unlock event queue mutex prior to waiting for
+       it.
+
+2023-02-22  Po Lu  <luangruo@yahoo.com>
+
+       ; Fix typo
+       * cross/ndk-build/ndk-build-shared-library.mk: Fix typo.
+
+       * src/image.c (imagemagick_load_image): Check HAVE_DECL_xxx.
+
+2023-02-22  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       ImageMagick now builds but does not link yet some of the time.
+
+       * INSTALL.android: Document ImageMagick and caveats.
+       * build-aux/ndk-build-helper-1.mk (NDK_SO_NAMES):
+       * build-aux/ndk-build-helper-2.mk (NDK_A_NAMES):
+       * build-aux/ndk-build-helper.mk (TARGET_ARCH_ABI): Define architecture
+       and don't respect explicitly specified library names.
+       * configure.ac: Enable ImageMagick and lcms2 on Android.
+       * cross/ndk-build/ndk-build-shared-library.mk (objname)::($(call
+       objname,$(LOCAL_MODULE),$(basename
+       $(1))))::(ALL_OBJECT_FILES$(LOCAL_MODULE)):
+       * cross/ndk-build/ndk-build-static-library.mk (objname)::($(call
+       objname,$(LOCAL_MODULE),$(basename $(1)))):
+       (NDK_CFLAGS, ALL_SOURCE_FILES): Handle sources files which start with
+       $(LOCAL_PATH).
+       * cross/ndk-build/ndk-clear-vars.mk: Don't undefine; clear variables
+       instead.
+       * m4/ndk-build.m4 (ndk_SEARCH_MODULE): Redirect make stderr to
+       config.log.xf64
+
+2023-02-22  Po Lu  <luangruo@yahoo.com>
+
+       * src/androidmenu.c (android_menu_show): Fix typo.
+
+       Update Android port
+       * doc/emacs/input.texi (On-Screen Keyboards): Document changes
+       to text conversion.
+       * java/org/gnu/emacs/EmacsInputConnection.java (getExtractedText)
+       (EmacsInputConnection):
+       * src/keyboard.c (read_key_sequence): Disable text conversion
+       after reading prefix key.
+       * src/textconv.c (get_extracted_text): Fix returned value when
+       request length is zero.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Add cross-compilation test for cleanup attribute
+       * configure.ac: Per title.
+
+       Update Android port
+       * INSTALL.android: Port to MIPS.
+       * configure.ac (modules): Default to ifavailable.
+       Write actual test for __attribute__((cleanup)).
+       * m4/ndk-build.m4: Recognize mips and mips64.
+       * src/emacs-module.c: Remove broken HAS_ATTRIBUTE test.
+
+2023-02-21  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu)
+       (addSubmenu, inflateMenuItems): Handle tooltips correctly.
+       * src/android.c (android_scan_directory_tree): Fix limit
+       generation for root directory.
+       * src/androidmenu.c (android_init_emacs_context_menu)
+       (android_menu_show): Implement menu item help text on Android
+       8.0 and later.
+
+       Update from gnulib
+       * admin/merge-gnulib (GNULIB_MODULES):
+       * cross/lib/getopt-pfx-core.h (optind):
+       * cross/lib/limits.in.h (BOOL_WIDTH):
+       * cross/lib/math.in.h:
+       * cross/lib/stpncpy.c (__stpncpy):
+       * cross/lib/string.in.h:
+       * lib/getopt-pfx-core.h (optind):
+       * lib/gnulib.mk.in (ANDROID_MIN_SDK):
+       (GL_COND_OBJ_STDIO_READ_CONDITION):
+       (LIBS):
+       (NDK_BUILD_AR):
+       (REPLACE__EXIT):
+       (libgnu_a_SOURCES):
+       * lib/limits.in.h (BOOL_WIDTH):
+       * lib/math.in.h:
+       * lib/stpncpy.c (__stpncpy):
+       * lib/string.in.h:
+       * m4/assert_h.m4 (gl_ASSERT_H):
+       * m4/fdopendir.m4 (gl_FUNC_FDOPENDIR):
+       * m4/getdelim.m4 (gl_FUNC_GETDELIM):
+       * m4/getline.m4 (gl_FUNC_GETLINE):
+       * m4/gnulib-common.m4 (gl_COMMON_BODY):
+       (gl_CONDITIONAL_HEADER):
+       (gl_CHECK_FUNCS_ANDROID):
+       * m4/gnulib-comp.m4 (gl_EARLY):
+       (gl_INIT):
+       (gl_FILE_LIST):
+       * m4/limits-h.m4:
+       * m4/lstat.m4 (gl_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK):
+       * m4/malloc.m4 (_AC_FUNC_MALLOC_IF):
+       * m4/printf.m4 (gl_PRINTF_SIZES_C99):
+       (gl_PRINTF_INFINITE):
+       (gl_PRINTF_INFINITE_LONG_DOUBLE):
+       (gl_PRINTF_DIRECTIVE_A):
+       (gl_PRINTF_DIRECTIVE_F):
+       (gl_PRINTF_FLAG_ZERO):
+       (gl_SNPRINTF_PRESENCE):
+       (gl_SNPRINTF_DIRECTIVE_N):
+       (gl_VSNPRINTF_ZEROSIZE_C99):
+       * m4/pselect.m4 (gl_FUNC_PSELECT):
+       * m4/readlink.m4 (gl_FUNC_READLINK):
+       * m4/realloc.m4 (_AC_FUNC_REALLOC_IF):
+       * m4/signbit.m4 (gl_SIGNBIT):
+       * m4/stpncpy.m4 (gl_FUNC_STPNCPY):
+       * m4/symlink.m4 (gl_FUNC_SYMLINK): Add gnulib module stpncpy.
+       * src/android.c: Include string.h.
+
+       Update Android port
+       * doc/emacs/android.texi (Android Startup): Document `content'
+       special directory.
+       * java/debug.sh (is_root): Improve /bin/tee detection.
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New
+       function `dup'.
+       * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity)
+       (checkReadableOrCopy, onCreate): Create content directory names
+       when the file is not readable.
+       * java/org/gnu/emacs/EmacsService.java (EmacsService)
+       (openContentUri, checkContentUri): New functions.
+       * src/android.c (struct android_emacs_service): New methods.
+       (android_content_name_p, android_get_content_name)
+       (android_check_content_access): New function.
+       (android_fstatat, android_open): Implement opening content URIs.
+       (dup): Export to Java.
+       (android_init_emacs_service): Initialize new methods.
+       (android_faccessat): Implement content file names.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-02-20  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-02-20  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * INSTALL.android: Explain where to get tree-sitter.
+
+       * configure.ac: Add support for dynamic modules and tree-sitter.
+
+       * doc/emacs/android.texi (Android Windowing):
+       * java/org/gnu/emacs/EmacsSdk11Clipboard.java
+       (EmacsSdk11Clipboard, ownsClipboard): Improve clipboard handling
+       and documentation.
+
+2023-02-20  Po Lu  <luangruo@yahoo.com>
+
+       Fix crash inside font-list-family
+       * src/androidfont.c (androidfont_list_family): Don't
+       unconditionally initialize the Android font driver.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Improve SFNT driver lookup efficiency
+       * src/fontset.c (fontset_find_font): Add compatibility test to
+       registry strangeness case.
+       * src/sfnt.c (sfnt_read_cmap_table): Don't read subtable data if
+       DATA is NULL.
+       * src/sfntfont.c (struct sfnt_font_desc): New field `registry'.
+       (sfnt_registry_for_subtable): New function.
+       (sfntfont_identify_cmap): Move above sfnt_grok_registry.
+       (sfnt_grok_registry): New function.
+       (sfnt_enum_font_1): Call it.
+       (sfntfont_registries_compatible_p): New function.
+       (sfntfont_list_1): Check registry compatibility.
+       (sfntfont_registry_for_desc): New function.
+       (mark_sfntfont): Mark desc->registry.
+
+       Improve reliability of Java code rebuilds
+       * java/Makefile.in ($(CLASS_FILES)): Depend on the Java
+       compiler's internal dependency tracking.
+
+2023-02-19  Po Lu  <luangruo@yahoo.com>
+
+       Match font registry after font is opened
+       * src/fontset.c (fontset_find_font): Work around TrueType
+       performance problem.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       * cross/Makefile.in (src/libemacs.so): Depend on libgnu.a.
+
+       More fixes to parallel Make
+       * cross/ndk-build/ndk-build.mk.in (NDK_BUILD_MODULES)
+       (NDK_BUILD_SHARED, NDK_BUILD_STATIC): Define group rule to build
+       all files so that they are built within one make process.
+       * java/Makefile.in: Reorganize cross compilation and make sure
+       there is only one make subprocess for each subdirectory of
+       cross.
+
+       More parallel build fixes
+       * cross/Makefile.in: (.PHONY):
+       * java/Makefile.in: (.PHONY):
+       * src/Makefile.in: (libemacs.so): Avoid calling ndk-build from
+       two places at once.  Build android-emacs separately from
+       libemacs.so.
+
+       Fix parallel compilation of Android port
+       * cross/Makefile.in ($(top_builddir)/lib/libgnu.a):
+       * java/Makefile.in (CROSS_LIBS): Explicitly depend on gnulib
+       to prevent it from being built at the same time from different
+       jobs.
+
+       Fix sfntfont.c build without mmap
+       * src/sfntfont.c (sfntfont_close): Don't unlink font if mmap is
+       not available.
+
+       Improve Android documentation
+       * INSTALL.android: Say where building Emacs is supported.
+       * doc/emacs/android.texi (Android Startup): Describe how to
+       connect via ADB.
+
+       Report both sides of the region to the input method upon setup
+       * java/org/gnu/emacs/EmacsNative.java (getSelection): Return
+       array of ints.
+       * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection):
+       Adjust accordingly.
+       * src/androidterm.c (struct android_get_selection_context): New
+       field `mark'.
+       (android_get_selection): Set the mark field as appropriate.
+       (getSelection): Adjust accordingly.
+
+       Fix gamegrid.el with high resolution displays
+       * lisp/play/gamegrid.el (gamegrid-setup-default-font): Clamp
+       font size at eight.
+
+       Allow opening more files in emacsclient on Android
+       * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity)
+       (checkReadableOrCopy): New function.
+       (onCreate): If the file specified is not readable from C, read
+       it into a temporary file and ask Emacs to open that.
+
+       Implement `fullscreen' on Android 4.0 and later
+       * doc/emacs/android.texi (Android Windowing): Document what new
+       frame parameters are now supported.
+       * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): New
+       field `isFullscreen'.
+       (detachWindow, attachWindow): Sync fullscreen state.
+       (onWindowFocusChanged): Add more logging.
+       (onResume): Restore previous fullscreen state.
+       (syncFullscreen): New function.
+       * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow)
+       (setFullscreen): New function.
+       * src/android.c (struct android_emacs_window): Add new method.
+       (android_init_emacs_window): Look up new method.
+       (android_set_fullscreen): New function.
+       * src/androidgui.h:
+       * src/androidterm.c (android_fullscreen_hook): Implement
+       accordingly.
+
+       Fix crashes in desktop-save-mode
+       * lisp/subr.el (overriding-text-conversion-style, y-or-n-p):
+       Disable text conversion when reading from minibuffer.
+       * src/androidfns.c (android_make_monitor_attribute_list): New
+       function.
+       (Fandroid_display_monitor_attributes_list): Call it to set
+       monitor_frames, which avoids a NULL pointer dereference.
+       Reported by Angelo Graziosi <angelo.g0@libero.it>.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-02-18  Po Lu  <luangruo@yahoo.com>
+
+       * lisp/loadup.el: Fix merge typos.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * doc/emacs/input.texi (On-Screen Keyboards): Document
+       `touch-screen-always-display'.
+       * doc/lispref/commands.texi (Misc Events): Improve documentation
+       of text conversion events.
+       * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog, display1):
+       Reorder buttons to make more sense.
+       * lisp/elec-pair.el (electric-pair-analyze-conversion): New
+       function.
+       * lisp/simple.el (analyze-text-conversion): Improve integration
+       with electric pair modes.
+       * lisp/term.el (term-mode): Always display the onscreen
+       keyboard.
+       * lisp/touch-screen.el (touch-screen-display-keyboard)
+       (touch-screen-handle-point-up): Respect new options.
+       * src/textconv.c (really_set_composing_text): Stop widenining
+       unnecessarily.
+       (really_delete_surrounding_text): Really delete surrounding
+       text.  Give text conversion analyzers the buffer text.
+       (syms_of_textconv): Update doc string.
+
+       Notify input methods when editing fails
+       * INSTALL.android: Clarify build instructions.
+       * src/textconv.c (struct complete_edit_check_context): New
+       structure.
+       (complete_edit_check): New function.
+       (handle_pending_conversion_events_1): If the window is known,
+       then ensure that any editing failures are reported to the input
+       method.
+
+       * configure.ac: Fix typo.
+
+       Update Android port
+       * configure.ac: Check for madvise.
+       * lisp/international/fontset.el (script-representative-chars):
+       Improve detection of CJK fonts.
+       * src/pdumper.c (dump_discard_mem): Use madvise if possible.
+       * src/sfnt.c (sfnt_map_glyf_table, sfnt_unmap_glyf_table): New
+       functions.
+       * src/sfnt.h (struct sfnt_glyf_table): New field.
+       * src/sfntfont.c (struct sfnt_font_info, sfntfont_open)
+       (sfntfont_close, sfntfont_detect_sigbus): Allow mmapping fonts
+       if possible.
+       * src/sfntfont.h: Update prototypes.
+       * src/sysdep.c (handle_sigbus, init_sigbus, init_signals):
+       Initialize SIGBUS correctly.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-02-17  Po Lu  <luangruo@yahoo.com>
+
+       Work around race condition bug in Android 13's input manager
+       * src/androidterm.c (android_get_selection): Use ephemeral last
+       point.
+       * src/textconv.c (report_selected_window_change): Set
+       w->ephemeral_last_point to the window's point now.
+
+       Update emacsbug and version.el for the Android port
+       * java/Makefile.in (install_temp/assets/version): New generated
+       file.
+       * lisp/loadup.el: Set emacs versions appropriately prior to
+       dumping on Android.
+       * lisp/mail/emacsbug.el (emacs-build-description): Insert
+       Android build fingerprint.
+       * lisp/version.el (emacs-repository-version-android)
+       (emacs-repository-get-version, emacs-repository-get-branch):
+       Implement for Android.
+       * src/androidterm.c (android_set_build_fingerprint): New
+       function.
+       (syms_of_androidterm): New variable `android-build-fingerprint'.
+
+       * src/android.c (android_exception_check): Fix typo.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Improve logging of Java exceptions
+       * src/android.c (android_exception_check): Print more detailed
+       information.
+
+       Fix crash on old versions of Android
+       * java/org/gnu/emacs/EmacsService.java (nameKeysym): Implement
+       stub on Android 3.0 and earlier.
+
+       Fix build and running on Android 2.2
+       * INSTALL.android: Document that Android 2.2 is now supported,
+       with caveats.
+       * configure.ac (ANDROID_MIN_SDK, ANDROID_SDK_18_OR_EARLIER)
+       (SYSTEM_TYPE, ANDROID_STUBIFY, SIZEOF_LONG): Correctly detect
+       things missing on Android 2.2.
+       * java/Makefile.in (ANDROID_JAR, JARSIGNER_FLAGS):
+       * java/debug.sh (jdb, gdbserver, line):
+       * java/org/gnu/emacs/EmacsApplication.java (findDumpFile):
+       * java/org/gnu/emacs/EmacsService.java (onCreate):
+       * java/org/gnu/emacs/EmacsThread.java (EmacsThread, run): Run
+       parameter initialization on main thread.
+       * src/android-asset.h (struct android_asset_manager)
+       (struct android_asset, AAssetManager_fromJava, AAssetManager_open)
+       (AAsset_close, android_asset_create_stream)
+       (android_asset_read_internal, AAsset_openFileDescriptor)
+       (AAsset_getLength, AAsset_getBuffer, AAsset_read): New file.
+       * src/android.c (android_user_full_name, android_hack_asset_fd)
+       (android_check_compressed_file): Implement for Android 2.2.
+       * src/process.c (Fprocess_send_eof): Don't call tcdrain if
+       unavailable.
+       * src/sfntfont-android.c (system_font_directories): Fix compiler
+       warning.
+       * src/sfntfont.c (sfntfont_read_cmap): Correctly test rc of
+       emacs_open.
+       * src/textconv.c (handle_pending_conversion_events_1): Mark
+       buffer UNINIT.
+
+2023-02-16  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-02-16  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * doc/emacs/android.texi (Android Fonts):
+       * doc/emacs/input.texi (On-Screen Keyboards):
+       * doc/lispref/commands.texi (Misc Events): Update documentation.
+       * java/org/gnu/emacs/EmacsInputConnection.java (setSelection):
+       New function.
+       * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView)
+       (reconfigureFrontBuffer): Make bitmap references weak
+       references.
+       * java/org/gnu/emacs/EmacsView.java (handleDirtyBitmap): Don't
+       clear surfaceView bitmap.
+       * lisp/comint.el (comint-mode):
+       * lisp/international/fontset.el (script-representative-chars)
+       (setup-default-fontset): Improve detection of CJK fonts.
+       * lisp/isearch.el (set-text-conversion-style): New variable.
+       (isearch-mode, isearch-done): Save and restore the text
+       conversion style.
+       * lisp/minibuffer.el (minibuffer-mode): Set an appropriate text
+       conversion style.
+       * lisp/simple.el (analyze-text-conversion): Run
+       post-self-insert-hook properly.
+       * lisp/subr.el (read-char-from-minibuffer): Disable text
+       conversion when reading character.
+       * src/androidterm.c (show_back_buffer): Don't check that F is
+       not garbaged.
+       (android_update_selection, android_reset_conversion): Use the
+       ephemeral last point and handle text conversion being disabled.
+       * src/buffer.c (syms_of_buffer): Convert old style DEFVAR.
+       * src/keyboard.c (kbd_buffer_get_event): Handle text conversion
+       first.
+       * src/lisp.h: Update prototypes.
+       * src/lread.c (read_filtered_event): Temporarily disable text
+       conversion.
+       * src/sfnt.c (sfnt_decompose_glyph_1, sfnt_decompose_glyph_2):
+       New functions.
+       (sfnt_decompose_glyph, sfnt_decompose_instructed_outline):
+       Refactor contour decomposition to those two functions.
+       (main): Update tests.
+       * src/sfntfont-android.c (system_font_directories): Add empty
+       field.
+       (Fandroid_enumerate_fonts, init_sfntfont_android): Enumerate
+       fonts in a user fonts directory.
+       * src/sfntfont.c (struct sfnt_font_desc): New field
+       `num_glyphs'.
+       (sfnt_enum_font_1): Set num_glyphs and avoid duplicate fonts.
+       (sfntfont_glyph_valid): New function.
+       (sfntfont_lookup_char, sfntfont_list_1): Make sure glyphs found
+       are valid.
+
+       * src/textconv.c (sync_overlay, really_commit_text)
+       (really_set_composing_text, really_set_composing_region)
+       (really_delete_surrounding_text, really_set_point_and_mark)
+       (handle_pending_conversion_events_1)
+       (handle_pending_conversion_events, conversion_disabled_p)
+       (disable_text_conversion, resume_text_conversion)
+       (Fset_text_conversion_style, syms_of_textconv): Update to
+       respect new options.
+       * src/textconv.h:
+       * src/window.h (GCALIGNED_STRUCT): New field
+       `ephemeral_last_point'.
+       * src/xdisp.c (mark_window_display_accurate_1): Set it.
+
+2023-02-15  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * doc/emacs/input.texi (On-Screen Keyboards):
+       * doc/lispref/commands.texi (Misc Events): Improve documentation
+       of text conversion stuff.
+       * java/org/gnu/emacs/EmacsInputConnection.java (beginBatchEdit)
+       (endBatchEdit, commitCompletion, commitText, deleteSurroundingText)
+       (finishComposingText, getSelectedText, getTextAfterCursor)
+       (EmacsInputConnection, setComposingRegion, performEditorAction)
+       (getExtractedText): Condition debug code on DEBUG_IC.
+       * java/org/gnu/emacs/EmacsService.java (EmacsService, updateIC):
+       Likewise.
+       * lisp/bindings.el (global-map):
+       * lisp/electric.el (global-map): Make `text-conversion'
+       `analyze-text-conversion'.
+       * lisp/progmodes/prog-mode.el (prog-mode): Enable text
+       conversion in input methods.
+       * lisp/simple.el (analyze-text-conversion): New function.
+       * lisp/textmodes/text-mode.el (text-conversion-style)
+       (text-mode): Likewise.
+       * src/androidterm.c (android_handle_ime_event): Handle
+       set_point_and_mark.
+       (android_sync_edit): Give Emacs 100 ms instead.
+       (android_perform_conversion_query): Skip the active region, not
+       the conversion region.
+       (getSelectedText): Implement properly.
+       (android_update_selection): Expose mark to input methods.
+       (android_reset_conversion): Handle `text-conversion-style'.
+       * src/buffer.c (init_buffer_once, syms_of_buffer): Add buffer
+       local variable `text-conversion-style'.
+       * src/buffer.h (struct buffer, bset_text_conversion_style): New
+       fields.
+       * src/emacs.c (android_emacs_init): Call syms_of_textconv.
+       * src/frame.h (enum text_conversion_operation): Rename
+       TEXTCONV_SET_POINT.
+       * src/lisp.h: Export syms_of_textconv.
+
+       * src/marker.c (set_marker_internal): Force redisplay when the
+       mark is set and the buffer is visible on builds that use text
+       conversion.  Explain why.
+
+       * src/textconv.c (copy_buffer): Fix copying past gap.
+       (get_mark): New function.
+       (textconv_query): Implement new flag.
+       (sync_overlay): New function.  Display conversion text in an
+       overlay.
+       (record_buffer_change, really_commit_text)
+       (really_set_composing_text, really_set_composing_region)
+       (really_delete_surrounding_text, really_set_point)
+       (handle_pending_conversion_events_1, decrement_inside)
+       (handle_pending_conversion_events, textconv_set_point)
+       (get_extracted_text, register_textconv_interface): Various fixes
+       and improvements.
+
+       * src/textconv.h (struct textconv_interface): Update
+       documentation.
+       * src/window.h (GCALIGNED_STRUCT): New field `prev_mark'.
+       * src/xdisp.c (mark_window_display_accurate_1): Handle
+       prev_mark.
+
+2023-02-15  Po Lu  <luangruo@yahoo.com>
+
+       Make debug.sh detect adb running as root
+       * java/debug.sh: Run gdbserver directly if possible.
+
+       Fix small bugs
+       * src/androidterm.c (android_handle_ime_event): Pacify compiler
+       warnings.
+       * src/textconv.c (really_set_composing_text)
+       (handle_pending_conversion_events, get_extracted_text): Fix
+       reentrancy problems and uses of uninitialized values.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-02-15  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * configure.ac (HAVE_TEXT_CONVERSION): Define on Android.
+       * doc/emacs/input.texi (On-Screen Keyboards): Document ``text
+       conversion'' slightly.
+       * doc/lispref/commands.texi (Misc Events): Document new
+       `text-conversion' event.
+       * java/org/gnu/emacs/EmacsContextMenu.java (display): Use
+       `syncRunnable'.
+       * java/org/gnu/emacs/EmacsDialog.java (display): Likewise.
+       * java/org/gnu/emacs/EmacsEditable.java: Delete file.
+       * java/org/gnu/emacs/EmacsInputConnection.java
+       (EmacsInputConnection): Reimplement from scratch.
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Add new
+       functions.
+       * java/org/gnu/emacs/EmacsService.java (EmacsService, getEmacsView)
+       (getLocationOnScreen, sync, getClipboardManager, restartEmacs):
+       Use syncRunnable.
+       (syncRunnable): New function.
+       (updateIC, resetIC): New functions.
+
+       * java/org/gnu/emacs/EmacsView.java (EmacsView): New field
+       `inputConnection' and `icMode'.
+       (onCreateInputConnection): Update accordingly.
+       (setICMode, getICMode): New functions.
+
+       * lisp/bindings.el (global-map): Ignore text conversion events.
+       * src/alloc.c (mark_frame): Mark text conversion data.
+       * src/android.c (struct android_emacs_service): New fields
+       `update_ic' and `reset_ic'.
+       (event_serial): Export.
+       (android_query_sem): New function.
+       (android_init_events): Initialize new semaphore.
+       (android_write_event): Export.
+       (android_select): Check for UI thread code.
+       (setEmacsParams, android_init_emacs_service): Initialize new
+       methods.
+       (android_check_query, android_begin_query, android_end_query)
+       (android_run_in_emacs_thread):
+       (android_update_ic, android_reset_ic): New functions for
+       managing synchronous queries from one thread to another.
+
+       * src/android.h: Export new functions.
+       * src/androidgui.h (enum android_event_type): Add input method
+       events.
+       (enum android_ime_operation, struct android_ime_event)
+       (union android_event, enum android_ic_mode): New structs and
+       enums.
+
+       * src/androidterm.c (android_window_to_frame): Allow DPYINFO to
+       be NULL.
+       (android_decode_utf16, android_handle_ime_event)
+       (handle_one_android_event, android_sync_edit)
+       (android_copy_java_string, beginBatchEdit, endBatchEdit)
+       (commitCompletion, deleteSurroundingText, finishComposingText)
+       (getSelectedtext, getTextAfterCursor, getTextBeforeCursor)
+       (setComposingText, setComposingRegion, setSelection, getSelection)
+       (performEditorAction, getExtractedText): New functions.
+       (struct android_conversion_query_context):
+       (android_perform_conversion_query):
+       (android_text_to_string):
+       (struct android_get_selection_context):
+       (android_get_selection):
+       (struct android_get_extracted_text_context):
+       (android_get_extracted_text):
+       (struct android_extracted_text_request_class):
+       (struct android_extracted_text_class):
+       (android_update_selection):
+       (android_reset_conversion):
+       (android_set_point):
+       (android_compose_region_changed):
+       (android_notify_conversion):
+       (text_conversion_interface): New functions and structures.
+       (android_term_init): Initialize text conversion.
+
+       * src/coding.c (syms_of_coding): Define Qutf_16le on Android.
+       * src/frame.c (make_frame): Clear conversion data.
+       (delete_frame): Reset conversion state.
+
+       * src/frame.h (enum text_conversion_operation)
+       (struct text_conversion_action, struct text_conversion_state)
+       (GCALIGNED_STRUCT): Update structures.
+       * src/keyboard.c (read_char, readable_events, kbd_buffer_get_event)
+       (syms_of_keyboard): Handle text conversion events.
+       * src/lisp.h:
+       * src/process.c: Fix includes.
+
+       * src/textconv.c (enum textconv_batch_edit_flags, textconv_query)
+       (reset_frame_state, detect_conversion_events)
+       (restore_selected_window, really_commit_text)
+       (really_finish_composing_text, really_set_composing_text)
+       (really_set_composing_region, really_delete_surrounding_text)
+       (really_set_point, complete_edit)
+       (handle_pending_conversion_events_1)
+       (handle_pending_conversion_events, start_batch_edit)
+       (end_batch_edit, commit_text, finish_composing_text)
+       (set_composing_text, set_composing_region, textconv_set_point)
+       (delete_surrounding_text, get_extracted_text)
+       (report_selected_window_change, report_point_change)
+       (register_texconv_interface): New functions.
+
+       * src/textconv.h (struct textconv_interface)
+       (TEXTCONV_SKIP_CONVERSION_REGION): Update prototype.
+       * src/xdisp.c (mark_window_display_accurate_1):
+       * src/xfns.c (xic_string_conversion_callback):
+       * src/xterm.c (init_xterm): Adjust accordingly.
+
+2023-02-12  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * doc/emacs/android.texi (Android Environment): Document
+       notifications permission.
+       * java/org/gnu/emacs/EmacsEditable.java (EmacsEditable):
+       * java/org/gnu/emacs/EmacsInputConnection.java
+       (EmacsInputConnection): New files.
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Load
+       library dependencies in a less verbose fashion.
+       * java/org/gnu/emacs/EmacsView.java (EmacsView): Make imManager
+       public.
+       (onCreateInputConnection): Set InputType to TYPE_NULL for now.
+       * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, onKeyDown)
+       (onKeyUp, getEventUnicodeChar): Correctly handle key events with
+       strings.
+       * lisp/term/android-win.el (android-clear-preedit-text)
+       (android-preedit-text): New special event handlers.
+       * src/android.c (struct android_emacs_window): Add function
+       lookup_string.
+       (android_init_emacs_window): Adjust accordingly.
+       (android_wc_lookup_string): New function.
+       * src/androidgui.h (struct android_key_event): Improve
+       commentary.
+       (enum android_lookup_status): New enum.
+       * src/androidterm.c (handle_one_android_event): Synchronize IM
+       lookup code with X.
+       * src/coding.c (from_unicode_buffer): Implement on Android.
+       * src/coding.h:
+       * src/sfnt.c: Fix commentary.
+
+2023-02-11  Po Lu  <luangruo@yahoo.com>
+
+       Fix displaying popup menus from a menu entry on Android
+       * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity, onDestroy)
+       (onWindowFocusChanged): Keep track of the last focused activity.
+       * java/org/gnu/emacs/EmacsDialog.java (display1): Use it if
+       there is no current focus.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-02-10  Po Lu  <luangruo@yahoo.com>
+
+       Improve appearance of the Android preferences screen
+       * .gitignore: Add org/gnu/emacs/R.java.
+       * cross/Makefile.in (top_builddir): Include verbose.mk.  Rewrite
+       rules to print nice looking statements.
+       * doc/emacs/android.texi (Android, Android Startup)
+       (Android Environment, Android Windowing, Android Fonts):
+       * doc/emacs/emacs.texi (Top): Add an extra ``Android
+       Troubleshooting'' node and move troubleshooting details there.
+       * java/Makefile.in: Generate R.java; improve appearance by using
+       verbose.mk.
+
+       * java/org/gnu/emacs/EmacsPreferencesActivity.java: Reimplement
+       in terms of PreferencesActivity.
+       * java/org/gnu/emacs/EmacsView.java (handleDirtyBitmap): Avoid
+       flicker.
+       * java/res/xml/preferences.xml: New file.
+       * src/verbose.mk.in (AM_V_AAPT, AM_V_SILENT): New variables.
+
+2023-02-10  Po Lu  <luangruo@yahoo.com>
+
+       Implement more features for the Emacs ``documents provider''
+       * java/org/gnu/emacs/EmacsDocumentsProvider.java (queryRoots):
+       Implement isChild.
+       (getNotificationUri, notifyChange): New functions.
+       (queryDocument1): Set rename and remove flags.
+       (queryDocument, queryChildDocuments): Allow the requester to
+       detect changes in the directory hierarchy.
+       (createDocument, deleteDocument, removeDocument): Signal changes
+       to the directory hierarchy.
+
+       * java/org/gnu/emacs/EmacsCopyArea.java (perform): Fix typo.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Fix buffer swapping on Android 7.1 and earlier
+       * java/org/gnu/emacs/EmacsSurfaceView.java
+       (reconfigureFrontBuffer): Don't use function only present on
+       Android 8.0 and later.
+
+       Update Android port
+       * doc/emacs/android.texi (Android Windowing): Remove yet another
+       limitation.
+       * java/debug.sh: Make this work on systems which prohibit
+       attaching to app processes from adbd.
+       * java/org/gnu/emacs/EmacsCopyArea.java (perform): Avoid
+       creating copies whenever possible.
+       * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView):
+       Remove SurfaceView based implementation and use manual double
+       buffering with invalidate instead.
+       * java/org/gnu/emacs/EmacsView.java (EmacsView, handleDirtyBitmap)
+       (raise, lower, onDetachedFromWindow): Adjust accordingly.
+       * java/org/gnu/emacs/EmacsWindow.java (windowUpdated): Remove
+       function.
+       * src/sfntfont.c (sfntfont_open): Set font->max_width correctly.
+
+2023-02-10  Po Lu  <luangruo@yahoo.com>
+
+       Fix IUP for contours which start past end
+       Found with Droid Sans Mono hinted with ttfautohint 1.8.4.
+
+       * src/sfnt.c (IUP_SINGLE_PAIR): If i is initially more than end,
+       make it start.
+       (sfnt_verbose): Handle cases where interpreter->glyph_zone is
+       NULL.
+       (main): Update tests.
+
+2023-02-10  Po Lu  <luangruo@yahoo.com>
+
+       Fix typo
+       * src/sfnt.c (sfnt_read_cmap_table): Fix typo.
+       (main): Update tests.
+
+2023-02-09  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Allow other text editors to edit files in Emacs' home directory
+       * java/AndroidManifest.xml.in: Declare the new documents
+       provider.
+       * java/README: Describe the meaning of files in res/values.
+       * java/org/gnu/emacs/EmacsDocumentsProvider.java
+       (EmacsDocumentsProvider): New file.
+       * java/res/values-v19/bool.xml:
+       * java/res/values/bool.xml: New files.
+
+       * src/sfnt.c (main): Update tests.
+
+2023-02-09  Po Lu  <luangruo@yahoo.com>
+
+       Implement instructing compound glyphs
+       * src/sfnt.c (sfnt_read_simple_glyph, sfnt_read_compound_glyph)
+       (sfnt_read_glyph): Take size_t offsets.
+       (struct sfnt_compound_glyph_context)
+       (sfnt_expand_compound_glyph_context)
+       (sfnt_decompose_compound_glyph): Take size_t contour offsets.
+       (sfnt_decompose_glyph): Always close contour even if the first
+       point isn't on-curve.
+       (sfnt_build_outline_edges): Fix coding style.
+       (sfnt_interpret_iup): Skip phantom points during IUP.
+       (sfnt_decompose_instructed_outline): Clarify documentation.
+       Always close contour even if the first point isn't on-curve.
+       (struct sfnt_test_dcontext, sfnt_test_move_to, sfnt_test_line_to)
+       (sfnt_test_curve_to, sfnt_transform_f26dot6, sfnt_test_get_glyph)
+       (sfnt_test_free_glyph, sfnt_test_span, sfnt_test_edge_ignore)
+       (sfnt_interpret_compound_glyph_2, sfnt_test_edges, main): Update
+       tests.
+
+       * src/sfnt.h: Export new function.
+
+       * src/sfntfont.c (sfntfont_get_glyph_outline): Handle compound
+       glyphs.
+       (sfntfont_measure_instructed_pcm, sfntfont_measure_pcm)
+       (sfntfont_draw): Update accordingly.
+
+2023-02-08  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * src/sfnt.c (SCFS): Fix order of arguments.
+       (sfnt_normalize_vector): Make sure vx and vy are within a
+       reasonable range.
+       (sfnt_move): Don't move when vectors are orthogonal.
+       (main): Update.
+
+       Update Android port
+       * doc/emacs/android.texi (Android Startup): Fix typos.
+       * src/sfnt.c (sfnt_interpret_msirp): Fix order in which operands
+       to MSIRP are popped.
+       (main): Reduce ppem values.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-02-08  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * doc/lispref/frames.texi (On-Screen Keyboards): Describe return
+       value of `frame-toggle-on-screen-keyboard'.
+       * java/org/gnu/emacs/EmacsSurfaceView.java (surfaceChanged)
+       (surfaceCreated, EmacsSurfaceView): Remove unuseful
+       synchronization code.  The framework doesn't seem to look at
+       this at all.
+
+       * java/org/gnu/emacs/EmacsView.java (EmacsView):
+       (onLayout): Lay out the window after children.
+       (swapBuffers): Properly implement `force'.
+       (windowUpdated): Delete function.
+
+       * lisp/frame.el (frame-toggle-on-screen-keyboard): Return
+       whether or not the on screen keyboard might've been displayed.
+
+       * lisp/minibuffer.el (minibuffer-on-screen-keyboard-timer):
+       (minibuffer-on-screen-keyboard-displayed):
+       (minibuffer-setup-on-screen-keyboard):
+       (minibuffer-exit-on-screen-keyboard): Improve OSK dismissal when
+       there are consecutive minibuffers.
+
+       * lisp/touch-screen.el (touch-screen-window-selection-changed):
+       New function.
+       (touch-screen-handle-point-up): Register it as a window
+       selection changed function.
+
+       * src/android.c (struct android_emacs_window)
+       (android_init_emacs_window): Remove references to
+       `windowUpdated'.
+       (android_window_updated): Delete function.
+       * src/android.h (struct android_output): Remove
+       `last_configure_serial'.
+       * src/androidterm.c (handle_one_android_event)
+       (android_frame_up_to_date):
+       * src/androidterm.h (struct android_output): Remove frame
+       synchronization, as that does not work on Android.
+
+2023-02-08  Po Lu  <luangruo@yahoo.com>
+
+       Fix graphics state when instructing glyphs
+       * src/sfntfont.c (sfntfont_get_glyph_outline): Take new argument
+       STATE and restore it prior to instructing the glyph.
+       (sfntfont_measure_instructed_pcm, sfntfont_measure_pcm)
+       (sfntfont_draw): Adjust accordingly.
+
+       Correctly round bearing values while computing pcm
+       * src/sfntfont.c (sfntfont_measure_instructed_pcm)
+       (sfntfont_measure_pcm): Ceil rbearing value.
+
+2023-02-08  Po Lu  <luangruo@yahoo.com>
+
+       Improve text display on Android port
+       * src/sfnt.c (sfnt_build_glyph_outline): Clear
+       build_outline_context.
+       (sfnt_poly_coverage): Extend coverage map.
+       (sfnt_prepare_raster): Always floor coordinates, since the
+       increase in coverage makes this hack unnecessary.
+       (sfnt_build_outline_edges): Likewise.
+       (sfnt_compare_edges): Remove function.
+       (sfnt_edge_sort): New function.  Since edges are already
+       partially sorted, and there are not many, insertion sort
+       suffices.
+       (sfnt_poly_edges): Use sfnt_edge_sort.
+       (sfnt_fill_span): Stop rounding x0 and x1 to the grid, and make
+       coverage computation static.
+       (sfnt_lookup_glyph_metrics): Fix return code for unscaled
+       metrics.
+       (sfnt_scale_metrics): New function.
+       (SFNT_ENABLE_HINTING): Remove define.
+       (struct sfnt_cvt_table, struct sfnt_fpgm_table)
+       (struct sfnt_prep_table): Move to sfnt.h.
+       (sfnt_read_cvt_table):
+       (sfnt_read_fpgm_table, sfnt_read_prep_table): Make TEST_STATIC.
+       (struct sfnt_unit_vector, struct sfnt_interpreter_definition)
+       (struct sfnt_interpreter_zone, struct sfnt_graphics_state):
+       (struct sfnt_interpreter): Move to sfnt.h.
+       (sfnt_make_interpreter): Make TEST_STATIC.
+       (POP, PUSH, DELTAP1, DELTAP2, DELTAP3): When TEST, define to
+       regular push and pop.
+       (sfnt_deltac):
+       (sfnt_deltap): Fix order of arguments.
+       (IUP_SINGLE_PAIR): Fix interpolation loop wraparound.
+       (sfnt_interpret_font_program):
+       (sfnt_interpret_control_value_program): Make TEST_STATIC.
+       (struct sfnt_instructed_outline): Move to sfnt.h.
+       (sfnt_build_instructed_outline): Make TEST_STATIC.
+       (sfnt_interpret_simple_glyph):
+       (sfnt_x_raster):
+       (sfnt_test_raster):
+       (all_tests):
+       (sfnt_verbose):
+       (main): Improve test code.
+
+       * src/sfnt.h (SFNT_ENABLE_HINTING, struct sfnt_cvt_table)
+       (struct sfnt_fpgm_table, struct sfnt_prep_table)
+       (struct sfnt_unit_vector, struct sfnt_interpreter_definition)
+       (struct sfnt_interpreter_zone, struct sfnt_graphics_state)
+       (struct sfnt_interpreter, struct sfnt_instructed_outline)
+       (PROTOTYPE): New definitions.
+       * src/sfntfont-android.c (sfntfont_android_put_glyphs): Make
+       coordinate generation more straightforward.
+       * src/sfntfont.c (sfntfont_get_glyph_outline): New arguments
+       INTERPRETER and METRICS.
+       (struct sfnt_font_info): New tables.
+       (sfntfont_setup_interpreter): New function.
+       (sfntfont_open): Avoid memory leak.  Set up interpreter.
+       (sfntfont_measure_instructed_pcm): New function.
+       (sfntfont_measure_pcm): Delegate to measure_instructed_pcm where
+       appropriate.
+       (sfntfont_close): Free new tables.
+       (sfntfont_draw): Scale metrics properly.
+
+2023-02-07  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Remove junk from instruction table
+       * src/sfnt.c (sfnt_name_instruction): Do so.
+
+       Remove bresenham stuff
+       * src/sfnt.c (sfnt_step_edge, sfnt_step_edge_n)
+       (sfnt_build_outline_edges, sfnt_test_edge, main):
+       * src/sfnt.h (struct sfnt_edge): Stop using error corrected line
+       drawing, as it's actually slower.
+
+2023-02-07  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * INSTALL.android: Describe patches for BoringSSL on ARM.
+
+       * src/sfnt.c (sfnt_build_glyph_outline): Remove redundant
+       multiplication.
+       (sfnt_prepare_raster): Update offset calculation for changes.
+       (sfnt_step_edge, sfnt_step_edge_n): Handle bresenham terms.
+       (sfnt_build_outline_edges): Don't subtract floored xmin, just
+       xmin.
+       (sfnt_saturate_short): Make clang generate better code.
+       (sfnt_fill_span): Stop rounding coordinates.
+       (sfnt_poly_span): Poly consecutive on transitions all in one go.
+       (sfnt_lookup_glyph_metrics): Remove redundant multiplication.
+       (struct sfnt_interpreter): New hooks for debugging.
+       (sfnt_large_integer_add): New function.
+       (sfnt_mul_f26dot6_fixed): Round product.
+       (sfnt_make_interpreter): Remove redundant multiplication.
+
+       (CHECK_STACK_ELEMENTS, POP_UNCHECKED, PUSH_UNCHECKED): New
+       macros.
+       (MOVE, POP, SWAP, CINDEX, RS, RCVT, LT, LTEQ, GT, GTEQ, EQ, NEQ)
+       (EVEN, AND, OR, NOT, ADD, SUB, DIV, MUL, ABS, NEG, FLOOR, CEILING)
+       (GETINFO, ROLL, _MAX, _MIN, ROUND, NROUND, GC, MD): Don't check
+       SP redundantly, especially when pushing an element right after
+       popping one.
+       (sfnt_move_glyph_zone): Don't touch points by passing NULL as
+       flags.
+       (sfnt_direct_move_zp2): Touch P in the directions of the
+       movement.
+       (sfnt_interpret_scfs): Fix coding style.
+       (sfnt_interpret_simple_glyph): Don't round Y coordinates.
+       (sfnt_test_span, sfnt_test_edges, sfnt_debug_edges, sfnt_test_edge)
+       (sfnt_x_raster, sfnt_test_raster, rcvt_test_args)
+       (deltac1_test_args, deltac2_test_args, deltac3_test_args)
+       (roll_1_test_args, sfnt_run_hook, sfnt_identify_instruction)
+       (sfnt_verbose, main): Improve debug code and tests.
+
+       * src/sfnt.h (struct sfnt_edge): Add bresenham terms.
+
+2023-02-06  Po Lu  <luangruo@yahoo.com>
+
+       Port emacsclient wrapper to Android 7.1 and earlier
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Load every
+       native library on which Emacs depends prior to loading libemacs
+       itself.
+
+       * java/org/gnu/emacs/EmacsOpenActivity.java (readEmacsClientLog)
+       (EmacsOpenActivity, startEmacsClient): Don't use redirectError
+       on Android 7.1 and earlier.
+
+2023-02-06  Po Lu  <luangruo@yahoo.com>
+
+       Adjust ndk-build implementation for old NDK versions
+       * configure.ac: Pass ANDROID_CFLAGS to ndk_INIT.
+       * cross/ndk-build/Makefile.in (NDK_BUILD_CFLAGS):
+       * cross/ndk-build/ndk-build-shared-library.mk
+       ($(call objname,$(LOCAL_MODULE),$(basename $(1)))):
+       ($$(error Unsupported suffix):
+       * cross/ndk-build/ndk-build-static-library.mk
+       ($(call objname,$(LOCAL_MODULE),$(basename $(1)))):
+       ($$(error Unsupported suffix): Use NDK_BUILD_CFLAGS.
+       * m4/ndk-build.m4 (ndk_INIT): Accept cflags.
+       (ndk_CONFIG_FILES): Export NDK_BUILD_CFLAGS.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-02-06  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/AndroidManifest.xml.in: Prevent the Emacs activity from
+       being overlayed by the emacsclient wrapper.
+       * java/org/gnu/emacs/EmacsOpenActivity.java (run): Likewise.
+       (onCreate): Set an appropriate theme on ICS and up.
+
+       * java/org/gnu/emacs/EmacsWindow.java (onTouchEvent): Handle
+       ACTION_CANCEL correctly.
+
+2023-02-06  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * src/sfnt.c (struct sfnt_build_glyph_outline_context)
+       (sfnt_build_glyph_outline, sfnt_fill_span): Improve glyph
+       appearance by rounding coordinate values.
+
+       (struct sfnt_interpreter): New fields `twilight_original_x',
+       `twilight_original_y'.
+       (sfnt_make_interpreter): Set new fields.
+       (DELTAP1, DELTAP2, DELTAP3, SVTCAy, SPVTL, SFVTL, MD): Implement
+       instructions.
+       (sfnt_save_projection_vector): New argument `dual_only'.  All
+       callers changed.
+       (sfnt_address_zp2, sfnt_address_zp1, sfnt_address_zp0): Obtain
+       original positions in the twilight zone as well.
+       (sfnt_check_zp1, sfnt_interpret_fliprgoff, sfnt_interpret_fliprgon)
+       (sfnt_interpret_flippt, sfnt_interpret_scfs, sfnt_interpret_miap)
+       (sfnt_interpret_alignrp, sfnt_line_to_vector, P)
+       (sfnt_interpret_msirp, sfnt_interpret_ip, sfnt_interpret_call)
+       (load_point, sfnt_interpret_iup_1, sfnt_interpret_iup)
+       (sfnt_interpret_run, struct sfnt_scaled_outline)
+       (struct sfnt_instructed_outline, sfnt_decompose_instructed_outline)
+       (sfnt_build_instructed_outline, sfnt_compute_phantom_points)
+       (sfnt_interpret_simple_glyph, all_tests, sfnt_setup_debugger)
+       (sfnt_name_instruction, sfnt_draw_debugger, sfnt_run_hook)
+       (sfnt_verbose, main): Make glyph instructing work.
+
+       * src/sfnt.h (SFNT_POLY_ROUND): New enumerator.
+
+2023-02-05  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * INSTALL.android: Explain how to build selinux.
+       * configure.ac: Enable selinux on Android.
+       * cross/ndk-build/ndk-build-shared-library.mk: ($(call
+       objname,$(LOCAL_MODULE),$(basename $(1))))::($$(error
+       Unsupported suffix)::(NDK_CFLAGS_$(LOCAL_MODULE)):
+       * cross/ndk-build/ndk-build-static-library.mk: ($(call
+       objname,$(LOCAL_MODULE),$(basename $(1))))::($$(error
+       Unsupported suffix)::(NDK_CFLAGS_$(LOCAL_MODULE)): Correctly
+       handle files with a .cc suffix, and clang-specific asflags.
+       * cross/ndk-build/ndk-clear-vars.mk: Handle AOSP extensions
+       LOCAL_ADDITIONAL_DEPENDENCIES,
+       LOCAL_CLANG_ASFLAGS_$(NDK_BUILD_ARCH) and LOCAL_IS_HOST_MODULE.
+
+       * doc/emacs/android.texi (Android Startup): Explain emacsclient
+       wrapper.
+
+       * java/org/gnu/emacs/EmacsView.java (EmacsView): New flag
+       `isCurrentlyTextEditor'.
+       (showOnScreenKeyboard, hideOnScreenKeyboard): Set as
+       appropriate.
+       (onCheckIsTextEditor): Return its value.
+
+       * lisp/touch-screen.el (touch-screen-handle-scroll): Don't ding
+       at buffer limits.
+       * m4/ndk-build.m4: Improve doc.
+
+       * src/Makefile.in (LIBSELINUX_CFLAGS): New variable.
+       (EMACS_CFLAGS): Add it.
+
+2023-02-05  Po Lu  <luangruo@yahoo.com>
+
+       Update from gnulib
+       * admin/merge-gnulib (avoided_flags):
+       * cross/lib/cdefs.h (__bos):
+       (__glibc_unsigned_or_positive):
+       (__glibc_unsafe_len):
+       (__glibc_fortify):
+       (__glibc_fortify_n):
+       * cross/lib/isnan.c:
+       * cross/lib/libc-config.h:
+       * cross/lib/openat-proc.c (openat_proc_name):
+       * cross/lib/vasnprintf.c (VASNPRINTF):
+       * cross/lib/verify.h (_Static_assert):
+       (_GL_SA3):
+       * lib/gnulib.mk.in (HAVE_GRANTPT):
+       (HAVE_SPAWN_H):
+       (NEXT_AS_FIRST_DIRECTIVE_LIMITS_H):
+       (NEXT_LIMITS_H):
+       (REPLACE_GETSUBOPT):
+       (REPLACE_ILOGB):
+       (SYSTEM_TYPE):
+       (BUILT_SOURCES):
+       * lib/isnan.c:
+       * lib/vasnprintf.c (VASNPRINTF):
+       * lib/verify.h (_GL_SA3):
+       * m4/gnulib-common.m4 (gl_COMMON_BODY):
+       * m4/gnulib-comp.m4 (gl_INIT): Update from gnulib.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * src/sfnt.c (struct sfnt_graphics_state):
+       (LOOPCALL):
+       (DELTAC3):
+       (PROJECT):
+       (SHPIX):
+       (sfnt_save_projection_vector):
+       (sfnt_check_zp0):
+       (sfnt_dual_project_vector):
+       (sfnt_interpret_scfs):
+       (sfnt_round_symmetric):
+       (sfnt_interpret_miap):
+       (sfnt_interpret_alignrp_1):
+       (sfnt_interpret_alignrp):
+       (sfnt_measure_distance):
+       (sfnt_interpret_msirp):
+       (sfnt_interpret_ip):
+       (sfnt_interpret_mdap):
+       (sfnt_deltap):
+       (sfnt_dual_project_onto_any_vector):
+       (sfnt_validate_gs):
+       (sfnt_set_projection_vector):
+       (sfnt_interpret_shp):
+       (sfnt_interpret_run):
+       (sfnt_check_sloop):
+       (main): Check in more WIP font code.
+
+2023-02-04  Po Lu  <luangruo@yahoo.com>
+
+       Add emacsclient desktop file equivalent on Android
+       * doc/emacs/android.texi (Android File System):
+       * java/AndroidManifest.xml.in: Update with new activity.  Remove
+       Android 10 restrictions through a special flag.
+
+       * java/org/gnu/emacs/EmacsNative.java (getProcName): New
+       function.
+       * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity):
+       New file.
+       * java/org/gnu/emacs/EmacsService.java (getLibraryDirection):
+       Remove unused annotation.
+       * lib-src/emacsclient.c (decode_options): Set alt_display on
+       Android.
+       * src/android.c (android_proc_name): New function.
+       (NATIVE_NAME): Export via JNI.
+
+2023-02-04  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Add additional permissions to Android port
+       * doc/emacs/android.texi (Android Environment):
+       * java/AndroidManifest.xml.in: Add network state permissions.
+
+       Update Android port
+       * src/sfnt.c (sfnt_multiply_divide_signed):
+       (struct sfnt_interpreter_zone):
+       (struct sfnt_graphics_state):
+       (struct sfnt_interpreter):
+       (sfnt_mul_f2dot14):
+       (sfnt_interpret_trap):
+       (WCVTF):
+       (ALIGNPTS):
+       (sfnt_scale_by_freedom_vector):
+       (sfnt_interpret_utp):
+       (sfnt_address_zp2):
+       (sfnt_address_zp1):
+       (sfnt_address_zp0):
+       (sfnt_check_zp2):
+       (sfnt_move_zp0):
+       (sfnt_move_zp1):
+       (sfnt_move_glyph_zone):
+       (sfnt_move_twilight_zone):
+       (sfnt_direct_move_zp2):
+       (sfnt_interpret_alignpts):
+       (sfnt_interpret_isect):
+       (sfnt_line_to_vector):
+       (sfnt_deltac):
+       (sfnt_interpret_mdap):
+       (sfnt_interpret_call):
+       (sfnt_dot_fix_14):
+       (sfnt_move_x):
+       (sfnt_move_y):
+       (sfnt_move):
+       (sfnt_validate_gs):
+       (sfnt_interpret_shz):
+       (sfnt_interpret_shc):
+       (sfnt_interpret_shp):
+       (sfnt_interpret_iup_1):
+       (sfnt_interpret_iup):
+       (sfnt_interpret_run):
+       (sfnt_interpret_font_program):
+       (sfnt_interpret_control_value_program):
+       (sfnt_interpret_simple_glyph):
+       (jrot_test_args):
+       (jrof_test_args):
+       (all_tests):
+       (main): Check in more WIP code.
+
+2023-02-02  Po Lu  <luangruo@yahoo.com>
+
+       Add Emacs icon for Android package
+       * java/AndroidManifest.xml.in: Add new icon.
+       * java/Makefile.in (srcdir): New variable.
+       (JAVA_FILES, RESOURCE_FILES): Update variables.
+       (emacs.apk-in): Apply resources.
+       * java/README: Describe directory tree.
+
+       Add Emacs icon for Android
+       * java/res/drawable/emacs.png: New file.
+
+       Update Android port
+       * src/android.c (android_get_current_api_level): New function.
+       * src/android.h: Export it.
+       * src/sfntfont-android.c (init_sfntfont_android): Make device
+       API level detection always work.
+
+       Clean up compiler warnings
+       * src/sfnt.c (sfnt_multiply_divide_signed): Add MAYBE_UNUSED.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-02-02  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * src/sfnt.c (xmalloc, xrealloc): Improve behavior upon
+       allocation failures during test.
+       (sfnt_table_names): Add prep.
+       (sfnt_transform_coordinates): Allow applying offsets during
+       coordinate transform.
+       (sfnt_decompose_compound_glyph): Defer offset computation until
+       any component compound glyph is loaded, then apply it during the
+       transform process.
+
+       (sfnt_multiply_divide): Make available everywhere.  Implement on
+       64 bit systems.
+       (sfnt_multiply_divide_signed): New function.
+       (sfnt_mul_fixed): Fix division overflow.
+
+       (sfnt_curve_to_and_build_1, sfnt_build_glyph_outline): Remove
+       outdated comment.
+       (sfnt_build_outline_edges): Fix coding style.
+       (sfnt_lookup_glyph_metrics): Allow looking up metrics without
+       scaling.
+       (struct sfnt_cvt_table): Fix type of cvt values.
+       (struct sfnt_prep_table): New structure.
+       (sfnt_read_cvt_table): Read cvt values in terms of fwords, not
+       longs (as Apple's doc seems to say).
+       (sfnt_read_fpgm_table): Fix memory allocation for font program
+       table.
+       (sfnt_read_prep_table): New function.
+       (struct sfnt_interpreter_zone): New structure.
+       (struct sfnt_interpreter_graphics_state): New fields `project',
+       `move', `vector_dot_product'.  Rename to `sfnt_graphics_state'.
+       (struct sfnt_interpreter, sfnt_mul_f26dot6): Stop doing rounding
+       division.
+       (sfnt_init_graphics_state):
+       (sfnt_make_interpreter):
+       (MOVE):
+       (SSW):
+       (RAW):
+       (SDS):
+       (ADD):
+       (SUB):
+       (ABS):
+       (NEG):
+       (WCVTF):
+       (_MIN):
+       (S45ROUND):
+       (SVTCAx):
+       (sfnt_set_srounding_state):
+       (sfnt_skip_code):
+       (sfnt_interpret_unimplemented):
+       (sfnt_interpret_fdef):
+       (sfnt_interpret_idef):
+       (sfnt_interpret_if):
+       (sfnt_interpret_else):
+       (sfnt_round_none):
+       (sfnt_round_to_grid):
+       (sfnt_round_to_double_grid):
+       (sfnt_round_down_to_grid):
+       (sfnt_round_up_to_grid):
+       (sfnt_round_to_half_grid):
+       (sfnt_round_super):
+       (sfnt_validate_gs):
+       (sfnt_interpret_run):
+       (sfnt_interpret_font_program):
+       (struct sfnt_test_dcontext):
+       (sfnt_test_move_to):
+       (sfnt_test_line_to):
+       (sfnt_test_curve_to):
+       (sfnt_test_get_glyph):
+       (sfnt_test_free_glyph):
+       (sfnt_test_span):
+       (sfnt_test_edge_ignore):
+       (sfnt_test_edge):
+       (sfnt_test_raster):
+       (test_interpreter_profile):
+       (test_cvt_values):
+       (test_interpreter_cvt):
+       (test_interpreter_head):
+       (sfnt_make_test_interpreter):
+       (struct sfnt_interpreter_test):
+       (sfnt_run_interpreter_test):
+       (struct sfnt_generic_test_args):
+       (sfnt_generic_check):
+       (sfnt_check_srp0):
+       (sfnt_check_szp0):
+       (sfnt_check_sloop):
+       (struct sfnt_rounding_test_args):
+       (sfnt_check_rounding):
+       (sfnt_check_smd):
+       (sfnt_check_scvtci):
+       (sfnt_check_sswci):
+       (sfnt_check_ssw):
+       (sfnt_check_flipon):
+       (sfnt_check_flipoff):
+       (npushb_test_args):
+       (npushw_test_args):
+       (pushb_test_args):
+       (pushw_test_args):
+       (stack_overflow_test_args):
+       (stack_underflow_test_args):
+       (rtg_test_args):
+       (rtg_symmetric_test_args):
+       (rtg_1_test_args):
+       (rtg_1_symmetric_test_args):
+       (rthg_test_args):
+       (rthg_1_test_args):
+       (rtdg_test_args):
+       (rtdg_1_test_args):
+       (rtdg_2_test_args):
+       (rtdg_3_test_args):
+       (else_test_args):
+       (jmpr_test_args):
+       (dup_test_args):
+       (pop_test_args):
+       (clear_test_args):
+       (swap_test_args):
+       (depth_test_args):
+       (cindex_test_args):
+       (mindex_test_args):
+       (raw_test_args):
+       (loopcall_test_args):
+       (call_test_args):
+       (fdef_test_args):
+       (fdef_1_test_args):
+       (endf_test_args):
+       (ws_test_args):
+       (rs_test_args):
+       (wcvtp_test_args):
+       (rcvt_test_args):
+       (mppem_test_args):
+       (mps_test_args):
+       (debug_test_args):
+       (lt_test_args):
+       (all_tests):
+       (main): Implement more instructions.
+
+       * src/sfnt.h (enum sfnt_table, struct sfnt_glyph_metrics): Add
+       new tables.  Add comment.
+
+2023-01-30  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-01-30  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * cross/ndk-build/ndk-build-shared-library.mk: ($(call
+       objname,$(LOCAL_MODULE),$(basename $(1)))):
+       * cross/ndk-build/ndk-build-static-library.mk: ($(call
+       objname,$(LOCAL_MODULE),$(basename $(1)))): Revert broken typo
+       fixes.
+       * src/sfnt.c (TEST_STATIC): Define ARRAYELTS.
+       (sfnt_table_names): New CVT and FPGM tables.
+       (sfnt_decompose_compound_glyph, sfnt_decompose_glyph)
+       (struct sfnt_large_integer, sfnt_multiply_divide_1)
+       (sfnt_count_leading_zero_bits, sfnt_multiply_divide_2)
+       (sfnt_multiply_divide, sfnt_mul_fixed, sfnt_div_fixed)
+       (sfnt_ceil_fixed, sfnt_build_glyph_outline): Fix fixed point
+       multiplication routines on systems without 64 bit long long
+       type.
+       (SFNT_ENABLE_HINTING, struct sfnt_test_dcontext, sfnt_test_move_to)
+       (sfnt_test_line_to, sfnt_test_curve_to, sfnt_test_get_glyph)
+       (sfnt_test_free_glyph, sfnt_test_span, sfnt_test_edge_ignore)
+       (sfnt_read_cvt_table, sfnt_test_edge, sfnt_test_raster)
+       (sfnt_read_fpgm_table, struct sfnt_unit_vector)
+       (struct sfnt_interpreter_definition)
+       (struct sfnt_interpreter_graphics_state, struct sfnt_interpreter)
+       (sfnt_div_f26dot6, sfnt_mul_f26dot6, sfnt_floor_f26dot6)
+       (sfnt_ceil_f26dot6, sfnt_round_f26dot6, sfnt_init_graphics_state)
+       (sfnt_make_interpreter, enum sfnt_interpreter_run_context)
+       (sfnt_interpret_trap, STACKSIZE, sfnt_set_srounding_state)
+       (sfnt_skip_code, sfnt_interpret_unimplemented, sfnt_interpret_fdef)
+       (sfnt_interpret_idef, sfnt_interpret_if, sfnt_interpret_else)
+       (sfnt_round_none, sfnt_round_to_grid, sfnt_round_to_double_grid)
+       (sfnt_round_down_to_grid, sfnt_round_up_to_grid)
+       (sfnt_round_to_half_grid, sfnt_round_super, sfnt_validate_gs)
+       (sfnt_interpret_run, sfnt_interpret_font_program)
+       (test_interpreter_profile, test_cvt_values, test_interpreter_cvt)
+       (test_interpreter_head, sfnt_make_test_interpreter)
+       (struct sfnt_interpreter_test, sfnt_run_interpreter_test)
+       (struct sfnt_generic_test_args, sfnt_generic_check)
+       (sfnt_check_srp0, sfnt_check_szp0, sfnt_check_sloop)
+       (struct sfnt_rounding_test_args, sfnt_check_rounding)
+       (sfnt_check_smd, sfnt_check_scvtci, sfnt_check_sswci)
+       (sfnt_check_ssw, sfnt_check_flipon, sfnt_check_flipoff)
+       (npushb_test_args, npushw_test_args, pushb_test_args)
+       (pushw_test_args, stack_overflow_test_args)
+       (stack_underflow_test_args, rtg_test_args, rtg_symmetric_test_args)
+       (rtg_1_test_args, rtg_1_symmetric_test_args, rthg_test_args)
+       (rthg_1_test_args, rtdg_test_args, rtdg_1_test_args)
+       (rtdg_2_test_args, rtdg_3_test_args, else_test_args)
+       (jmpr_test_args, dup_test_args, pop_test_args, clear_test_args)
+       (swap_test_args, depth_test_args, cindex_test_args)
+       (mindex_test_args, raw_test_args, loopcall_test_args)
+       (call_test_args, fdef_test_args, fdef_1_test_args, endf_test_args)
+       (ws_test_args, rs_test_args, wcvtp_test_args, rcvt_test_args)
+       (mppem_test_args, mps_test_args, debug_test_args, lt_test_args)
+       (all_tests, main): Check in WIP hinting code.
+
+       * src/sfnt.h (enum sfnt_table): Add `cvt ' and `fpgm' tables.
+
+2023-01-29  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Check in missing files
+       * .gitignore:
+       * cross/lib/_Noreturn.h (_Noreturn): Add missing gnulib files.
+
+       Update Android port
+       * INSTALL.android (module_target): Clarify documentation.
+       * cross/ndk-build/ndk-build-shared-library.mk:
+       * cross/ndk-build/ndk-build-static-library.mk: Fix building Neon 
objects.
+       * java/AndroidManifest.xml.in: Add a version code.
+
+2023-01-28  Po Lu  <luangruo@yahoo.com>
+
+       Implement `restart-emacs' on Android
+       * java/org/gnu/emacs/EmacsService.java (restartEmacs): New
+       function.
+       * src/android.c (struct android_emacs_service)
+       (android_init_emacs_service): Add new method.
+       (android_restart_emacs): New function.
+       * src/android.h: Update prototypes.
+       * src/emacs.c (Fkill_emacs): Call android_restart_emacs whenever
+       appropriate.
+
+2023-01-28  Po Lu  <luangruo@yahoo.com>
+
+       Add libtiff support to Android port
+       * INSTALL.android: Document how to build with libtiff.
+
+       * build-aux/ndk-build-helper-1.mk (NDK_SO_NAME):
+       * build-aux/ndk-build-helper-2.mk (NDK_A_NAME):
+       * build-aux/ndk-build-helper-4.mk: Decrease number of duplicate
+       dependencies found.
+       * configure.ac (ANDROID_SDK_18_OR_EARLIER, XCONFIGURE, PNG_CFLAGS)
+       (HAVE_TIFF): Allow using libtiff on Android.
+       * cross/ndk-build/ndk-clear-vars.mk: Undefine additional
+       variables.
+       * cross/ndk-build/ndk-resolve.mk: Split CFLAGS resolution from
+       a-name resolution, and do not recursively add archive or shared
+       object names for dependencies of shared libraries.
+
+       * src/Makefile.in (TIFF_CFLAGS): New variable.
+       (EMACS_CFLAGS): Use it.
+
+2023-01-28  Po Lu  <luangruo@yahoo.com>
+
+       * src/image.c (syms_of_image): Fix typo.
+
+2023-01-28  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * doc/emacs/android.texi (Android File System): Describe an
+       easier way to disable scoped storage.
+       * java/AndroidManifest.xml.in: Add new permission to allow that.
+       * java/README: Add more text describing Java.
+       * java/org/gnu/emacs/EmacsContextMenu.java (Item): New fields
+       `isCheckable' and `isChecked'.
+       (EmacsContextMenu, addItem): New arguments.
+       (inflateMenuItems): Set checked status as appropriate.
+
+       * java/org/gnu/emacs/EmacsCopyArea.java (perform): Disallow
+       operations where width and height are less than or equal to
+       zero.
+       * lisp/menu-bar.el (menu-bar-edit-menu): Make
+       execute-extended-command available as a menu item.
+       * src/androidmenu.c (android_init_emacs_context_menu)
+       (android_menu_show):
+       * src/menu.c (have_boxes): Implement menu check boxes.
+
+2023-01-28  Po Lu  <luangruo@yahoo.com>
+
+       Set up fontset stuff on Android
+       * lisp/term/android-win.el (window-system-initialization):
+       Create default fontset.
+
+       Fix file descriptor leaks
+       * src/sfntfont.c (sfntfont_read_cmap):
+       (sfntfont_open): Fix leaks of file descriptors.
+
+       Update from gnulib
+       * cross/lib/stdalign.in.h (_GL_STDALIGN_H):
+       (_):
+       (__alignof_is_defined):
+       * cross/lib/vasnprintf.c:
+       * lib/gnulib.mk.in (ANDROID_MIN_SDK):
+       (HAVE_SPAWN_H):
+       (LIBGCCJIT_LIBS):
+       (NATIVE_COMPILATION_AOT):
+       (NEXT_AS_FIRST_DIRECTIVE_LIMITS_H):
+       (NEXT_LIMITS_H):
+       (SIZEOF_LONG):
+       (stdalign.h):
+       * ../../../../dev/null:
+       * lib/stdalign.in.h (_GL_STDALIGN_H):
+       (_):
+       (__alignof_is_defined):
+       * lib/vasnprintf.c:
+       * m4/gnulib-common.m4 (gl_COMMON_BODY):
+       * m4/stdalign.m4 (gl_ALIGNASOF):
+       * m4/stddef_h.m4: Update from gnulib.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-01-28  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * INSTALL.android: Document support for gnutls and libgmp.
+       * build-aux/ndk-build-helper-1.mk (NDK_SO_NAMES, NDK_INCLUDES)
+       (SYSTEM_LIBRARIES):
+       * build-aux/ndk-build-helper-2.mk: Recursively resolve and add
+       shared library dependencies; even those of static libraries.
+       * build-aux/ndk-module-extract.awk: Fix makefile_imports code.
+       * configure.ac (ANDROID_SDK_18_OR_EARLIER, XCONFIGURE)
+       (LIBGMP_CFLAGS): Enable GMP and gnutls on Android.
+
+       * cross/ndk-build/Makefile.in (LOCAL_EXPORT_C_INCLUDES):
+       * cross/ndk-build/ndk-build-shared-library.mk: ($(call
+       objname,$(LOCAL_MODULE),$(basename $(1))))::($$(error
+       Unsupported suffix)::($(LOCAL_MODULE_FILENAME)):
+       * cross/ndk-build/ndk-build-static-library.mk: ($(call
+       objname,$(LOCAL_MODULE),$(basename $(1))))::($$(error
+       Unsupported suffix):
+       * cross/ndk-build/ndk-clear-vars.mk:
+       * cross/ndk-build/ndk-resolve.mk (NDK_SYSTEM_LIBRARIES):
+       (NDK_LOCAL_EXPORT_C_INCLUDES_$(LOCAL_MODULE)):
+       (NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE)):
+       Implement ``LOCAL_ASM_RULE'' and ``LOCAL_C_ADDITIONAL_FLAGS''
+       extensions for libgmp.
+
+       * doc/emacs/input.texi (Touchscreens): Document how to
+       horizontally scroll.
+       * java/org/gnu/emacs/EmacsActivity.java (attachWindow): Give the
+       view focus again if necessary.
+       (onPause): Call right super function.
+       * java/org/gnu/emacs/EmacsPreferencesActivity.java (onClick):
+       Clear dumpFileName lest Emacs try to load a nonexistent dump
+       file.
+       * java/org/gnu/emacs/EmacsView.java (onDetachedFromWindow)
+       (onAttachedToWindow): Call super functions.
+       (onCreateInputConnection): Make sure the IME never obscures
+       Emacs.
+       * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, onKeyDown)
+       (onKeyUp): Improve tracking of quit keys.
+
+       * lisp/isearch.el (isearch-mode): Bring up the onscreen
+       keyboard.
+       * lisp/touch-screen.el (touch-screen-current-tool): Add three
+       fields.
+       (touch-screen-handle-scroll): Allow hscrolling as well.
+       (touch-screen-handle-touch): Add additional fields to
+       `touch-screen-current-tool'.
+       * src/Makefile.in (LIBGMP_CFLAGS, EMACS_CFLAGS): Add new
+       variable.
+       * src/android.c (android_run_select_thread):
+       (android_write_event): Use pthread_cond_broadcast because
+       pthread_cond_signal does nothing on some Android
+       versions/devices?
+
+2023-01-26  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       * doc/emacs/input.texi (On-Screen Keyboards): Fix typo.
+
+       Update Android port
+       * INSTALL.android: Describe that apksigner is also required.
+       * configure.ac: Correctly add cross/Makefile to
+       SUBDIR_MAKEFILES.
+       * cross/Makefile.in: (config.status): Depend on
+       $(top_srcdir)/config.status.
+       * doc/emacs/input.texi (On-Screen Keyboards): Document how to
+       quit without a physical keyboard.
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New
+       function `quit'.
+       * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): New field
+       `lastVolumeButtonPress'.
+       (onKeyDown): Quit if necessary.
+       * m4/ndk-build.m4 (ndk_where_cc): Fix search if CC is not a
+       single word.
+       * src/android.c (android_open): Remove unused variable.
+       (quit): New function.
+       * src/androidmenu.c (android_process_events_for_menu): Allow
+       quitting the menu.
+       * src/xterm.c (handle_one_xevent, x_term_init, syms_of_xterm):
+       Implement features described above, so they work on free
+       operating systems.
+       * src/xterm.h (struct x_display_info): New fields `quit_keysym',
+       `quit_keysym_time'.
+
+2023-01-26  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * INSTALL.android: Document how to install sqlite3.
+       * build-aux/ndk-build-helper-1.mk (SYSTEM_LIBRARIES):
+       * build-aux/ndk-build-helper-2.mk (SYSTEM_LIBRARIES): Add liblog
+       and libandroid.
+       * configure.ac (SQLITE3_LIBS, HAVE_SQLITE3)
+       (HAVE_SQLITE3_LOAD_EXTENSION): Support on Android.
+       (APKSIGNER): Look for this new required binary.
+
+       * cross/ndk-build/ndk-build-shared-library.mk (objname):
+       * cross/ndk-build/ndk-build-static-library.mk (objname): Avoid
+       duplicate rules by prefixing objects with module type.
+
+       * cross/ndk-build/ndk-build.mk.in (NDK_BUILD_SHARED): Fix
+       definition.
+       * cross/ndk-build/ndk-resolve.mk:
+       (NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE)): Handle new system
+       libraries.
+
+       * doc/emacs/android.texi (Android File System): Document Android
+       10 system restriction.
+
+       * java/AndroidManifest.xml.in: Target Android 33, not 28.
+       * java/Makefile.in (SIGN_EMACS_V2, APKSIGNER): New variables.
+       ($(APK_NAME)): Make sure to apply a ``version 2 signature'' to
+       the package as well.
+
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New
+       argument apiLevel.
+       * java/org/gnu/emacs/EmacsNoninteractive.java (main):
+       * java/org/gnu/emacs/EmacsThread.java (run): Pass API level.
+       * m4/ndk-build.m4 (ndk_package_mape): Add package mapping for
+       sqlite3.
+       * src/Makefile.in (SQLITE3_CFLAGS): New substition.
+       (EMACS_CFLAGS): Add that variable.
+
+       * src/android.c (android_api_level): New variable.
+       (initEmacs): Set it.
+       (android_file_access_p): Make static.
+       (android_hack_asset_fd): Adjust for restrictions in Android 29
+       and later.
+       (android_close_on_exec): New function.
+       (android_open): Adjust to not duplicate file descriptor even if
+       CLOEXEC.
+       (android_faccessat): Use fstatat at-func emulation.
+
+       * src/android.h: Update prototypes.
+       * src/dired.c (file_name_completion_dirp):
+       * src/fileio.c (file_access_p, Faccess_file): Now that
+       sys_faccessat takes care of everything, stop calling
+       android_file_access_p.
+
+2023-01-26  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * .gitignore: Ignore lib/math.h.
+       * INSTALL.android: Update accordingly.
+       * build-aux/ndk-build-helper-1.mk:
+       * build-aux/ndk-build-helper-2.mk:
+       * build-aux/ndk-build-helper.mk:
+       * build-aux/ndk-module-extract.awk: Handle C++ modules.
+       * configure.ac: Enable libxml2 on Android.
+
+       * cross/ndk-build/Makefile.in:
+       * cross/ndk-build/ndk-build-shared-library.mk:
+       * cross/ndk-build/ndk-build-static-library.mk:
+       * cross/ndk-build/ndk-build.mk.in:
+       * cross/ndk-build/ndk-resolve.mk: Fix dependency resolution of
+       includes.
+
+       * java/org/gnu/emacs/EmacsView.java (popupMenu): Fix minimum SDK
+       version for actual popup menus.
+       * lib/math.h: Delete file.
+
+       * m4/ndk-build.m4 (ndk_SEARCH_MODULE, ndk_CHECK_MODULES): Look
+       for nasm and C++ libraries.
+
+       * src/android.c (faccessat): Rename to `android_faccessat'.
+       * src/android.h: Update prototypes.
+       * src/dired.c (file_name_completion_dirp):
+       * src/fileio.c (file_access_p, Faccess_file, file_directory_p):
+       * src/lisp.h:
+       * src/lread.c (openp):
+       * src/process.c (allocate_pty): Use sys_faccessat.
+       * src/sysdep.c (sys_faccessat): New function.
+
+2023-01-26  Po Lu  <luangruo@yahoo.com>
+
+       Remove unused file
+       * cross/ndk-build/ndk-build.in: Delete unused file.
+
+2023-01-25  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/org/gnu/emacs/EmacsDrawLine.java: Fix this again.  Gosh,
+       how does Android do this.
+       * java/org/gnu/emacs/EmacsNoninteractive.java (main): Port to
+       Android 2.3.3.
+
+       * java/org/gnu/emacs/EmacsSdk11Clipboard.java
+       (EmacsSdk11Clipboard): Port to Android 4.0.3.
+       * java/org/gnu/emacs/EmacsService.java (getClipboardManager):
+       New function.
+
+       * src/alloc.c (find_string_data_in_pure): Fix Android alignment
+       issue.
+
+       * src/android-emacs.c (main): Port to Android 4.4.
+       * src/android.c (initEmacs): Align stack to 32 bytes, so it ends
+       up aligned to 16 even though gcc thinks the stack is already
+       aligned to 16 bytes.
+
+       * src/callproc.c (init_callproc): Use /system/bin/sh instead of
+       /bin/sh by default.
+
+2023-01-25  Po Lu  <luangruo@yahoo.com>
+
+       Remove extra header
+       * cross/lib/math.h: Delete header.
+
+       Minor fixes to Android port
+       * java/Makefile.in: (emacs.apk-in): Don't call cp with empty
+       args.
+       * java/org/gnu/emacs/EmacsDrawLine.java (perform): Fix for
+       PostScript filling semantics.
+       * src/Makefile.in (android-emacs): Build android-emacs directly.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-01-25  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * doc/emacs/android.texi (Android Startup, Android Environment):
+       Document that restrictions on starting Emacs have been lifted.
+
+       * java/README: Document Java for Emacs developers and how the
+       Android port works.
+
+       * java/org/gnu/emacs/EmacsApplication.java (EmacsApplication)
+       (findDumpFile): New function.
+       (onCreate): Factor out dump file finding functions to there.
+
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Update
+       function declarations.
+       * java/org/gnu/emacs/EmacsNoninteractive.java
+       (EmacsNoninteractive): New class.
+
+       * java/org/gnu/emacs/EmacsService.java (EmacsService, getApkFile)
+       (onCreate): Pass classpath to setEmacsParams.
+       * java/org/gnu/emacs/EmacsThread.java (EmacsThread): Make run an
+       override.
+       * lisp/loadup.el: Don't dump on Android when noninteractive.
+       * lisp/shell.el (shell--command-completion-data): Handle
+       inaccessible directories.
+       * src/Makefile.in (android-emacs): Link with gnulib.
+       * src/android-emacs.c (main): Implement to launch app-process
+       and then EmacsNoninteractive.
+       * src/android.c (setEmacsParams): New argument `class_path'.
+       Don't set stuff up when running noninteractive.
+       * src/android.h (initEmacs): Likewise.
+       * src/androidfont.c (init_androidfont):
+       * src/androidselect.c (init_androidselect): Don't initialize
+       when running noninteractive.
+       * src/emacs.c (load_pdump): New argument `dump_file'.
+       (android_emacs_init): Give new argument `dump_file' to
+       `load_pdump'.
+       * src/sfntfont-android.c (init_sfntfont_android): Don't
+       initialize when running noninteractive.
+
+2023-01-25  Po Lu  <luangruo@yahoo.com>
+
+       Import gnulib modules printf-posix and vasprintf-posix
+       These are neccessary because Android's printf is missing basic format
+       modifiers such as t.
+
+       * admin/merge-gnulib (GNULIB_MODULES): Add printf-posix and
+       vasprintf-posix.  Update from gnulib.
+       * configure.ac (CFLAGS): Add -DHAVE_CONFIG_H.
+
+2023-01-24  Po Lu  <luangruo@yahoo.com>
+
+       Make binaries distributed with Emacs work on Android
+       * doc/lispref/processes.texi (Subprocess Creation): Document
+       variables containing program names.
+       * etc/NEWS: Document new variables.
+       * java/Makefile.in (CROSS_BINS): Add missing etags binary.
+       * lisp/cedet/semantic/db-ebrowse.el
+       (semanticdb-create-ebrowse-database):
+       * lisp/gnus/mail-source.el (mail-source-movemail-program):
+       * lisp/hexl.el (hexl-program):
+       * lisp/htmlfontify.el (hfy-etags-bin):
+       * lisp/ielm.el (inferior-emacs-lisp-mode):
+       * lisp/mail/rmail.el (rmail-autodetect):
+       (rmail-insert-inbox-text):
+       * lisp/org/org-ctags.el (org-ctags-path-to-ctags):
+       * lisp/progmodes/cperl-mode.el (cperl-etags):
+       * lisp/speedbar.el (speedbar-fetch-etags-command):
+       * lisp/textmodes/reftex-global.el (reftex-create-tags-file): Use
+       new variables.
+       * src/callproc.c (syms_of_callproc): New variables naming
+       binaries redistributed with Emacs.
+
+2023-01-24  Po Lu  <luangruo@yahoo.com>
+
+       Enable libjpeg on Android
+       * INSTALL.android: Update documentation.
+       * build-aux/ndk-build-helper-1.mk: When building shared
+       libraries, do not link libemacs.so with dependent archive files.
+       * build-aux/ndk-build-helper-2.mk: Add whole archive
+       dependencies as well.
+
+       * configure.ac (HAVE_JPEG): Enable on Android.
+
+       * cross/ndk-build/ndk-build-shared-library.mk: Link the shared
+       object with archive file dependencies.
+       * cross/ndk-build/ndk-build-static-library.mk: Build all code
+       position-independently.
+       * cross/ndk-build/ndk-resolve.mk: Separately resolve a names of
+       archive and whole archive dependencies.
+
+       * src/Makefile.in (JPEG_CFLAGS): New variable.
+       (EMACS_CFLAGS): Add it.
+
+2023-01-24  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * INSTALL.android: Update.
+       * build-aux/ndk-build-helper-1.mk: Fix typo.
+       * configure.ac: Enable --with-json on Android.
+       * cross/ndk-build/ndk-build-shared-library.mk:
+       (NDK_CFLAGS_$(LOCAL_MODULE)):
+       (LOCAL_MODULE_FILENAME):
+       * cross/ndk-build/ndk-build-static-library.mk:
+       (ALL_OBJECT_FILES$(LOCAL_MODULE)):
+       (LOCAL_MODULE_FILENAME): Recursively resolve dependencies.
+       * cross/ndk-build/ndk-resolve.mk: New function.
+
+       * doc/emacs/android.texi (Android Startup): Document how Emacs
+       is dumped during initial startup.
+
+       * java/Makefile.in (filename): Fix build with multiple shared
+       libraries.
+       * java/README: Improve commentary.
+       * java/org/gnu/emacs/EmacsApplication.java (onCreate): Look and
+       set dump file.
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New
+       function getFingerprint.
+       * java/org/gnu/emacs/EmacsPreferencesActivity.java (onCreate):
+       Add option to erase the dump file.
+       * java/org/gnu/emacs/EmacsService.java (browseUrl): New
+       function.
+       * java/org/gnu/emacs/EmacsThread.java (run): Specify dump file
+       if found.
+       * lisp/loadup.el: Always dump during loadup on Android.
+
+       * lisp/net/browse-url.el (browse-url--browser-defcustom-type):
+       (browse-url-default-browser):
+       (browse-url-default-android-browser): New browse url type.
+
+       * m4/ndk-build.m4 (ndk_package_map): Map jansson to libjansson.
+       * src/android.c (struct android_emacs_service): New method
+       `browse_url'.
+       (getFingerprint): New function.
+       (android_init_emacs_service): Initialize new method.
+       (android_browse_url): New function.
+
+       * src/android.h: Update prototypes.
+
+       * src/androidselect.c (Fandroid_browse_url): New function.
+       (syms_of_androidselect): Define it.
+
+       * src/emacs.c (load_pdump): Don't look in fancy places on
+       Android.
+       * src/pdumper.c (Fdump_emacs_portable): Allow dumping while
+       interactive on Android.
+       (syms_of_pdumper): New variable `pdumper-fingerprint'.
+
+       * src/sfntfont-android.c (sfntfont_android_composite_bitmap):
+       Fix unused variables.
+
+2023-01-24  Po Lu  <luangruo@yahoo.com>
+
+       Update from gnulib
+       Update from gnulib.  In addition,
+
+       * admin/merge-gnulib: Fix paths for rename.
+
+2023-01-24  Po Lu  <luangruo@yahoo.com>
+
+       Improve lib-src/Makefile.in
+       * lib-src/Makefile.in (DONT_INSTALL):
+       (clean): Correctly define asset-directory-tool.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Fix distclean target
+       * cross/Makefile.in (distclean bootstrap-clean): Remove Makefile.
+
+2023-01-24  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.
+
+2023-01-24  Po Lu  <luangruo@yahoo.com>
+
+       Update android port
+       * xcompile: Move to cross.
+       * cross: New directory.
+
+       Update android port
+       * xcompile: Move to cross.
+       * cross: New directory.
+
+2023-01-21  Po Lu  <luangruo@yahoo.com>
+
+       Improve touch-screen support
+       * doc/lispref/commands.texi (Touchscreen Events): Document
+       changes.
+       * lisp/touch-screen.el (touch-screen-current-tool): Update doc
+       string.
+       (touch-screen-precision-scroll): New user option.
+       (touch-screen-handle-scroll): Use traditional scrolling by
+       default.
+       (touch-screen-handle-touch): Adust format of
+       touch-screen-current-tool.
+       (touch-screen-track-tap): Don't print waiting for events.
+       (touch-screen-track-drag): Likewise.  Also, don't call UPDATE
+       until threshold is reached.
+       (touch-screen-drag-mode-line-1, touch-screen-drag-mode-line):
+       Improve window dragging.
+
+       * src/fileio.c (Fverify_visited_file_modtime): Fix fs check.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-01-21  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * doc/emacs/android.texi (Android File System): Document that
+       ls-lisp is now used by default.
+       * java/org/gnu/emacs/EmacsThread.java (EmacsThread): Name the
+       thread something meaningful.
+       * lisp/loadup.el (featurep): Load ls-lisp on Android.
+       * lisp/ls-lisp.el (ls-lisp-use-insert-directory-program):
+       Default to off on Android.
+       * src/android.c (android_is_directory): New fucntion.
+       (android_fstatat): Handle directories created by
+       `android_opendir'.
+       (android_open): Return meaningful file mode.
+       (struct android_dir): New fields `next', `asset_file' and `fd'.
+       (android_opendir): Populate those fields.
+       (android_dirfd): New function.
+       (android_closedir): Close file descriptor if set.
+       (android_lookup_asset_directory_fd): New function.
+
+       * src/android.h: Update prototypes.
+       * src/androidfont.c (androidfont_check_init): New function.
+       (androidfont_list, androidfont_match, androidfont_draw)
+       (androidfont_open_font, androidfont_close_font)
+       (androidfont_has_char, androidfont_encode_char)
+       (androidfont_text_extents, androidfont_list_family): Initialize
+       font driver if necessary.
+       (init_androidfont): Don't initialize Java font if necessary.
+
+       * src/dired.c (open_directory): Return android_dirfd if
+       appropriate.
+       (directory_files_internal, file_name_completion_dirp): Implement
+       correctly for Android.
+
+       * src/fileio.c (check_mutable_filename): New function.
+       (Fcopy_file, Fdelete_directory_internal, Fdelete_file)
+       (Frename_file, Fadd_name_to_file, Fmake_symbolic_link)
+       (Fset_file_modes, Fset_file_times, Ffile_newer_than_file_p)
+       (Fverify_visited_file_modtime, Fset_visited_file_modtime): Check
+       that files being written to do not lie in /assets.
+
+       * src/sfntfont-android.c (GET_SCANLINE_BUFFER)
+       (sfntfont_android_u255to256, sfntfont_android_over_8888_1)
+       (sfntfont_android_over_8888, sfntfont_android_composite_bitmap):
+       Optimize on 64-bit ARM devices.
+       (sfntfont_android_put_glyphs): Optimize away memset if
+       background need not be filled.
+
+2023-01-20  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Update Android port
+       * src/android.c (android_run_select_thread, android_select)
+       (android_ftruncate):
+       * src/android.h (ftruncate): Fix compilation on Android 16 and
+       up.
+
+       Update Android port
+       * src/android.c (android_run_select_thread, android_init_events)
+       (android_select): Add alternative android_select implementation
+       for API 16 and lower.
+       * src/androidterm.c (handle_one_android_event): Fix
+       use-after-frees.
+
+       Remove unused file
+       * xcompile/lib/gnulib.mk.in: Delete.
+
+2023-01-20  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * .gitignore: Don't ignore verbose.mk.android.
+       * doc/emacs/Makefile.in (EMACSSOURCES): Add android.texi and
+       input.texi.
+       * doc/emacs/android.texi (Android): Document support for the
+       on-screen keyboard.
+       (Android Startup): Document how to start Emacs with -Q on
+       Android.
+       (Android Environment): Document how Emacs works around the
+       system ``task killer''.  Document changes to frame deletion
+       behavior.
+       * doc/emacs/emacs.texi (Top):
+       * doc/emacs/input.texi (Other Input Devices, On-Screen
+       Keyboards): Document how to use Emacs with virtual keyboards.
+       * doc/lispref/commands.texi (Touchscreen Events): Document
+       changes to `touch-screen-track-drag'.
+       * doc/lispref/frames.texi (Frames, On-Screen Keyboards): New
+       node.
+       * java/AndroidManifest.xml.in: Add settings activity and
+       appropriate OSK adjustment mode.
+       * java/org/gnu/emacs/EmacsActivity.java (onCreate): Allow
+       creating Emacs with -Q.
+       (onDestroy): Don't remove if killed by the system.
+       * java/org/gnu/emacs/EmacsContextMenu.java (inflateMenuItems):
+       Fix context menus again.
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Make all
+       event sending functions return long.
+       * java/org/gnu/emacs/EmacsPreferencesActivity.java
+       (EmacsPreferencesActivity): New class.
+       * java/org/gnu/emacs/EmacsService.java (EmacsService)
+       (onStartCommand, onCreate, startEmacsService): Start as a
+       foreground service if necessary to bypass system restrictions.
+       * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView):
+       * java/org/gnu/emacs/EmacsThread.java (EmacsThread, run):
+       * java/org/gnu/emacs/EmacsView.java (EmacsView, onLayout)
+       (onDetachedFromWindow):
+       * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, viewLayout):
+       Implement frame resize synchronization..
+       * java/org/gnu/emacs/EmacsWindowAttachmentManager.java
+       (EmacsWindowAttachmentManager, removeWindowConsumer): Adjust
+       accordingly for changes to frame deletion behavior.
+       * lisp/frame.el (android-toggle-on-screen-keyboard)
+       (frame-toggle-on-screen-keyboard): New function.
+       * lisp/minibuffer.el (minibuffer-setup-on-screen-keyboard)
+       (minibuffer-exit-on-screen-keyboard): New functions.
+       (minibuffer-setup-hook, minibuffer-exit-hook): Add new functions
+       to hooks.
+
+       * lisp/touch-screen.el (touch-screen-relative-xy): Accept new
+       value of window `frame'.  Return frame coordinates in that case.
+       (touch-screen-set-point-commands): New variable.
+       (touch-screen-handle-point-up): Respect that variable.
+       (touch-screen-track-drag): Return `no-drag' where appropriate.
+       (touch-screen-drag-mode-line-1, touch-screen-drag-mode-line):
+       Refactor to use `no-drag'.
+
+       * src/android.c (struct android_emacs_window): New methods.
+       Make all event sending functions return the event serial.
+       (android_toggle_on_screen_keyboard, android_window_updated): New
+       functions.
+       * src/android.h: Update prototypes.
+       * src/androidfns.c (Fandroid_toggle_on_screen_keyboard)
+       (syms_of_androidfns): New function.
+       * src/androidgui.h (struct android_any_event)
+       (struct android_key_event, struct android_configure_event)
+       (struct android_focus_event, struct android_window_action_event)
+       (struct android_crossing_event, struct android_motion_event)
+       (struct android_button_event, struct android_touch_event)
+       (struct android_wheel_event, struct android_iconify_event)
+       (struct android_menu_event): Add `serial' fields.
+
+       * src/androidterm.c (handle_one_android_event)
+       (android_frame_up_to_date):
+       * src/androidterm.h (struct android_output): Implement frame
+       resize synchronization.
+
+2023-01-20  Po Lu  <luangruo@yahoo.com>
+
+       Check in missing file
+       * xcompile/verbose.mk.android: New file.
+
+2023-01-19  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-01-19  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * .gitignore: Add new files.
+       * INSTALL.android: Explain how to build Emacs for ancient
+       versions of Android.
+       * admin/merge-gnulib (GNULIB_MODULES): Add getdelim.
+       * build-aux/config.guess (timestamp, version):
+       * build-aux/config.sub (timestamp, version): Autoupdate.
+       * configure.ac (BUILD_DETAILS, ANDROID_MIN_SDK):
+       (ANDROID_STUBIFY): Allow specifying CFLAGS via ANDROID_CFLAGS.
+       Add new configure tests for Android API version when not
+       explicitly specified.
+
+       * doc/emacs/android.texi (Android): Add reference to ``Other
+       Input Devices''.
+       (Android File System): Remove restrictions on directory-files on
+       the assets directory.
+       * doc/emacs/emacs.texi (Top): Add Other Input Devices to menu.
+       * doc/emacs/input.texi (Other Input Devices): New node.
+       * doc/lispref/commands.texi (Touchscreen Events): Document
+       changes to touchscreen input events.
+       * doc/lispref/frames.texi (Pop-Up Menus): Likewise.
+       * etc/NEWS: Announce changes.
+       * java/Makefile.in: Use lib-src/asset-directory-tool to generate
+       an `directory-tree' file placed in /assets.
+       * java/debug.sh: Large adjustments to support Android 2.2 and
+       later.
+
+       * java/org/gnu/emacs/EmacsContextMenu.java (inflateMenuItems):
+       * java/org/gnu/emacs/EmacsCopyArea.java (perform):
+       * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog):
+       * java/org/gnu/emacs/EmacsDrawLine.java (perform):
+       * java/org/gnu/emacs/EmacsDrawRectangle.java (perform):
+       * java/org/gnu/emacs/EmacsDrawable.java (EmacsDrawable):
+       * java/org/gnu/emacs/EmacsFillPolygon.java (perform):
+       * java/org/gnu/emacs/EmacsFillRectangle.java (perform):
+       * java/org/gnu/emacs/EmacsGC.java (EmacsGC):
+       * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap):
+       (destroyHandle):
+       * java/org/gnu/emacs/EmacsSdk7FontDriver.java (draw): Avoid
+       redundant canvas saves and restores.
+       * java/org/gnu/emacs/EmacsService.java (run):
+       * java/org/gnu/emacs/EmacsView.java (EmacsView):
+       (handleDirtyBitmap):
+       * java/org/gnu/emacs/EmacsWindow.java (changeWindowBackground)
+       (EmacsWindow): Make compatible with Android 2.2 and later.
+
+       * lib-src/Makefile.in (DONT_INSTALL): Add asset-directory-tool
+       on Android.:(asset-directory-tool{EXEEXT}): New target.
+       * lib-src/asset-directory-tool.c (struct directory_tree, xmalloc)
+       (main_1, main_2, main): New file.
+
+       * lib, m4: Merge from gnulib.  This will be reverted before
+       merging to master.
+
+       * lisp/button.el (button-map):
+       (push-button):
+       * lisp/frame.el (display-popup-menus-p): Improve touchscreen
+       support.
+       * lisp/subr.el (event-start):
+       (event-end): Handle touchscreen events.
+       * lisp/touch-screen.el (touch-screen-handle-timeout):
+       (touch-screen-handle-point-update):
+       (touch-screen-handle-point-up):
+       (touch-screen-track-tap):
+       (touch-screen-track-drag):
+       (touch-screen-drag-mode-line-1):
+       (touch-screen-drag-mode-line): New functions.
+       ([mode-line touchscreen-begin]):
+       ([bottom-divider touchscreen-begin]): Bind new events.
+
+       * lisp/wid-edit.el (widget-event-point):
+       (widget-keymap):
+       (widget-event-start):
+       (widget-button--check-and-call-button):
+       (widget-button-click): Improve touchscreen support.
+
+       * src/alloc.c (make_lisp_symbol): Avoid ICE on Android NDK GCC.
+       (mark_pinned_symbols): Likewise.
+
+       * src/android.c (struct android_emacs_window): New struct.
+       (window_class): New variable.
+       (android_run_select_thread): Add workaround for Android platform
+       bug.
+       (android_extract_long, android_scan_directory_tree): New
+       functions.
+       (android_file_access_p): Use those functions instead.
+       (android_init_emacs_window): New function.
+       (android_init_emacs_gc_class): Update signature of `markDirty'.
+       (android_change_gc, android_set_clip_rectangles): Tell the GC
+       whether or not clip rects were dirtied.
+       (android_swap_buffers): Do not look up method every time.
+       (struct android_dir): Adjust for new directory tree lookup.
+       (android_opendir, android_readdir, android_closedir): Likewise.
+       (android_four_corners_bilinear): Fix coding style.
+       (android_ftruncate): New function.
+       * src/android.h: Update prototypes.  Replace ftruncate with
+       android_ftruncate when necessary.
+
+       * src/androidterm.c (handle_one_android_event): Pacify GCC.  Fix
+       touch screen tool bar bug.
+       * src/emacs.c (using_utf8): Fix compilation error.
+       * src/fileio.c (Ffile_system_info): Return Qnil when fsusage.o
+       is not built.
+       * src/filelock.c (BOOT_TIME_FILE): Fix definition for Android.
+       * src/frame.c (Fx_parse_geometry): Fix uninitialized variable
+       uses.
+       * src/keyboard.c (lispy_function_keys): Fix `back'.
+       * src/menu.c (x_popup_menu_1): Handle touch screen events.
+       (Fx_popup_menu): Document changes.
+
+       * src/sfnt.c (main): Improve tests.
+
+       * src/sfntfont-android.c (sfntfont_android_put_glyphs): Fix
+       minor problem.
+       (init_sfntfont_android): Check for
+       HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL.
+       * src/sfntfont.c (struct sfnt_font_desc): New fields `adstyle'
+       and `languages'.
+       (sfnt_parse_style): Append tokens to adstyle.
+       (sfnt_parse_languages): New function.
+       (sfnt_enum_font_1): Parse supported languages and adstyle.
+       (sfntfont_list_1): Handle new fields.
+       (sfntfont_text_extents): Fix uninitialized variable use.
+       (syms_of_sfntfont, mark_sfntfont): Adjust accordingly.
+
+2023-01-17  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-01-17  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * doc/emacs/android.texi (Android Fonts): Document that TTC
+       format fonts are now supported.
+       * doc/emacs/emacs.texi (Top): Fix menus.
+       * doc/lispref/commands.texi (Touchscreen Events)
+       (Key Sequence Input): Document changes to touchscreen events.
+       * etc/DEBUG: Describe how to debug 64 bit binaries on Android.
+
+       * java/org/gnu/emacs/EmacsCopyArea.java (perform): Explicitly
+       recycle copy bitmap.
+       * java/org/gnu/emacs/EmacsDialog.java (EmacsDialog): New class.
+       * java/org/gnu/emacs/EmacsDrawRectangle.java (perform): Use 5
+       point PolyLine like X, because Android behaves like Postscript
+       on some devices and X elsewhere.
+       * java/org/gnu/emacs/EmacsFillRectangle.java (perform):
+       Explicitly recycle copy bitmap.
+       * java/org/gnu/emacs/EmacsPixmap.java (destroyHandle):
+       Explicitly recycle bitmap and GC if it is big.
+       * java/org/gnu/emacs/EmacsView.java (EmacsView): Make
+       `bitmapDirty' a boolean.
+       (handleDirtyBitmap): Reimplement in terms of that boolean.
+       Explicitly recycle old bitmap and GC.
+       (onLayout): Fix lock up.
+       (onDetachedFromWindow): Recycle bitmap and GC.
+
+       * java/org/gnu/emacs/EmacsWindow.java (requestViewLayout):
+       Update call to explicitlyDirtyBitmap.
+
+       * src/android.c (android_run_select_thread, android_select):
+       Really fix android_select.
+       (android_build_jstring): New function.
+       * src/android.h: Update prototypes.
+       * src/androidmenu.c (android_process_events_for_menu): Totally
+       unblock input before process_pending_signals.
+       (android_menu_show): Remove redundant unblock_input and
+       debugging code.
+       (struct android_emacs_dialog, android_init_emacs_dialog)
+       (android_dialog_show, android_popup_dialog, init_androidmenu):
+       Implement popup dialogs on Android.
+
+       * src/androidterm.c (android_update_tools)
+       (handle_one_android_event, android_frame_up_to_date): Allow
+       tapping tool bar items.
+       (android_create_terminal): Add dialog hook.
+       (android_wait_for_event): Adjust call to android_select.
+       * src/androidterm.h (struct android_touch_point): New field
+       `tool_bar_p'.
+       * src/keyboard.c (read_key_sequence, head_table)
+       (syms_of_keyboard): Prefix touchscreen events with posn.
+       * src/keyboard.h (EVENT_HEAD): Handle touchscreen events.
+       * src/process.c (wait_reading_process_output): Adjust call to
+       android_select.
+       * src/sfnt.c (sfnt_read_table_directory): If the first long
+       turns out to be ttcf, return -1.
+       (sfnt_read_ttc_header): New function.
+       (main): Test TTC support.
+
+       * src/sfnt.h (struct sfnt_ttc_header): New structure.
+       (enum sfnt_ttc_tag): New enum.
+
+       * src/sfntfont-android.c (struct
+       sfntfont_android_scanline_buffer): New structure.
+       (GET_SCANLINE_BUFFER): New macro.  Try to avoid so much malloc
+       upon accessing the scanline buffer.
+       (sfntfont_android_put_glyphs): Do not use SAFE_ALLOCA to
+       allocate the scaline buffer.
+       (Fandroid_enumerate_fonts): Enumerate ttc fonts too.
+
+       * src/sfntfont.c (struct sfnt_font_desc): New field `offset'.
+       (sfnt_enum_font_1): Split out enumeration code from
+       sfnt_enum_font.
+       (sfnt_enum_font): Read TTC tables and enumerate each font
+       therein.
+       (sfntfont_open): Seek to the offset specified.
+
+       * xcompile/Makefile.in (maintainer-clean): Fix depends here.
+
+2023-01-16  Po Lu  <luangruo@yahoo.com>
+
+       Fix display of glyphs with word-sized component offsets on Android
+       * src/sfnt.c (sfnt_decompose_compound_glyph): Handle correctly
+       the Y offset in components with ARG_1_AND_2_ARE_WORDS.
+       (main): Update debugging code.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-01-16  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * doc/emacs/android.texi (Android, Android Environment): Improve
+       documentation.
+       * doc/lispref/commands.texi (Touchscreen Events): Document
+       changes to touchscreen support.
+       * doc/lispref/display.texi (Defining Faces, Window Systems):
+       * doc/lispref/frames.texi (Frame Layout, Font and Color
+       Parameters):
+       * doc/lispref/os.texi (System Environment): Document Android in
+       various places.
+
+       * java/org/gnu/emacs/EmacsWindow.java (figureChange): Fix crash.
+       * lisp/loadup.el: ("touch-screen"): Load touch-screen.el.
+       * lisp/pixel-scroll.el: Autoload two functions.
+       * lisp/term/android-win.el: Add require 'touch-screen.
+       * lisp/touch-screen.el (touch-screen-current-tool)
+       (touch-screen-current-timer, touch-screen-delay)
+       (touch-screen-relative-xy, touch-screen-handle-scroll)
+       (touch-screen-handle-timeout, touch-screen-handle-point-update)
+       (touch-screen-handle-point-up, touch-screen-handle-touch)
+       (global-map, touch-screen): New file.
+       * src/android.c (android_run_debug_thread): Fix build on 64 bit
+       systems.
+       (JNICALL, android_put_pixel): Likewise.
+       (android_transform_coordinates, android_four_corners_bilinear)
+       (android_fetch_pixel_bilinear, android_project_image_bilinear)
+       (android_fetch_pixel_nearest_24, android_fetch_pixel_nearest_1)
+       (android_project_image_nearest): New functions.
+       * src/androidgui.h (struct android_transform): New structure.
+       * src/androidterm.c (android_note_mouse_movement): Remove
+       obsolete TODO.
+       (android_get_scale_factor): New function.
+       (android_draw_underwave): Scale underwave correctly.
+       * src/dispextern.h: Support native image transforms on Android.
+       * src/image.c (matrix_identity, matrix_rotate)
+       (matrix_mirror_horizontal, matrix_translate): New functions.
+       (image_set_transform): Implement native image transforms on
+       Android.
+       (Fimage_transforms_p): Implement on Android.
+
+       * src/keyboard.c (make_lispy_event, syms_of_keyboard): Handle
+       touch screen- menu bar events.
+       * src/sfnt.c: Fix typo in comment.
+       * src/sfntfont-android.c (sfntfont_android_blend, U255TO256)
+       (sfntfont_android_put_glyphs): Avoid redundant swizzling.
+       * src/sfntfont.c (sfntfont_lookup_char): Fix build on 64 bit
+       systems.
+
+2023-01-15  Po Lu  <luangruo@yahoo.com>
+
+       Implement submenus on Android
+       * java/org/gnu/emacs/EmacsActivity.java (onCreate): Set the
+       default theme to Theme.DeviceDefault.NoActionBar if possible.
+       (onContextMenuClosed): Add hack for Android bug.
+       * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu)
+       (onMenuItemClick): Set flag upon submenu selection.
+       (inflateMenuItems): Set onClickListener for submenus as well.
+       (display1): Clear new flag.
+       * java/org/gnu/emacs/EmacsDrawRectangle.java (perform): Fix
+       rectangle bounds.
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative):
+       * java/org/gnu/emacs/EmacsService.java (onCreate): Pass cache
+       directory.
+       (sync): New function.
+       * src/android.c (struct android_emacs_service): New method
+       `sync'.
+       (setEmacsParams, initEmacs): Handle cache directory.
+       (android_init_emacs_service): Initialize new method `sync'.
+       (android_sync): New function.
+       * src/androidfns.c (Fx_show_tip): Call both functions.
+       * src/androidgui.h: Update prototypes.
+       * src/androidmenu.c (struct android_menu_subprefix)
+       (android_free_subprefixes, android_menu_show): Handle submenu
+       prefixes correctly.
+       * src/androidterm.c (handle_one_android_event): Clear help echo
+       on MotionNotify like on X.
+       * src/menu.c (single_menu_item): Enable submenus on Android.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-01-15  Po Lu  <luangruo@yahoo.com>
+
+       Implement toolkit menus on Android
+       * java/org/gnu/emacs/EmacsActivity.java (onContextMenuClosed):
+       New function.
+       * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu):
+       New field `itemAlreadySelected'.
+       (onMenuItemClick): New function.
+       (inflateMenuItems): Attach onClickListener as appropriate.
+       (display1): Clear itemAlreadySelected.
+       (display): Fix runnable synchronization.
+       * java/org/gnu/emacs/EmacsNative.java (sendContextMenu): New
+       function.
+       * java/org/gnu/emacs/EmacsView.java (popupMenu):
+       (cancelPopupMenu): Set popupactive correctly.
+
+       * src/android.c (android_run_select_thread): Fix android_select
+       again.
+       (android_wait_event): New function.
+       * src/android.h: Update prototypes.
+       * src/androidgui.h (enum android_event_type): New
+       `ANDROID_CONTEXT_MENU' event.
+       (struct android_menu_event, union android_event): Add new event.
+
+       * src/androidmenu.c (struct android_emacs_context_menu): New
+       structure.
+       (android_init_emacs_context_menu): Add `dismiss' method.
+       (struct android_dismiss_menu_data): New structure.
+       (android_dismiss_menu, android_process_events_for_menu): New
+       functions.
+       (android_menu_show): Set an actual item ID.
+       (popup_activated): Define when stubify as well.
+       (Fmenu_or_popup_active_p): New function.
+       (syms_of_androidmenu): New function.
+
+       * src/androidterm.c (handle_one_android_event): Handle context
+       menu events.
+       * src/androidterm.h (struct android_display_info): New field for
+       menu item ID.
+       * src/emacs.c (android_emacs_init): Call syms_of_androidmenu.
+       * src/xdisp.c (note_mouse_highlight): Return if popup_activated
+       on Android as well.
+
+2023-01-14  Po Lu  <luangruo@yahoo.com>
+
+       Fix android_select
+       * src/android.c (android_run_select_thread, android_select):
+       Handle EINTR in sem_wait and fix sigsets.
+
+       Add temporary gnulib patch
+       * xcompile/lib/fpending.c (__fpending): Fix gnulib problem.
+
+       Drop unneeded changes to gnulib
+       * xcompile/lib/fpending.c (__fpending):
+       * xcompile/lib/open.c:
+       * xcompile/lib/unistd.c (_GL_UNISTD_INLINE): Remove Android
+       patches.
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-01-14  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * java/Makefile.in (clean): Fix distclean and bootstrap-clean rules.
+       * java/debug.sh (jdb_port):
+       (attach_existing):
+       (num_pids):
+       (line): Add new options to upload a gdbserver binary to the device.
+
+       * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): Make
+       focusedActivities public.
+       * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu):
+       New class.
+       * java/org/gnu/emacs/EmacsDrawRectangle.java (perform): Fix
+       bounds computation.
+       * java/org/gnu/emacs/EmacsGC.java (markDirty): Set stroke width
+       explicitly.
+       * java/org/gnu/emacs/EmacsService.java (EmacsService)
+       (getLocationOnScreen, nameKeysym): New functions.
+       * java/org/gnu/emacs/EmacsView.java (EmacsView): Disable focus
+       highlight.
+       (onCreateContextMenu, popupMenu, cancelPopupMenu): New
+       functions.
+       * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): Implement a
+       kind of ``override redirect'' window for tooltips.
+       * src/android.c (struct android_emacs_service): New method
+       `name_keysym'.
+       (android_run_select_thread, android_init_events):
+       (android_select): Release select thread on semaphores instead of
+       signals to avoid one nasty race on SIGUSR2 delivery.
+       (android_init_emacs_service): Initialize new method.
+       (android_create_window): Handle CW_OVERRIDE_REDIRECT.
+       (android_move_resize_window, android_map_raised)
+       (android_translate_coordinates, android_get_keysym_name)
+       (android_build_string, android_exception_check): New functions.
+       * src/android.h: Update prototypes.
+
+       * src/androidfns.c (android_set_parent_frame, Fx_create_frame)
+       (unwind_create_tip_frame, android_create_tip_frame)
+       (android_hide_tip, compute_tip_xy, Fx_show_tip, Fx_hide_tip)
+       (syms_of_androidfns): Implement tooltips and iconification
+       reporting.
+
+       * src/androidgui.h (enum android_window_value_mask): Add
+       CWOverrideRedirect.
+       (struct android_set_window_attributes): Add `override_redirect'.
+       (ANDROID_IS_MODIFIER_KEY): Recognize Caps Lock.
+
+       * src/androidmenu.c (struct android_emacs_context_menu): New
+       struct.
+       (android_init_emacs_context_menu, android_unwind_local_frame)
+       (android_push_local_frame, android_menu_show, init_androidmenu):
+       New functions.
+
+       * src/androidterm.c (handle_one_android_event): Fix NULL pointer
+       dereference.
+       (android_fullscreen_hook): Handle fullscreen correctly.
+       (android_draw_box_rect): Fix top line.
+       (get_keysym_name): Implement function.
+       (android_create_terminal): Remove scroll bar stubs and add menu
+       hook.
+
+       * src/androidterm.h: Update prototypes.
+       * src/emacs.c (android_emacs_init): Initialize androidmenu.c.
+       * xcompile/Makefile.in: Fix clean rules.
+
+2023-01-14  Po Lu  <luangruo@yahoo.com>
+
+       Improve reliability of Android build system
+       * .gitignore: Add new files.
+       * INSTALL.android: New file.
+       * Makefile.in (clean_dirs): Clean xcompile as well.
+       * admin/merge-gnulib (avoided_flags): Import gnulib into Android
+       directory as well.
+       * doc/emacs/android.texi (Android):
+       * doc/emacs/emacs.texi (Top): New node `Android'.
+       * java/org/gnu/emacs/EmacsThread.java (run): Use right
+       executable name.
+       * lib/Makefile.in (ANDROID_CFLAGS): Use better way to refer to
+       /src.
+       (vpath): Delete ugly block of vpath statements.
+       (mostlyclean): Remove Makefile.android.
+       * lib/fpending.c (__fpending):
+       * lib/open.c:
+       * lib/unistd.c (_GL_UNISTD_INLINE): Revert changes to gnulib in
+       lib/.
+       * src/android.h:
+       * src/androidterm.c: Fix build.
+       * xcompile/Makefile.in (LIB_SRCDIR):
+       (LIBSRC_BINARIES, src/verbose.mk):
+       (PRE_BUILD_DEPS, PHONY): Use gnulib in xcompile/lib/ as opposed
+       to lib/.
+       * xcompile/README: Adjust README.
+
+       Check in gnulib with Android patches
+       * xcompile/lib: Check-in gnulib with patches for Android.
+
+2023-01-13  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       Fix crashes in Android port
+       * java/org/gnu/emacs/EmacsService.java (queryTree): Fix NULL
+       pointer dereference.
+       * src/android.c (android_query_tree): Set *nchildren_return.
+
+       * .gitignore: Add AndroidManifest.xml.
+
+       * java/AndroidManifest.xml: Remove file that is now generated.
+
+       * src/frame.c (make_monitor_attribute_list): Allow source to be NULL.
+
+2023-01-13  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * configure.ac (ANDROID_MIN_SDK): New variable.
+       (DX): Remove and replace with D8.
+       (XCONFIGURE): Check for the minimum version of Android the cross
+       compiler compiles for.  Generate java/AndroidManifest.xml from
+       java/AndroidManifest.xml.in.  Allow using Zlib on Android.
+
+       * java/AndroidManifest.xml.in: New file.  Use the minimum SDK
+       detected by configure.
+
+       * java/Makefile.in (top_srcdir, version): New variables.
+       (DX, D8): Replace with D8.
+       (ANDROID_MIN_SDK, APK_NAME): New variables.
+       (.PHONY):
+       (.PRECIOUS):
+       (classes.dex):
+       (emacs.apk): Generate $(APK_NAME) instead of `emacs.apk'.
+
+       * java/debug.sh: New option --attach-existing.  Attach to an
+       existing Emacs instance when specified.
+
+       * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): New
+       field `isPaused'.
+       (invalidateFocus1): Fix infinite recursion.
+       (detachWindow): Deiconify window.
+       (attachWindow): Iconify the window if the activity is paused.
+       (onCreate): Use the ``no title bar'' theme.
+       (onPause, onResume): New functions.
+       * java/org/gnu/emacs/EmacsNative.java (sendTouchUp, sendTouchDown)
+       (sendTouchMove, sendWheel, sendIconified, sendDeiconified): New
+       functions.
+       * java/org/gnu/emacs/EmacsSdk7FontDriver.java (Sdk7Typeface):
+       (list): Remove logging for code that is mostly going to be unused.
+       * java/org/gnu/emacs/EmacsService.java (ringBell, queryTree)
+       (getScreenWidth, getScreenHeight, detectMouse): New functions.
+       * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView)
+       (surfaceChanged, surfaceCreated, surfaceDestroyed): Add extra
+       debug logging.  Avoid deadlock in surfaceCreated.
+
+       * java/org/gnu/emacs/EmacsView.java (EmacsView): Try very hard
+       to make the SurfaceView respect Z order.  It didn't work.
+       (handleDirtyBitmap): Copy over the contents from the old bitmap.
+       (explicitlyDirtyBitmap): New function.
+       (onLayout): Don't dirty bitmap if unnecessary.
+       (damageRect, swapBuffers): Don't synchronize so hard.
+       (onTouchEvent): Call window.onTouchEvent instead.
+       (moveChildToBack, raise, lower): New functions.
+
+       * java/org/gnu/emacs/EmacsWindow.java (Coordinate): New
+       subclass.
+       (pointerMap, isMapped, isIconified, dontFocusOnMap)
+       (dontAcceptFocus): New fields.
+       (EmacsWindow): Don't immediately register unmapped window.
+       (viewLayout): Send configure event outside the lock.
+       (requestViewLayout): Explicitly dirty the bitmap.
+       (mapWindow): Register the window now.  Respect dontFocusOnMap.
+       (unmapWindow): Unregister the window now.
+       (figureChange, onTouchEvent): New functions.
+       (onSomeKindOfMotionEvent): Handle scroll wheel events.
+       (reparentTo, makeInputFocus, raise, lower, getWindowGeometry)
+       (noticeIconified, noticeDeiconified, setDontAcceptFocus)
+       (setDontFocusOnMap, getDontFocusOnMap): New functions.
+       * java/org/gnu/emacs/EmacsWindowAttachmentManager.java
+       (registerWindow, detachWindow): Synchronize.
+       (noticeIconified, noticeDeiconified): New functions.
+       (copyWindows): New function.
+
+       * lisp/frame.el (frame-geometry, frame-edges)
+       (mouse-absolute-pixel-position, set-mouse-absolute-pixel-position)
+       (frame-list-z-order, frame-restack, display-mouse-p)
+       (display-monitor-attributes-list): Implement on Android.
+
+       * lisp/mwheel.el (mouse-wheel-down-event):
+       (mouse-wheel-up-event):
+       (mouse-wheel-left-event):
+       (mouse-wheel-right-event): Define on Android.
+
+       * src/android.c (struct android_emacs_service): New methods
+       `ringBell', `queryTree', `getScreenWidth', `getScreenHeight',
+       and `detectMouse'.
+       (struct android_event_queue, android_init_events)
+       (android_next_event, android_write_event): Remove write limit.
+       (android_file_access_p): Handle directories correcty.
+       (android_close): Fix coding style.
+       (android_fclose): New function.
+       (android_init_emacs_service): Initialize new methods.
+       (android_reparent_window): Implement function.
+       (android_bell, android_set_input_focus, android_raise_window)
+       (android_lower_window, android_query_tree, android_get_geometry)
+       (android_get_screen_width, android_get_screen_height)
+       (android_get_mm_width, android_get_mm_height, android_detect_mouse)
+       (android_set_dont_focus_on_map, android_set_dont_accept_focus):
+       New functions.
+       (struct android_dir): New structure.
+       (android_opendir, android_readdir, android_closedir): New
+       functions.
+       (emacs_abort): Implement here on Android and poke debuggerd into
+       generating a tombstone.
+
+       * src/android.h: Update prototypes.
+
+       * src/androidfns.c (android_set_parent_frame): New function.
+       (android_default_font_parameter): Use sane font size by default.
+       (Fx_display_pixel_width, Fx_display_pixel_height)
+       (Fx_display_mm_width, Fx_display_mm_height)
+       (Fx_display_monitor_attributes_list): Rename to start with
+       `android-'.  Implement.  Fiddle with documentation to introduce
+       Android specific nuances.
+       (Fandroid_display_monitor_attributes_list): New function.
+       (Fx_frame_geometry, frame_geometry): New function.
+       (Fandroid_frame_geometry): Implement correctly.
+       (Fx_frame_list_z_order): Rename to start with `android-'.
+       (android_frame_list_z_order, Fandroid_frame_list_z_order):
+       Implement.
+       (Fx_frame_restack): Rename to start with `android-'.
+       (Fandroid_frame_restack): ``Implement''.
+       (Fx_mouse_absolute_pixel_position): Rename to start with
+       `android-'.
+       (Fandroid_mouse_absolute_pixel_position): ``Implement''.
+       (Fx_set_mouse_absolute_pixel_position): Rename to start with
+       `android-'.
+       (Fandroid_set_mouse_absolute_pixel_position): ``Implement''.
+       (Fandroid_detect_mouse): New function.
+       (android_set_menu_bar_lines): Use FRAME_ANDROID_DRAWABLE when
+       clearing area.
+       (android_set_no_focus_on_map, android_set_no_accept_focus): New
+       functions.
+       (android_frame_parm_handlers): Register new frame parameter
+       handlers.
+       (syms_of_androidfns): Update appropriately.
+
+       * src/androidfont.c (androidfont_draw): Use
+       FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW.
+
+       * src/androidgui.h (enum android_event_type): New events.
+       (struct android_touch_event, struct android_wheel_event)
+       (struct android_iconify_event): New structures.
+       (union android_event): Add new events.
+
+       * src/androidterm.c (android_clear_frame): Use
+       FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW.
+       (android_flash, android_ring_bell): Implement bell ringing.
+       (android_toggle_invisible_pointer): Don't TODO function that
+       can't be implemented.
+       (show_back_buffer, android_flush_dirty_back_buffer_on): Check if
+       a buffer flip is required before doing the flip.
+       (android_lower_frame, android_raise_frame): Implement functions.
+       (android_update_tools, android_find_tool): New functions.
+       (handle_one_android_event): Handle new iconification, wheel and
+       touch events.
+       (android_read_socket): Implement pending-autoraise-frames.
+       (android_frame_up_to_date): Implement bell ringing.
+       (android_buffer_flipping_unblocked_hook): Check if a buffer flip
+       is required before doing the flip.
+       (android_focus_frame, android_frame_highlight)
+       (android_frame_unhighlight): New function.
+       (android_frame_rehighlight): Implement functions.
+       (android_iconify_frame): Always display error.
+       (android_set_alpha): Update commentary.
+       (android_free_frame_resources): Free frame touch points.
+       (android_scroll_run, android_flip_and_flush)
+       (android_clear_rectangle, android_draw_fringe_bitmap)
+       (android_draw_glyph_string_background, android_fill_triangle)
+       (android_clear_point, android_draw_relief_rect)
+       (android_draw_box_rect, android_draw_glyph_string_bg_rect)
+       (android_draw_image_foreground, android_draw_stretch_glyph_string)
+       (android_draw_underwave, android_draw_glyph_string_foreground)
+       (android_draw_composite_glyph_string_foreground)
+       (android_draw_glyphless_glyph_string_foreground)
+       (android_draw_glyph_string, android_clear_frame_area)
+       (android_clear_under_internal_border, android_draw_hollow_cursor)
+       (android_draw_bar_cursor, android_draw_vertical_window_border)
+       (android_draw_window_divider): Use FRAME_ANDROID_DRAWABLE
+       instead of FRAME_ANDROID_WINDOW for drawing operations.
+
+       * src/androidterm.h (struct android_touch_point): New structure.
+       (struct android_output): New fields.
+       (FRAME_ANDROID_NEED_BUFFER_FLIP): New macro.
+
+       * src/dired.c (emacs_readdir, open_directory)
+       (directory_files_internal_unwind, read_dirent)
+       (directory_files_internal, file_name_completion): Add
+       indirection over readdir and opendir.  Use android variants on
+       Android.
+
+       * src/dispnew.c (Fopen_termscript):
+       * src/fileio.c (fclose_unwind): Use emacs_fclose.
+       (Faccess_file): Call android_file_access_p.
+       (file_accessible_directory_p): Append right suffix to Android
+       assets directory.
+       (do_auto_save_unwind): Use emacs_fclose.
+       * src/keyboard.c (lispy_function_keys): Use right function key
+       for page up and page down.
+       (Fopen_dribble_file): Use emacs_fclose.
+
+       * src/lisp.h: New prototype emacs_fclose.
+
+       * src/lread.c (close_infile_unwind): Use emacs_fclose.
+
+       * src/sfnt.c (sfnt_curve_is_flat): Fix area-squared computation.
+       (sfnt_prepare_raster): Compute raster width and height
+       consistently with outline building.
+       (sfnt_build_outline_edges): Use the same offsets used to set
+       offy and offx.
+       (main): Adjust debug code.
+
+       * src/sfntfont-android.c (sfntfont_android_saturate32): Delete
+       function.
+       (sfntfont_android_blend, sfntfont_android_blendrgb): Remove
+       unnecessary debug code.
+       (sfntfont_android_composite_bitmap): Prevent out of bounds
+       write.
+       (sfntfont_android_put_glyphs): Use FRAME_ANDROID_DRAWABLE.
+       (init_sfntfont_android): Initialize Monospace Serif font to
+       something sensible.
+       * src/sfntfont.c (sfntfont_text_extents): Clear glyph metrics
+       before summing up pcm.
+       (sfntfont_draw): Use s->font instead of s->face->font.
+
+       * src/sysdep.c (emacs_fclose): Wrap around android_fclose on
+       android.
+
+       * src/term.c (Fsuspend_tty):
+       (delete_tty): Use emacs_fclose.
+       * src/verbose.mk.in (AM_V_DX): Replace with D8 version.
+
+2023-01-11  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-01-11  Po Lu  <luangruo@yahoo.com>
+
+       Bring up the sfnt-android font driver
+       * configure.ac (ANDROID_CFLAGS): Add sfnt-related font objects
+       to ANDROID_OBJ when not building stubs.
+       * lisp/startup.el (android-fonts-enumerated): New variable.
+       (normal-top-level): Set it.  Also enumerate fonts as early as
+       possible upon startup.
+
+       * src/alloc.c (cleanup_vector): Only finalize Android font
+       entities.
+       (garbage_collect): Mark sfntfont.c.
+
+       * src/android.c (struct android_emacs_drawable): New field
+       `damage_rect'.
+       (android_init_emacs_drawable): Initialize
+       Lorg/gnu/emacs/EmacsDrawable;#damageRect(Landroid/graphics/rect;)V.
+       (android_create_gc): Initialize cached GC fields.
+       (android_free_gc): Free cached GC clip rectangles.
+       (android_change_gc): Cache fields as appropriate.
+       (android_set_clip_rectangles): Set cached clip rectangles for
+       easy access from C.
+       (android_get_gc_values): Use cached values.
+       (android_get_image): Remove obsolete comment.
+       (android_lock_bitmap, android_damage_window): New functions that
+       don't parallel anything on X.
+
+       * src/android.h: Update prototypes.
+
+       * src/androidfns.c (android_default_font_parameter): Set Droid
+       Sans Mono as the default monospace font.
+       (Fx_create_frame): Register the sfntfont driver.
+
+       * src/androidgui.h (struct android_gc): Add C side caches for
+       clip rectangles and the foreground and background colors.
+
+       * src/androidterm.h: Update prototypes.
+
+       * src/dispextern.h (struct gui_box): New struct.
+       (gui_union_rectangles): New function.
+
+       * src/emacs.c (android_emacs_init): Initialize Android font
+       stuff late.
+       * src/font.c (font_make_entity): Clear `is_android' field on
+       Android.
+       (font_make_entity_android): Set `is_android' field.
+       * src/font.h (struct font_entity): New field `is_android'.
+
+       * src/print.c (print_vectorlike): Don't print private data,
+       which could include Lisp_Misc.
+
+       * src/sfnt.c (sfnt_read_cmap_format_0, sfnt_read_cmap_format_2)
+       (sfnt_read_cmap_format_4, sfnt_read_cmap_format_6)
+       (sfnt_read_cmap_format_8, sfnt_read_cmap_format_12): Remove
+       buggy pragmas.
+       (sfnt_lookup_glyph_4_1): New function.
+       (sfnt_lookup_glyph_4): Handle malformed lookup tables found on
+       Android.
+       (sfnt_lookup_glyph): Fix overflow problems in glyph checks.
+       (sfnt_read_glyph): Handle empty glyphs.  This implements some
+       behavior which everyone else seems to as well, but I can't find
+       documented in the TrueType Reference Manual.
+       (sfnt_free_glyph): Export correctly.
+       (sfnt_transform_coordinates): Make faster.
+       (sfnt_lerp_half): Fix lerping in some cases.
+       (sfnt_decompose_glyph): Handle empty glyphs correctly.  Close
+       contours manually instead of waiting for the edge building
+       process to do that.  This lets curves be handled correctly.
+       (struct sfnt_build_glyph_outline_context): Move internal struct
+       back to sfnt.c.
+       (sfnt_build_append): Fix detection of initial entry.
+       (sfnt_curve_to_and_build_1): Fix De Casteljau implementation.
+       (sfnt_curve_to_and_build): Use fixed point arithmetic to scale
+       outlines.
+       (sfnt_build_glyph_outline): Clear reference counts.  Use fixed
+       point arithmetic.
+       (sfnt_prepare_raster): Align rasters to 4 bytes,
+       SFNT_POLY_ALIGNMENT.  Fix calculation of offx and offy.
+       (sfnt_step_edge_by): Step edge by previously computed step_x.
+       (sfnt_build_outline_edges): Adjust for already closed contours.
+       Ignore edges abandoned after grid fit.  Also precompute step_x
+       to avoid multiplication on each span rastered.
+       (sfnt_poly_edges): Improve alignment.
+       (sfnt_fill_span): Rewrite to avoid control flow in while loop.
+       (sfnt_poly_span): Remove unnecessary code.
+       (sfnt_raster_glyph_outline): Use raster stride instead of width.
+       (sfnt_test_edge, sfnt_test_raster, main): Improve debugging
+       code.
+
+       * src/sfnt.h (struct sfnt_glyph_outline): Add refcount field to
+       outline.
+       (struct sfnt_build_glyph_outline_context): Remove private
+       struct.
+       (struct sfnt_raster): Add refcount field to raster.
+       (struct sfnt_edge): Improve doc.  Add `source_x' field used when
+       built with TEST.
+       (SFNT_CEIL_FIXED): New macro.
+
+       * src/sfntfont-android.c (sfntfont_android_saturate32)
+       (sfntfont_android_scale32, sfntfont_android_mul8x2)
+       (sfntfont_android_blend, U255TO256)
+       (sfntfont_android_composite_bitmap, sfntfont_android_union_boxes)
+       (sfntfont_android_put_glyphs, sfntfont_android_get_cache): New
+       functions.
+       (android_sfntfont_driver): New font driver.
+       (Fandroid_enumerate_fonts): New function.
+       (syms_of_sfntfont_android_for_pdumper, init_sfntfont_android)
+       (syms_of_sfntfont_android): Initialize default fonts, special
+       family mapping and font driver.
+       * src/sfntfont.c (struct sfnt_font_desc): New fields
+       `char_cache', `cmap_invalid' and `subtable'.
+       (sfnt_setup_coding_system): Improve commentary.  Add default
+       branch.  Fix return value.
+       (sfnt_safe_encode_coding_object_1)
+       (sfnt_safe_encode_coding_object_2):
+       (sfnt_safe_encode_coding_object): Use decode_coding_object
+       instead of encode_coding_object.
+       (sfnt_decode_font_string): Adjust for rename.
+       (sfnt_decode_foundry_name): New function.
+       (sfnt_weight_descriptions, sfnt_slant_descriptions)
+       (sfnt_width_descriptions): Fix definitions.
+       (sfnt_parse_style): Make function work.
+       (sfnt_enum_font): Initialize designer, char-cache and subtable
+       platform ID.
+       (sfntfont_charset_for_name, mark_sfntfont)
+       (sfntfont_charset_for_cmap): New functions.
+       (syms_of_sfntfont): New variable `sfnt-default-family-alist'.
+
+       * src/sfntfont.h (_SFNTFONT_H_): Update prototypes.
+
+       * src/xdisp.c (gui_union_rectangles): New function.
+
+2023-01-08  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+       * configure.ac (ANDROID_OBJS): Add sfntfont files.
+
+       Check in new files
+       * src/sfnt.h:
+       * src/sfntfont-android.c:
+       * src/sfntfont.c:
+       * src/sfntfont.h: New files.
+
+2023-01-08  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       Note that the port currently does not work as of this check-in.
+
+       * src/android.c (android_change_gc): Fix situations where clip
+       rects are cleared.
+       (android_create_pixmap_from_bitmap_data): Fix bitmap data
+       iteration.
+       * src/androidfns.c (Fx_show_tip, Fx_hide_tip): Remove annoying
+       errors.
+
+       * src/androidgui.h (enum android_event_type):
+       (struct android_crossing_event):
+       (struct android_motion_event):
+       (struct android_button_event):
+       (union android_event): New crossing, motion and button events.
+
+       * src/androidterm.c (android_note_mouse_movement)
+       (mouse_or_wdesc_frame, android_construct_mouse_click)
+       (handle_one_android_event, android_mouse_position)
+       (android_wait_for_event, android_set_window_size_1)
+       (android_bitmap_icon, android_free_frame_resources)
+       (syms_of_androidterm): New functions.  Handle crossing, motion
+       and button events.
+
+       * src/androidterm.h (struct android_display_info): New field
+       `last_mouse_movement_time'.
+       (struct android_output): Remove unused `need_buffer_flip' field.
+
+       * src/emacs.c (android_emacs_init): Initialize sfntfont.
+       * src/frame.c (syms_of_frame): Set frame_inhibit_implied_resize
+       to some reasonable value.
+       * src/frame.h (GCALIGNED_STRUCT): Set wait_event_type on
+       Android.
+
+       * src/sfnt.c (eassert):
+       (TEST_STATIC):
+       (available):
+       (enum sfnt_table):
+       (sfnt_table_names):
+       (SFNT_ENDOF):
+       (struct sfnt_table_directory):
+       (enum sfnt_scaler_type):
+       (sfnt_coerce_fixed):
+       (struct sfnt_hhea_table):
+       (struct sfnt_cmap_table):
+       (enum sfnt_platform_id):
+       (enum sfnt_unicode_platform_specific_id):
+       (enum sfnt_macintosh_platform_specific_id):
+       (enum sfnt_microsoft_platform_specific_id):
+       (struct sfnt_cmap_encoding_subtable):
+       (struct sfnt_cmap_encoding_subtable_data):
+       (struct sfnt_cmap_format_0):
+       (struct sfnt_cmap_format_2_subheader):
+       (struct sfnt_cmap_format_2):
+       (struct sfnt_cmap_format_4):
+       (struct sfnt_cmap_format_6):
+       (struct sfnt_cmap_format_8_or_12_group):
+       (struct sfnt_cmap_format_8):
+       (struct sfnt_cmap_format_12):
+       (struct sfnt_maxp_table):
+       (struct sfnt_loca_table_short):
+       (struct sfnt_loca_table_long):
+       (struct sfnt_glyf_table):
+       (struct sfnt_simple_glyph):
+       (struct sfnt_compound_glyph_component):
+       (struct sfnt_compound_glyph):
+       (struct sfnt_glyph):
+       (sfnt_read_table_directory):
+       (file):
+       (sfnt_read_cmap_table):
+       (sfnt_read_head_table):
+       (success):
+       (sfnt_read_hhea_table):
+       (sfnt_read_loca_table_short):
+       (sfnt_read_loca_table_long):
+       (sfnt_read_maxp_table):
+       (sfnt_read_glyf_table):
+       (sfnt_read_compound_glyph):
+       (sfnt_read_glyph):
+       (struct sfnt_point):
+       (sfnt_expand_compound_glyph_context):
+       (sfnt_decompose_compound_glyph):
+       (struct sfnt_glyph_outline):
+       (enum sfnt_glyph_outline_flags):
+       (struct sfnt_build_glyph_outline_context):
+       (sfnt_build_append):
+       (sfnt_build_glyph_outline):
+       (struct sfnt_raster):
+       (struct sfnt_edge):
+       (sfnt_prepare_raster):
+       (sfnt_build_outline_edges):
+       (sfnt_raster_glyph_outline): Move structures to sfnt.h.
+
+       (struct sfnt_long_hor_metric):
+       (struct sfnt_hmtx_table):
+       (struct sfnt_glyph_metrics):
+       (sfnt_read_hmtx_table):
+       (sfnt_lookup_glyph_metrics):
+       (sfnt_read_name_table):
+       (sfnt_find_name):
+       (sfnt_read_meta_table):
+       (sfnt_find_metadata):
+       (sfnt_test_edge_ignore): New functions.
+       (main): Add new tests.
+       * src/xdisp.c (redisplay_tool_bar):
+
+2023-01-08  Po Lu  <luangruo@yahoo.com>
+
+       Delete unused files
+       * java/org/gnu/emacs/EmacsPaintQueue.java
+       * java/org/gnu/emacs/EmacsPaintReq.java: Remove files.
+
+       Update Java part of Android port
+       * java/org/gnu/emacs/EmacsCopyArea.java (EmacsCopyArea, perform)
+       (paintTo):
+       * java/org/gnu/emacs/EmacsDrawLine.java (EmacsDrawLine):
+       * java/org/gnu/emacs/EmacsDrawPoint.java (EmacsDrawPoint):
+       * java/org/gnu/emacs/EmacsDrawRectangle.java (EmacsDrawRectangle)
+       (paintTo):
+       * java/org/gnu/emacs/EmacsDrawable.java (EmacsDrawable):
+       * java/org/gnu/emacs/EmacsFillPolygon.java (EmacsFillPolygon):
+       * java/org/gnu/emacs/EmacsFillRectangle.java
+       (EmacsFillRectangle):
+       * java/org/gnu/emacs/EmacsFontDriver.java (EmacsFontDriver):
+       * java/org/gnu/emacs/EmacsGC.java (EmacsGC):
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative):
+       * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap):
+       * java/org/gnu/emacs/EmacsSdk23FontDriver.java
+       (EmacsSdk23FontDriver):
+       * java/org/gnu/emacs/EmacsSdk7FontDriver.java
+       (EmacsSdk7FontDriver, textExtents1, textExtents, draw):
+       * java/org/gnu/emacs/EmacsService.java (EmacsService, copyArea):
+       * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView):
+       * java/org/gnu/emacs/EmacsView.java (EmacsView, onLayout)
+       (onFocusChanged):
+       * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, run)
+       (resizeWindow, lockCanvas, getBitmap, onKeyDown, onKeyUp)
+       (onActivityDetached): Move rendering to main thread.  Make
+       drawing operations completely static.
+
+       Check in new file androidmenu.c
+       * src/androidmenu.c: New file.
+
+2023-01-07  Po Lu  <luangruo@yahoo.com>
+
+       Check in new file sfnt.c
+       * src/sfnt.c (xmalloc, xrealloc, xfree, eassert, MIN)
+       (sfnt_table_names, SFNT_ENDOF, struct sfnt_table_directory)
+       (enum sfnt_scaler_type, sfnt_coerce_fixed, struct sfnt_hhea_table)
+       (struct sfnt_cmap_table, enum sfnt_platform_id)
+       (enum sfnt_unicode_platform_specific_id)
+       (enum sfnt_macintosh_platform_specific_id)
+       (enum sfnt_microsoft_platform_specific_id)
+       (struct sfnt_cmap_encoding_subtable)
+       (struct sfnt_cmap_encoding_subtable_data)
+       (struct sfnt_cmap_format_0, struct sfnt_cmap_format_2_subheader)
+       (struct sfnt_cmap_format_2, struct sfnt_cmap_format_4)
+       (struct sfnt_cmap_format_6, struct sfnt_cmap_format_8_or_12_group)
+       (struct sfnt_cmap_format_8, struct sfnt_cmap_format_12)
+       (struct sfnt_maxp_table, struct sfnt_loca_table_short)
+       (struct sfnt_loca_table_long, struct sfnt_glyf_table)
+       (struct sfnt_simple_glyph, struct sfnt_compound_glyph_component)
+       (struct sfnt_compound_glyph, struct sfnt_glyph, _sfnt_swap16)
+       (_sfnt_swap32, sfnt_swap16, sfnt_find_table)
+       (sfnt_read_cmap_format_0, sfnt_read_cmap_format_2)
+       (sfnt_read_cmap_format_4, sfnt_read_cmap_format_6)
+       (sfnt_read_cmap_format_8, sfnt_read_cmap_format_12)
+       (sfnt_read_cmap_table_1, sfnt_read_cmap_table, sfnt_lookup_glyph_0)
+       (sfnt_lookup_glyph_2, sfnt_bsearch_above, sfnt_compare_uint16)
+       (sfnt_lookup_glyph_4, sfnt_lookup_glyph_6, sfnt_lookup_glyph_8)
+       (sfnt_lookup_glyph_12, sfnt_lookup_glyph, sfnt_read_head_table)
+       (sfnt_read_hhea_table, sfnt_read_loca_table_short)
+       (sfnt_read_loca_table_long, sfnt_read_maxp_table)
+       (sfnt_read_glyf_table, sfnt_read_simple_glyph)
+       (sfnt_read_compound_glyph, sfnt_read_glyph, sfnt_free_glyph)
+       (struct sfnt_point, sfnt_transform_coordinates)
+       (struct sfnt_compound_glyph_context)
+       (sfnt_expand_compound_glyph_context, sfnt_round_fixed)
+       (sfnt_decompose_compound_glyph, sfnt_lerp_half)
+       (sfnt_decompose_glyph, struct sfnt_glyph_outline)
+       (enum sfnt_glyph_outline_flags)
+       (struct sfnt_build_glyph_outline_context, sfnt_build_append)
+       (sfnt_move_to_and_build, sfnt_line_to_and_build, sfnt_mul_fixed)
+       (sfnt_div_fixed, sfnt_ceil_fixed, sfnt_floor_fixed)
+       (sfnt_curve_is_flat, sfnt_curve_to_and_build_1)
+       (sfnt_curve_to_and_build, sfnt_build_glyph_outline)
+       (struct sfnt_raster, struct sfnt_edge, sfnt_poly_coverage)
+       (sfnt_poly_grid_ceil, sfnt_prepare_raster, sfnt_step_edge_by)
+       (sfnt_build_outline_edges, sfnt_compare_edges, sfnt_poly_edges)
+       (sfnt_saturate_short, sfnt_fill_span, sfnt_poly_span)
+       (sfnt_raster_span, sfnt_raster_edge, sfnt_raster_glyph_outline)
+       (struct sfnt_long_hor_metric, struct sfnt_hmtx_table)
+       (struct sfnt_glyph_metrics, sfnt_read_hmtx_table)
+       (sfnt_lookup_glyph_metrics, struct sfnt_test_dcontext)
+       (sfnt_test_move_to, sfnt_test_line_to, sfnt_test_curve_to)
+       (sfnt_test_get_glyph, sfnt_test_free_glyph, sfnt_test_span)
+       (sfnt_test_edge, sfnt_test_raster, main): Check in
+       5000-line-long file written by me for reading TrueType and
+       OpenType fonts with TrueType outlines.
+
+2023-01-02  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2023-01-02  Po Lu  <luangruo@yahoo.com>
+
+       Update Android port
+       * Makefile.in (java): Depend on info.
+       (MAKEFILE_NAME):
+       (config.status): Remove unneeded changes.
+       * configure.ac (BUILD_DETAILS, ANDROID_STUBIFY): Don't require a
+       C++ compiler on Android.
+       * java/AndroidManifest.xml: <EmacsActivity>: Set launchMode
+       appropriately.  <EmacsMultitaskActivity>: New activity.
+       * java/Makefile.in (CROSS_BINS): Add EmacsClient.
+       * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity)
+       (onCreate): Use the window attachment manager.
+       * java/org/gnu/emacs/EmacsCopyArea.java (EmacsCopyArea)
+       (paintTo): Implement clip masks correctly.
+       * java/org/gnu/emacs/EmacsDrawRectangle.java (getRect, paintTo):
+       Fix damage tracking rectangles.
+       * java/org/gnu/emacs/EmacsFontDriver.java (FontSpec, toString):
+       New function.
+       (FontMetrics, EmacsFontDriver): Fix signature of textExtents.
+       * java/org/gnu/emacs/EmacsMultitaskActivity.java
+       (EmacsMultitaskActivity): New file.
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New
+       functions sendFocusIn, sendFocusOut, sendWindowAction.
+       * java/org/gnu/emacs/EmacsPaintQueue.java (run): Fix clipping
+       handling.
+       * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap): Add
+       constructor for mutable pixmaps.
+       * java/org/gnu/emacs/EmacsSdk23FontDriver.java
+       (EmacsSdk23FontDriver): New file.
+       * java/org/gnu/emacs/EmacsSdk7FontDriver.java
+       (EmacsSdk7FontDriver, Sdk7Typeface, Sdk7FontEntity, Sdk7FontObject)
+       (checkMatch, hasChar, encodeChar): Implement text display and
+       fix font metrics semantics.
+
+       * java/org/gnu/emacs/EmacsService.java (EmacsService): Remove
+       availableChildren.
+       (getLibraryDirectory, onCreate): Pass pixel density to Emacs.
+       (clearArea): Fix arguments.  Switch to using the window
+       attachment manager.
+       * java/org/gnu/emacs/EmacsSurfaceView.java (surfaceChanged)
+       (surfaceCreated): Flip buffers on surface attachment.
+       * java/org/gnu/emacs/EmacsView.java (EmacsView, swapBuffers):
+       New argument FORCE.  Always swap if it is true.
+       (onKeyMultiple, onFocusChanged): New functions.
+
+       * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, destroyHandle)
+       (run): Switch to using the window attachment manager.
+       * java/org/gnu/emacs/EmacsWindowAttachmentManager.java
+       (EmacsWindowAttachmentManager): New file.
+
+       * lisp/cus-edit.el (custom-button, custom-button-mouse)
+       (custom-button-pressed):
+       * lisp/faces.el (tool-bar): Define faces correctly on Android.
+       * src/android.c (struct android_emacs_pixmap): Add mutable
+       constructor.
+       (struct android_emacs_drawable): New structure.
+       (android_write_event): Check if event queue hasn't yet been
+       initialized.
+       (android_select): Set errno to EINTR if pselect fails.
+       (android_close): Remove unused debugging code.
+       (android_get_home_directory): New function.
+       (Java_org_gnu_emacs_EmacsNative_setEmacsParams): Set pixel
+       density and compute game path.
+       (android_init_emacs_drawable): New function.
+       (Java_org_gnu_emacs_EmacsNative_sendKeyPress): New argument
+       `unicode_char'.  Pass it in events.
+       (Java_org_gnu_emacs_EmacsNative_sendKeyRelease): Likewise.
+       (Java_org_gnu_emacs_EmacsNative_sendFocusIn)
+       (Java_org_gnu_emacs_EmacsNative_sendFocusOut)
+       (Java_org_gnu_emacs_EmacsNative_sendWindowAction): New
+       functions.
+       (android_resolve_handle): Export function.
+       (android_change_gc): Clear clip rects under the right
+       circumstances.  Set right clip mask field.
+       (android_create_pixmap_from_bitmap_data): Use correct alpha
+       channels.
+       (android_create_pixmap): Create mutable pixmap and avoid
+       redundant color array allocation.
+       (android_create_bitmap_from_data, android_create_image)
+       (android_destroy_image, android_put_pixel, android_get_pixel)
+       (android_get_image, android_put_image, faccessat): New
+       functions.
+
+       * src/android.h: Update prototypes.
+
+       * src/androidfns.c (android_default_font_parameter): Prefer
+       monospace to Droid Sans Mono.
+       * src/androidfont.c (struct android_emacs_font_driver): New
+       method `draw'.
+       (struct android_emacs_font_spec): New field `dpi'.
+       (struct androidfont_info): Add font metrics cache.
+       (android_init_font_driver, android_init_font_spec): Adjust
+       accordingly.
+       (androidfont_from_lisp, androidfont_from_java): Handle new
+       fields.
+       (androidfont_draw): Implement function.
+       (androidfont_open_font): Set pixel size correctly.
+       (androidfont_close_font): Free metrics cache.
+       (androidfont_cache_text_extents)
+       (androidfont_check_cached_extents): New functions.
+       (androidfont_text_extents): Cache glyph metrics somewhere for
+       future use.
+       (androidfont_list_family): Implement function.
+
+       * src/androidgui.h (enum android_event_type): New focus and
+       window action events.
+       (enum android_modifier_mask): New masks.
+       (struct android_key_event): New field `unicode_char'.
+       (ANDROID_IS_MODIFIER_KEY): Newmacro.
+       (struct android_focus_event, struct
+       android_window_action_event): New structs.
+       (union android_event): Add new fields.
+       (enum android_image_format, struct android_image): New enums and
+       structs.
+
+       * src/androidterm.c (android_android_to_emacs_modifiers)
+       (android_emacs_to_android_modifiers, android_lower_frame)
+       (android_raise_frame, android_new_focus_frame)
+       (android_focus_changed, android_detect_focus_change): New
+       functions.
+       (handle_one_android_event): Implement focus and key event
+       handling.
+       (android_frame_rehighlight): New function.
+       (android_frame_raise_lower): Implement accordingly.
+       (android_make_frame_invisible): Clear highlight_frame if
+       required.
+       (android_free_frame_resources): Clear x_focus_event_frame if
+       required.
+       (android_draw_fringe_bitmap, android_draw_image_foreground)
+       (android_draw_image_foreground_1)
+       (android_draw_image_glyph_string): Remove unnecessary code.
+       (android_create_terminal, android_term_init): Set the baud rate
+       to something sensible.
+       * src/androidterm.h (struct android_bitmap_record): Make
+       structure the same as on X.
+       (struct android_display_info): New focus tracking fields.
+       (struct android_output): Likewise.
+       * src/dispextern.h (struct image): Add ximg and mask_img on
+       Android.
+
+       * src/emacs.c (android_emacs_init): Fix argc sorting iteration.
+
+       * src/fileio.c (user_homedir):
+       (get_homedir): Implement correctly on Android.
+
+       * src/font.h (PT_PER_INCH): Define correctly on Android.
+
+       * src/fringe.c (X, swap_nibble, init_fringe_bitmap): Swap fringe
+       bitmaps correctly on Android.
+
+       * src/image.c (GET_PIXEL, image_create_bitmap_from_data)
+       (image_create_bitmap_from_file, free_bitmap_record)
+       (image_unget_x_image_or_dc, struct image_type)
+       (prepare_image_for_display, image_clear_image_1)
+       (image_size_in_bytes, x_check_image_size)
+       (x_create_x_image_and_pixmap, x_destroy_x_image)
+       (image_check_image_size, image_create_x_image_and_pixmap_1)
+       (image_destroy_x_image, gui_put_x_image, image_put_x_image)
+       (image_get_x_image, image_unget_x_image)
+       (Create_Pixmap_From_Bitmap_Data, image_pixmap_draw_cross)
+       (MaskForeground, image_types, syms_of_image): Implement all of
+       the above on Android in terms of an API very similar to X.
+
+       * src/keyboard.c (FUNCTION_KEY_OFFSET, lispy_function_keys):
+       Define on Android to something sensible.
+
+       * src/lread.c (build_load_history): Fix problem.
+
+2022-12-31  Po Lu  <luangruo@yahoo.com>
+
+       Merge remote-tracking branch 'origin/master' into feature/android
+
+2022-12-31  Po Lu  <luangruo@yahoo.com>
+
+       Bring up the Android operating system and its window system
+       * .dir-locals.el (c-mode): Add ANDROID_EXPORT noise macro.
+       * .gitignore: Add new files to ignore.
+       * Makefile.in: Adjust for Android.
+       * admin/merge-gnulib: Add new warning.
+       * configure.ac: Detect Android.  Run cross-configuration for
+       Android when appropriate.
+
+       * etc/DEBUG: Document how to debug Emacs on Android.
+
+       * java/AndroidManifest.xml:
+       * java/Makefile.in:
+       * java/README:
+       * java/debug.sh:
+       * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity):
+       * java/org/gnu/emacs/EmacsApplication.java (EmacsApplication):
+       * java/org/gnu/emacs/EmacsCopyArea.java (EmacsCopyArea):
+       * java/org/gnu/emacs/EmacsDrawLine.java (EmacsDrawLine):
+       * java/org/gnu/emacs/EmacsDrawPoint.java (EmacsDrawPoint):
+       * java/org/gnu/emacs/EmacsDrawRectangle.java
+       (EmacsDrawRectangle):
+       * java/org/gnu/emacs/EmacsDrawable.java (EmacsDrawable):
+       * java/org/gnu/emacs/EmacsFillPolygon.java (EmacsFillPolygon):
+       * java/org/gnu/emacs/EmacsFillRectangle.java
+       (EmacsFillRectangle):
+       * java/org/gnu/emacs/EmacsFontDriver.java (EmacsFontDriver):
+       * java/org/gnu/emacs/EmacsGC.java (EmacsGC):
+       * java/org/gnu/emacs/EmacsHandleObject.java (EmacsHandleObject):
+       * java/org/gnu/emacs/EmacsNative.java (EmacsNative):
+       * java/org/gnu/emacs/EmacsPaintQueue.java (EmacsPaintQueue):
+       * java/org/gnu/emacs/EmacsPaintReq.java (EmacsPaintReq):
+       * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap):
+       * java/org/gnu/emacs/EmacsSdk7FontDriver.java
+       (EmacsSdk7FontDriver):
+       * java/org/gnu/emacs/EmacsService.java (class Holder<T>)
+       (EmacsService):
+       * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView):
+       * java/org/gnu/emacs/EmacsThread.java (EmacsThread):
+       * java/org/gnu/emacs/EmacsView.java (EmacsView):
+       * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): New files
+       and classes.
+
+       * lib-src/Makefile.in (srcdir):
+       * lib/Makefile.in (VPATH):
+       (HAVE_NATIVE_COMP):
+       (libgnu_a_SOURCES):
+       (DEPFLAGS): Configure correctly for cross-compiling.
+
+       * lib/faccessat.c:
+       * lib/fpending.c (__fpending):
+       * lib/open.c:
+       * lib/unistd.c (_GL_UNISTD_INLINE): Temporary adjustments to
+       gnulib.
+
+       * lisp/frame.el (display-graphic-p):
+       (display-screens):
+       (display-pixel-height):
+       (display-pixel-width):
+       (display-mm-height):
+       (display-mm-width):
+       (display-backing-store):
+       (display-save-under):
+       (display-planes):
+       (display-color-cells):
+       (display-visual-class): Adjust for new window system `android'.
+
+       * lisp/image/wallpaper.el (x-open-connection): Add declaration.
+       * lisp/loadup.el (featurep): Load up files for Android.
+       * lisp/net/eww.el (eww-form-submit, eww-form-file)
+       (eww-form-checkbox, eww-form-select): Adjust faces for android.
+       * lisp/term/android-win.el: New file.
+       * src/Makefile.in: Add new targets emacs.so and android-emacs,
+       then adjust for cross compilation.
+       * src/alloc.c (cleanup_vector): Clean up Android font entities
+       as well.
+       (garbage_collect): Mark androidterm.
+       * src/android-emacs.c (main):
+       * src/android.c (ANDROID_THROW, enum android_fd_table_entry_flags)
+       (struct android_emacs_service, struct android_emacs_pixmap)
+       (struct android_graphics_point, struct android_event_container)
+       (struct android_event_queue, android_run_select_thread)
+       (android_handle_sigusr1, android_init_events, android_pending)
+       (android_next_event, android_write_event, android_select)
+       (android_run_debug_thread, android_user_full_name)
+       (android_get_asset_name, android_fstat, android_fstatat)
+       (android_file_access_p, android_hack_asset_fd, android_open)
+       (android_close, JNICALL, android_init_emacs_service)
+       (android_init_emacs_pixmap, android_init_graphics_point)
+       (MAX_HANDLE, struct android_handle_entry, android_alloc_id)
+       (android_destroy_handle, android_resolve_handle)
+       (android_resolve_handle2, android_change_window_attributes)
+       (android_create_window, android_set_window_background)
+       (android_destroy_window, android_init_android_rect_class)
+       (android_init_emacs_gc_class, android_create_gc, android_free_gc)
+       (android_change_gc, android_set_clip_rectangles)
+       (android_reparent_window, android_lookup_method)
+       (android_clear_window, android_map_window, android_unmap_window)
+       (android_resize_window, android_move_window, android_swap_buffers)
+       (android_get_gc_values, android_set_foreground)
+       (android_fill_rectangle, android_create_pixmap_from_bitmap_data)
+       (android_set_clip_mask, android_set_fill_style, android_copy_area)
+       (android_free_pixmap, android_set_background, android_fill_polygon)
+       (android_draw_rectangle, android_draw_point, android_draw_line)
+       (android_create_pixmap, android_set_ts_origin, android_clear_area):
+       * src/android.h (ANDROID_EXPORT):
+       * src/androidfns.c (android_display_info_for_name)
+       (check_android_display_info, check_x_display_info, gamma_correct)
+       (android_defined_color, android_decode_color)
+       (android_implicitly_set_name, android_explicitly_set_name)
+       (android_set_tool_bar_lines, android_change_tool_bar_height)
+       (android_set_tab_bar_lines, android_change_tab_bar_height)
+       (android_set_scroll_bar_default_height)
+       (android_set_scroll_bar_default_width, android_icon_verify)
+       (android_icon, android_make_gc, android_free_gcs)
+       (unwind_create_frame, do_unwind_create_frame)
+       (android_default_font_parameter, android_create_frame_window)
+       (Fx_create_frame, Fxw_color_defined_p, Fxw_color_values)
+       (Fxw_display_color_p, Fx_display_grayscale_p)
+       (Fx_display_pixel_width, Fx_display_pixel_height)
+       (Fx_display_planes, Fx_display_color_cells, Fx_display_screens)
+       (Fx_display_mm_width, Fx_display_mm_height)
+       (Fx_display_backing_store, Fx_display_visual_class)
+       (Fx_display_monitor_attributes_list, Fx_frame_geometry)
+       (Fx_frame_list_z_order, Fx_frame_restack)
+       (Fx_mouse_absolute_pixel_position)
+       (Fx_set_mouse_absolute_pixel_position, Fandroid_get_connection)
+       (Fx_display_list, Fx_show_tip, Fx_hide_tip)
+       (android_set_background_color, android_set_border_color)
+       (android_set_cursor_color, android_set_cursor_type)
+       (android_set_foreground_color)
+       (android_set_child_frame_border_width)
+       (android_set_internal_border_width, android_set_menu_bar_lines)
+       (android_set_mouse_color, android_set_title, android_set_alpha)
+       (android_frame_parm_handlers, syms_of_androidfns):
+       * src/androidfont.c (struct android_emacs_font_driver)
+       (struct android_emacs_font_spec, struct android_emacs_font_metrics)
+       (struct android_emacs_font_object, struct android_integer)
+       (struct androidfont_info, struct androidfont_entity)
+       (android_init_font_driver, android_init_font_spec)
+       (android_init_font_metrics, android_init_integer)
+       (android_init_font_object, androidfont_get_cache)
+       (androidfont_from_lisp, androidfont_from_java, androidfont_list)
+       (androidfont_match, androidfont_draw, androidfont_open_font)
+       (androidfont_close_font, androidfont_has_char)
+       (androidfont_encode_char, androidfont_text_extents)
+       (androidfont_list_family, androidfont_driver)
+       (syms_of_androidfont_for_pdumper, syms_of_androidfont)
+       (init_androidfont, android_finalize_font_entity):
+       * src/androidgui.h (_ANDROID_GUI_H_, struct android_rectangle)
+       (struct android_point, enum android_gc_function)
+       (enum android_gc_value_mask, enum android_fill_style)
+       (enum android_window_value_mask)
+       (struct android_set_window_attributes, struct android_gc_values)
+       (struct android_gc, enum android_swap_action, enum android_shape)
+       (enum android_coord_mode, struct android_swap_info)
+       (NativeRectangle, struct android_any_event)
+       (struct android_key_event, struct android_configure_event)
+       (union android_event):
+       * src/androidterm.c (android_window_to_frame, android_clear_frame)
+       (android_ring_bell, android_toggle_invisible_pointer)
+       (android_update_begin, android_update_end, show_back_buffer)
+       (android_flush_dirty_back_buffer_on, handle_one_android_event)
+       (android_read_socket, android_frame_up_to_date)
+       (android_buffer_flipping_unblocked_hook)
+       (android_query_frame_background_color, android_parse_color)
+       (android_alloc_nearest_color, android_query_colors)
+       (android_mouse_position, android_get_focus_frame)
+       (android_focus_frame, android_frame_rehighlight)
+       (android_frame_raise_lower, android_make_frame_visible)
+       (android_make_frame_invisible)
+       (android_make_frame_visible_invisible, android_fullscreen_hook)
+       (android_iconify_frame, android_set_window_size_1)
+       (android_set_window_size, android_set_offset, android_set_alpha)
+       (android_new_font, android_bitmap_icon, android_free_pixmap_hook)
+       (android_free_frame_resources, android_delete_frame)
+       (android_delete_terminal, android_scroll_run)
+       (android_after_update_window_line, android_flip_and_flush)
+       (android_clear_rectangle, android_reset_clip_rectangles)
+       (android_clip_to_row, android_draw_fringe_bitmap)
+       (android_set_cursor_gc, android_set_mouse_face_gc)
+       (android_set_mode_line_face_gc, android_set_glyph_string_gc)
+       (android_set_glyph_string_clipping)
+       (android_set_glyph_string_clipping_exactly)
+       (android_compute_glyph_string_overhangs)
+       (android_clear_glyph_string_rect)
+       (android_draw_glyph_string_background, android_fill_triangle)
+       (android_make_point, android_inside_rect_p, android_clear_point)
+       (android_draw_relief_rect, android_draw_box_rect)
+       (HIGHLIGHT_COLOR_DARK_BOOST_LIMIT, android_setup_relief_color)
+       (android_setup_relief_colors, android_draw_glyph_string_box)
+       (android_draw_glyph_string_bg_rect, android_draw_image_relief)
+       (android_draw_image_foreground, android_draw_image_foreground_1)
+       (android_draw_image_glyph_string)
+       (android_draw_stretch_glyph_string, android_draw_underwave)
+       (android_draw_glyph_string_foreground)
+       (android_draw_composite_glyph_string_foreground)
+       (android_draw_glyphless_glyph_string_foreground)
+       (android_draw_glyph_string, android_define_frame_cursor)
+       (android_clear_frame_area, android_clear_under_internal_border)
+       (android_draw_hollow_cursor, android_draw_bar_cursor)
+       (android_draw_window_cursor, android_draw_vertical_window_border)
+       (android_draw_window_divider, android_redisplay_interface)
+       (frame_set_mouse_pixel_position, get_keysym_name)
+       (android_create_terminal, android_term_init, syms_of_androidterm)
+       (mark_androidterm):
+       * src/androidterm.h (_ANDROID_TERM_H_, struct android_display_info)
+       (struct android_output, FRAME_ANDROID_OUTPUT, XSCROLL_BAR): New
+       files.
+       * src/dired.c (file_attributes): Do not use openat on Android.
+       * src/dispextern.h (No_Cursor): Define appropriately on Android.
+       (struct glyph_string, struct face): Make gc field of type struct
+       android_gc on Android.
+       * src/dispnew.c (clear_current_matrices, clear_desired_matrices)
+       (adjust_frame_glyphs_for_window_redisplay, free_glyphs)
+       (update_frame, scrolling, char_ins_del_cost, update_frame_line)
+       (init_display_interactive): Disable text terminal support
+       completely on Android.  Fix non-toolkit menus for non-X systems.
+       * src/editfns.c (Fuser_full_name): Call android_user_full_name.
+       * src/emacs.c (android_emacs_init): Make main this on Android.
+       Prohibit argv sorting from exceeding end of argv.
+       * src/epaths.in: Add path definitions for Android.
+
+       * src/fileio.c (file_access_p): Call android_file_access_p.
+       (file_name_directory): Avoid using openat on Android.
+       (Fcopy_file): Adjust to call sys_fstat instead.
+       (file_directory_p):
+       (Finsert_file_contents):
+       (write_region): Likewise.
+       * src/filelock.c:
+       * src/fns.c (Flocale_info): Pacify warning on Android.
+       * src/font.c (font_make_entity_android): New function.
+       * src/font.h:
+       * src/frame.c (Fframep):
+       (Fwindow_system): Handle new window system `android'.  Update doc 
strings.
+       (Fmake_terminal_frame): Disable on Android.
+       (gui_display_get_resource): Disable get_string_resource_hook on Android.
+       (syms_of_frame): New defsym `android'.
+
+       * src/frame.h (GCALIGNED_STRUCT): Add new output data for
+       Android.
+       (ENUM_BF): Expand enumerator size.
+       (FRAME_ANDROID_P, FRAME_WINDOW_P, MOUSE_HL_INFO): Add
+       definitions for Android.
+
+       * src/image.c (GET_PIXEL):
+       (image_create_bitmap_from_file):
+       (image_create_x_image_and_pixmap_1):
+       (image_get_x_image):
+       (slurp_file):
+       (lookup_rgb_color):
+       (image_to_emacs_colors):
+       (image_from_emacs_colors):
+       (image_pixmap_draw_cross):
+       (image_disable_image):
+       (MaskForeground):
+       (gif_load): Add stubs for Android.
+
+       * src/lisp.h:
+       * src/lread.c (safe_to_load_version, maybe_swap_for_eln1, openp):
+       * src/pdumper.c (pdumper_load): Call sys_fstat instead of fstat.
+       * src/process.c (wait_reading_process_output): Use
+       android_select instead of pselect.
+       * src/scroll.c: Disable on Android.
+       * src/sysdep.c (widen_foreground_group, reset_sys_modes)
+       (init_signals, emacs_fstatat, sys_fstat): New function.
+       (emacs_open, emacs_open_noquit, emacs_close): Implement
+       differently on Android.
+       (close_output_streams): Disable what is not required on Android.
+
+       * src/term.c (OUTPUT1_IF, encode_terminal_code, string_cost)
+       (string_cost_one_line, per_line_cost, calculate_costs)
+       (struct fkey_table, tty_append_glyph, produce_glyphs)
+       (tty_capable_p, Fsuspend_tty, Fresume_tty, device, init_tty)
+       (maybe_fatal, syms_of_term): Disable text terminal support on
+       Android.
+
+       * src/termhooks.h (enum output_method): Add android output
+       method.
+       (GCALIGNED_STRUCT, TERMINAL_FONT_CACHE): Define for Android.
+
+       * src/terminal.c (Fterminal_live_p): Implement for Android.
+
+       * src/verbose.mk.in (AM_V_GLOBALS): Add JAVAC and DX.
+       * src/xdisp.c (redisplay_internal): Disable text terminals on Android.
+       (display_menu_bar):
+       (display_tty_menu_item):
+       (draw_row_with_mouse_face):
+       (expose_frame): Make the non toolkit menu bar work on Android.
+
+       * src/xfaces.c (GCGraphicsExposures):
+       (x_create_gc):
+       (x_free_gc):
+       (Fx_load_color_file): Define for Android.
+
+       * xcompile/Makefile.in (top_srcdir):
+       (top_builddir):
+       * xcompile/README:
+       * xcompile/langinfo.h (nl_langinfo): New files.
+
+This ChangeLog only chronicles changes constituting the initial
+development of the Android port.  Refer to other ChangeLog files for a
+narrative of unrelated modifications made to Emacs during that time,
+and those made after the Android port was installed.
+
+;; Local Variables:
+;; coding: utf-8
+;; End:
+
+  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/>.
diff --git a/INSTALL b/INSTALL
index 2bb8df52dc9..605be366e92 100644
--- a/INSTALL
+++ b/INSTALL
@@ -5,9 +5,9 @@ See the end of the file for license conditions.
 
 
 This file contains general information on building GNU Emacs.  For
-more information specific to the MS-Windows, GNUstep/macOS, and MS-DOS
-ports, also read the files nt/INSTALL, nextstep/INSTALL, and
-msdos/INSTALL.
+more information specific to the MS-Windows, GNUstep/macOS, MS-DOS,
+and Android ports, also read the files nt/INSTALL, nextstep/INSTALL,
+msdos/INSTALL, and java/INSTALL.
 
 For information about building from a Git checkout (rather than an
 Emacs release), read the INSTALL.REPO file first.
diff --git a/Makefile.in b/Makefile.in
index 729cd4140e5..b47e88f6970 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -106,15 +106,15 @@ top_builddir = @top_builddir@
 
 FIND_DELETE = @FIND_DELETE@
 
-HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@
-
 USE_STARTUP_NOTIFICATION = @USE_STARTUP_NOTIFICATION@
 
+HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@
 HAVE_BE_APP = @HAVE_BE_APP@
-
 HAVE_PGTK = @HAVE_PGTK@
 HAVE_GSETTINGS = @HAVE_GSETTINGS@
 
+ANDROID = @ANDROID@
+
 # ==================== Where To Install Things ====================
 
 # Location to install Emacs.app under GNUstep / macOS.
@@ -339,6 +339,10 @@ EMACS_PDMP = `./src/emacs${EXEEXT} --fingerprint`.pdmp
 # Subdirectories to make recursively.
 SUBDIR = $(NTDIR) lib lib-src src lisp
 
+ifeq ($(ANDROID),yes)
+SUBDIR := $(SUBDIR) java
+endif
+
 # The subdir makefiles created by config.status.
 SUBDIR_MAKEFILES_IN = @SUBDIR_MAKEFILES_IN@
 SUBDIR_MAKEFILES = $(patsubst ${srcdir}/%,%,${SUBDIR_MAKEFILES_IN:.in=})
@@ -467,20 +471,20 @@ epaths-force:
          esac; \
        done
        @(gamedir='${gamedir}'; \
-         sed < ${srcdir}/src/epaths.in > epaths.h.$$$$         \
-         -e 's;\(#.*PATH_LOADSEARCH\).*$$;\1 "${standardlisppath}";' \
-         -e 's;\(#.*PATH_REL_LOADSEARCH\).*$$;\1 "${lispdirrel}";'     \
-         -e 's;\(#.*PATH_SITELOADSEARCH\).*$$;\1 "${locallisppath}";' \
-         -e 's;\(#.*PATH_DUMPLOADSEARCH\).*$$;\1 "${buildlisppath}";' \
-         -e '/^#define PATH_[^ ]*SEARCH /s/\([":]\):*/\1/g'            \
-         -e '/^#define PATH_[^ ]*SEARCH /s/:"/"/'                      \
-         -e 's;\(#.*PATH_EXEC\).*$$;\1 "${archlibdir}";'               \
-         -e 's;\(#.*PATH_INFO\).*$$;\1 "${infodir}";'                  \
-         -e 's;\(#.*PATH_DATA\).*$$;\1 "${etcdir}";'                   \
-         -e 's;\(#.*PATH_BITMAPS\).*$$;\1 "${bitmapdir}";'             \
-         -e 's;\(#.*PATH_X_DEFAULTS\).*$$;\1 "${x_default_search_path}";' \
-         -e 's;\(#.*PATH_GAME\).*$$;\1 $(PATH_GAME);'                  \
-         -e 's;\(#.*PATH_DOC\).*$$;\1 "${etcdocdir}";') &&             \
+         sed < ${srcdir}/src/epaths.in > epaths.h.$$$$                         
        \
+         -e 's;\(#define.*PATH_LOADSEARCH\).*$$;\1 "${standardlisppath}";'     
        \
+         -e 's;\(#define.*PATH_REL_LOADSEARCH\).*$$;\1 "${lispdirrel}";'       
        \
+         -e 's;\(#define.*PATH_SITELOADSEARCH\).*$$;\1 "${locallisppath}";'    
        \
+         -e 's;\(#define.*PATH_DUMPLOADSEARCH\).*$$;\1 "${buildlisppath}";'    
        \
+         -e '/^#define PATH_[^ ]*SEARCH /s/\([":]\):*/\1/g'                    
        \
+         -e '/^#define PATH_[^ ]*SEARCH /s/:"/"/'                              
        \
+         -e 's;\(#define.*PATH_EXEC\).*$$;\1 "${archlibdir}";'                 
        \
+         -e 's;\(#define.*PATH_INFO\).*$$;\1 "${infodir}";'                    
        \
+         -e 's;\(#define.*PATH_DATA\).*$$;\1 "${etcdir}";'                     
        \
+         -e 's;\(#define.*PATH_BITMAPS\).*$$;\1 "${bitmapdir}";'               
        \
+         -e 's;\(#define.*PATH_X_DEFAULTS\).*$$;\1 
"${x_default_search_path}";'        \
+         -e 's;\(#define.*PATH_GAME\).*$$;\1 $(PATH_GAME);'                    
        \
+         -e 's;\(#define.*PATH_DOC\).*$$;\1 "${etcdocdir}";') &&               
        \
        ${srcdir}/build-aux/move-if-change epaths.h.$$$$ src/epaths.h
 
 # The w32 build needs a slightly different editing, and it uses
@@ -532,6 +536,12 @@ lisp: src
 lib lib-src lisp nt: Makefile
        $(MAKE) -C $@ all
 
+java: lisp info
+       $(MAKE) -C $@ all
+
+cross: src
+       $(MAKE) -C $@ all
+
 trampolines: src lisp
 ifeq ($(HAVE_NATIVE_COMP),yes)
        $(MAKE) -C lisp trampolines
@@ -569,10 +579,13 @@ $(MAKEFILE_NAME): config.status $(srcdir)/configure \
 # Don't erase these files if make is interrupted while refreshing them.
 .PRECIOUS: Makefile config.status
 
+# Note that calling config.status --recheck is insufficient on Android
+# due to the recursive calls to configure.
+
 config.status: ${srcdir}/configure
-       if [ -x ./config.status ]; then \
+       if [ -x ./config.status ]; then         \
            $(CFG) ./config.status --recheck;   \
-       else                            \
+       else                                    \
            $(CFG) $(srcdir)/configure $(CONFIGURE_FLAGS); \
        fi
 
@@ -987,6 +1000,12 @@ endef
 mostlyclean_dirs = src oldXMenu lwlib lib lib-src nt doc/emacs doc/misc \
   doc/lispref doc/lispintro test
 
+### Add the libexec directory to mostlyclean_dirs if its Makefile has
+### been created.
+ifneq ($(wildcard exec/Makefile),)
+mostlyclean_dirs := $(mostlyclean_dirs) exec
+endif
+
 $(foreach dir,$(mostlyclean_dirs),$(eval $(call 
submake_template,$(dir),mostlyclean)))
 
 mostlyclean: $(mostlyclean_dirs:=_mostlyclean)
@@ -999,7 +1018,8 @@ mostlyclean: $(mostlyclean_dirs:=_mostlyclean)
 ###      with them.
 ###
 ###      Delete '.dvi' files here if they are not part of the distribution.
-clean_dirs = $(mostlyclean_dirs) nextstep admin/charsets admin/unidata
+clean_dirs = $(mostlyclean_dirs) java cross nextstep admin/charsets \
+  admin/unidata
 
 $(foreach dir,$(clean_dirs),$(eval $(call submake_template,$(dir),clean)))
 
@@ -1081,6 +1101,8 @@ extraclean: maintainer-clean
        -[ "${srcdir}" = "." ] || \
          find ${srcdir} '(' -name '*~' -o -name '#*' ')' ${FIND_DELETE}
        -find . '(' -name '*~' -o -name '#*' ')' ${FIND_DELETE}
+       -rm -f ${srcdir}/exec/config-tmp-* ${srcdir}/exec/aclocal.m4 \
+         ${srcdir}/src/config.in ${srcdir}/exec/configure
 
 # The src subdir knows how to do the right thing
 # even when the build directory and source dir are different.
diff --git a/README b/README
index 19d5c96e348..694b3aa4c94 100644
--- a/README
+++ b/README
@@ -95,6 +95,11 @@ 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.
+'exec'     holds the source code to several helper executables used to run
+           user-installed programs on Android.
 
    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/admin/merge-gnulib b/admin/merge-gnulib
index eb790933414..b533f69cceb 100755
--- a/admin/merge-gnulib
+++ b/admin/merge-gnulib
@@ -37,7 +37,7 @@ GNULIB_MODULES='
   fchmodat fcntl fcntl-h fdopendir file-has-acl
   filemode filename filevercmp flexmember fpieee
   free-posix fstatat fsusage fsync futimens
-  getloadavg getopt-gnu getrandom gettime gettimeofday gitlog-to-changelog
+  getline getloadavg getopt-gnu getrandom gettime gettimeofday 
gitlog-to-changelog
   ieee754-h ignore-value intprops largefile libgmp lstat
   manywarnings memmem-simple mempcpy memrchr memset_explicit
   minmax mkostemp mktime
@@ -45,7 +45,7 @@ GNULIB_MODULES='
   pathmax pipe2 pselect pthread_sigmask
   qcopy-acl readlink readlinkat regex
   sig2str sigdescr_np socklen stat-time std-gnu11 stdbool stdckdint stddef 
stdio
-  stpcpy strnlen strtoimax symlink sys_stat sys_time
+  stpcpy stpncpy strnlen strnlen strtoimax symlink sys_stat sys_time
   tempname time-h time_r time_rz timegm timer-time timespec-add timespec-sub
   update-copyright unlocked-io utimensat
   vla warnings year2038
@@ -114,6 +114,11 @@ for module in $AVOIDED_MODULES; do
   avoided_flags="$avoided_flags --avoid=$module"
 done
 
+# Clean the lib directory as well.
+if [ -e "$src"/lib/Makefile ]; then
+   make -C "$src"/lib maintainer-clean
+fi
+
 "$gnulib_srcdir"/gnulib-tool --dir="$src" $GNULIB_TOOL_FLAGS \
        $avoided_flags $GNULIB_MODULES &&
 rm -- "$src"lib/gl_openssl.h \
diff --git a/admin/notes/unicode b/admin/notes/unicode
index 31c850af8fd..b4f23f68def 100644
--- a/admin/notes/unicode
+++ b/admin/notes/unicode
@@ -72,7 +72,10 @@ characters are those marked with W or F in that file.  
Zero-width
 characters are not taken from EastAsianWidth.txt, they are those whose
 Unicode General Category property is one of Mn, Me, or Cf, and also
 Hangul jungseong and jongseong characters (a.k.a. "Jamo medial vowels"
-and "Jamo final consonants").
+and "Jamo final consonants").  The list of "ambiguous-width
+characters" recorded in the ambiguous-width-chars table (around line
+1400 of characters.el) should also be checked against the latest
+Unicode data in EastAsianWidth.txt.
 
 Any new scripts added by UnicodeData.txt will also need updates to
 script-representative-chars defined in fontset.el, and also the list
diff --git a/autogen.sh b/autogen.sh
index 6127e7b24f4..3cb77afedb3 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -256,6 +256,12 @@ Please report any problems with this script to 
bug-gnu-emacs@gnu.org .'
   ## Let autoreconf figure out what, if anything, needs doing.
   ## Use autoreconf's -f option in case autoreconf itself has changed.
   autoreconf -fi -I m4 || exit
+
+  echo "Running 'autoreconf -fi' in exec ..."
+
+  # Now, run autoreconf inside the exec directory to generate its
+  # configure script.
+  autoreconf -fi exec || exit
 fi
 
 
diff --git a/build-aux/makecounter.sh b/build-aux/makecounter.sh
new file mode 100755
index 00000000000..3bebd288031
--- /dev/null
+++ b/build-aux/makecounter.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+# Generate or update a C file containing an increasing counter
+# variable.
+#
+# 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/>.
+
+set -e
+
+curcount=
+if test -f "$1"; then
+    curcount=`cat "$1" | grep = | cut -d= -f2 \
+                 | sed -e 's/;//' -e 's/ //'`
+fi
+
+curcount=`expr 1 + $curcount 2>/dev/null || echo 0`
+
+cat > $1 <<EOF
+/* Generated automatically by makecounter.sh.  Do not edit! */
+
+#include <config.h>
+
+#ifdef HAVE_ANDROID
+#define EXPORT __attribute__ ((visibility ("default")))
+#endif /* HAVE_ANDROID */
+
+#ifdef EXPORT
+EXPORT
+#endif /* EXPORT */
+int emacs_shortlisp_counter = $curcount;
+EOF
diff --git a/build-aux/ndk-build-helper-1.mk b/build-aux/ndk-build-helper-1.mk
new file mode 100644
index 00000000000..2cde5146301
--- /dev/null
+++ b/build-aux/ndk-build-helper-1.mk
@@ -0,0 +1,112 @@
+# 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)
+NDK_$(LOCAL_MODULE)_EXPORT_INCLUDES := $(LOCAL_EXPORT_C_INCLUDE_DIRS) 
$(LOCAL_EXPORT_C_INCLUDES)
+NDK_CXX_FLAG_$(LOCAL_MODULE) :=
+
+$(info Building $(build_kind))
+$(info $(LOCAL_MODULE))
+$(info $(addprefix $(LOCAL_PATH)/,$(LOCAL_SRC_FILES) 
$(LOCAL_SRC_FILES$(EMACS_ABI))))
+
+ifeq ($(findstring lib,$(LOCAL_MODULE)),lib)
+NDK_SO_NAMES = $(LOCAL_MODULE)_emacs.so
+else
+NDK_SO_NAMES = lib$(LOCAL_MODULE)_emacs.so
+endif
+
+define add-so-name-1
+# Now recurse over this module's dependencies.
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), 
$$(NDK_$(1)_SHARED_LIBRARIES)),$$(eval $$(call add-so-name,$$(module))))
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), 
$$(NDK_$(1)_STATIC_LIBRARIES)),$$(eval $$(call add-so-name-1,$$(module))))
+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 ($$(findstring $$(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)_SHARED_LIBRARIES)),$$(eval $$(call add-so-name,$$(module))))
+
+# Recurse over static library dependencies of this shared library.
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), 
$$(NDK_$(1)_STATIC_LIBRARIES) $$(NDK_$(1)_WHOLE_LIBRARIES)),$$(eval $$(call 
add-so-name-1,$$(module))))
+endif
+
+ifneq ($$(findstring stdc++,$$(NDK_$(1)_SHARED_LIBRARIES)),)
+NDK_CXX_FLAG_$(LOCAL_MODULE) := yes
+endif
+endef
+
+# Figure out includes from dependencies as well.
+NDK_INCLUDES := $(LOCAL_EXPORT_C_INCLUDE_DIRS) $(LOCAL_EXPORT_C_INCLUDES)
+
+define add-includes
+ifeq ($$(findstring $$(NDK_$(1)_EXPORT_INCLUDES),$$(NDK_INCLUDES)),)
+NDK_INCLUDES += $$(NDK_$(1)_EXPORT_INCLUDES)
+
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), 
$$(NDK_$(1)_SHARED_LIBRARIES)) $$(NDK_$(1)_STATIC_LIBRARIES),$$(eval $$(call 
add-includes,$$(module))))
+
+# Recurse over shared library dependencies of this static library.
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), 
$$(NDK_$(1)_SHARED_LIBRARIES)),$$(eval $$(call add-so-name,$$(module))))
+
+# Recurse over static or shared library dependencies of this static
+# library.
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), 
$$(NDK_$(1)_STATIC_LIBRARIES)),$$(eval $$(call add-so-name-1,$$(module))))
+endif
+endef
+
+# Resolve additional dependencies and their export includes based on
+# LOCAL_STATIC_LIBRARIES and LOCAL_SHARED_LIBRARIES.  Static library
+# dependencies can be ignored while building a shared library, as they
+# will be linked in to the resulting shared object file later.
+
+SYSTEM_LIBRARIES = z libz libc c libdl dl stdc++ libstdc++ log liblog android 
libandroid
+
+$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), 
$(LOCAL_SHARED_LIBRARIES)),$(eval $(call add-so-name,$(module))))
+$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), $(LOCAL_SHARED_LIBRARIES) 
$(LOCAL_STATIC_LIBRARIES) $(LOCAL_WHOLE_STATIC_LIBRARIES)),$(eval $(call 
add-includes,$(module))))
+
+ifneq ($(findstring stdc++,$(LOCAL_SHARED_LIBRARIES)),)
+NDK_CXX_FLAG_$(LOCAL_MODULE) := yes
+endif
+
+$(info $(foreach dir,$(NDK_INCLUDES),-I$(dir)))
+$(info $(LOCAL_EXPORT_CFLAGS))
+
+$(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 $(NDK_CXX_FLAG_$(LOCAL_MODULE)))
+$(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..186f3aec333
--- /dev/null
+++ b/build-aux/ndk-build-helper-2.mk
@@ -0,0 +1,105 @@
+# 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)
+NDK_$(LOCAL_MODULE)_EXPORT_INCLUDES := $(LOCAL_EXPORT_C_INCLUDE_DIRS) 
$(LOCAL_EXPORT_C_INCLUDES)
+NDK_CXX_FLAG_$(LOCAL_MODULE) :=
+
+$(info Building $(build_kind))
+$(info $(LOCAL_MODULE))
+$(info $(addprefix $(LOCAL_PATH)/,$(LOCAL_SRC_FILES) 
$(LOCAL_SRC_FILES$(EMACS_ABI))))
+
+ifeq ($(findstring lib,$(LOCAL_MODULE)),lib)
+NDK_A_NAMES = $(LOCAL_MODULE).a
+else
+NDK_A_NAMES = lib$(LOCAL_MODULE).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 ($$(findstring $$(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
+
+ifneq ($$(findstring stdc++,$$(NDK_$(1)_SHARED_LIBRARIES)),)
+NDK_CXX_FLAG_$(LOCAL_MODULE) := yes
+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
+
+# Figure out includes from dependencies as well.
+NDK_INCLUDES := $(LOCAL_EXPORT_C_INCLUDE_DIRS) $(LOCAL_EXPORT_C_INCLUDES)
+
+define add-includes
+ifeq ($$(findstring $$(NDK_$(1)_EXPORT_INCLUDES),$$(NDK_INCLUDES)),)
+NDK_INCLUDES += $$(NDK_$(1)_EXPORT_INCLUDES)
+
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), 
$$(NDK_$(1)_SHARED_LIBRARIES)) $$(NDK_$(1)_STATIC_LIBRARIES),$$(eval $$(call 
add-includes,$$(module))))
+endif
+endef
+
+# Resolve additional dependencies based on LOCAL_STATIC_LIBRARIES and
+# LOCAL_SHARED_LIBRARIES.
+
+SYSTEM_LIBRARIES = z libz libc c libdl dl libstdc++ stdc++ log liblog android 
libandroid
+
+$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), $(LOCAL_STATIC_LIBRARIES) 
$(LOCAL_WHOLE_STATIC_LIBRARIES)),$(eval $(call add-a-name,$(module))))
+$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), 
$(LOCAL_SHARED_LIBRARIES)),$(eval $(call add-so-name,$(module))))
+$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), $(LOCAL_SHARED_LIBRARIES) 
$(LOCAL_STATIC_LIBRARIES) $(LOCAL_WHOLE_LIBRARIES)),$(eval $(call 
add-includes,$(module))))
+
+ifneq ($(findstring stdc++,$(LOCAL_SHARED_LIBRARIES)),)
+NDK_CXX_FLAG_$(LOCAL_MODULE) := yes
+endif
+
+$(info $(foreach dir,$(NDK_INCLUDES),-I$(dir)))
+$(info $(LOCAL_EXPORT_CFLAGS))
+$(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) $(NDK_SO_NAMES))
+$(info $(NDK_CXX_FLAG_$(LOCAL_MODULE)))
+$(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..a41679c53af
--- /dev/null
+++ b/build-aux/ndk-build-helper-4.mk
@@ -0,0 +1,39 @@
+# 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_C_INCLUDE_DIRS
+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..05f0af76411
--- /dev/null
+++ b/build-aux/ndk-build-helper.mk
@@ -0,0 +1,81 @@
+# 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
+# cross/ndk-build/README for more details.
+
+# TARGET_ARCH_ABI is the ABI that is being built for.
+TARGET_ARCH_ABI := $(EMACS_ABI)
+
+# TARGET_ARCH is the architecture that is being built for.
+TARGET_ARCH := $(NDK_BUILD_ARCH)
+
+# NDK_LAST_MAKEFILE is the last Makefile that was included.
+NDK_LAST_MAKEFILE = $(lastword $(filter %Android.mk,$(MAKEFILE_LIST)))
+
+# local-makefile is the current Makefile being loaded.
+local-makefile = $(NDK_LAST_MAKEFILE)
+
+# Make NDK_BUILD_DIR absolute.
+NDK_BUILD_DIR := $(absname $(NDK_BUILD_DIR))
+
+# Make EMACS_SRCDIR absolute.  This must be absolute, or nested
+# Android.mk files will not be able to find CLEAR_VARS.
+EMACS_SRCDIR := $(absname $(EMACS_SRCDIR))
+
+# my-dir is a function that returns the Android module directory.  If
+# no Android.mk has been loaded, use ANDROID_MODULE_DIRECTORY.
+my-dir = $(or $(and $(local-makefile),$(dir 
$(local-makefile))),$(ANDROID_MODULE_DIRECTORY))
+
+# Return all Android.mk files under the first arg.
+all-makefiles-under = $(wildcard $(1)/*/Android.mk)
+
+# Return all Android.mk files in subdirectories of this Makefile's
+# location.
+all-subdir-makefiles = $(call all-makefiles-under,$(call my-dir))
+
+# These functions are not implemented.
+parent-makefile =
+grand-parent-makefile =
+
+NDK_IMPORTS :=
+
+# Add the specified module (arg 1) to NDK_IMPORTS.
+import-module = $(eval NDK_IMPORTS += $(1))
+
+# Print out module information every time BUILD_SHARED_LIBRARY is
+# called.
+
+BUILD_SHARED_LIBRARY=$(BUILD_AUXDIR)ndk-build-helper-1.mk
+BUILD_STATIC_LIBRARY=$(BUILD_AUXDIR)ndk-build-helper-2.mk
+BUILD_EXECUTABLE=$(BUILD_AUXDIR)ndk-build-helper-3.mk
+CLEAR_VARS=$(BUILD_AUXDIR)ndk-build-helper-4.mk
+
+# Now include Android.mk.
+
+include $(ANDROID_MAKEFILE)
+
+# Finally, print out the imports.
+$(info Start Imports)
+$(info $(NDK_IMPORTS))
+$(info End Imports)
+
+# 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..6ff30973d67
--- /dev/null
+++ b/build-aux/ndk-module-extract.awk
@@ -0,0 +1,88 @@
+/^Building.+$/ {
+  kind = $2
+}
+
+/^Start Imports$/ {
+  imports = 1
+}
+
+// {
+  if (imports && ++imports > 2)
+    {
+      if (!match ($0, /^End Imports$/))
+       makefile_imports = makefile_imports " " $0
+    }
+  else if (!match ($0, /^End$/) && !match ($0, /^Building.+$/))
+    {
+      if (kind)
+       {
+         if (target_found)
+           cxx_deps = $0
+         else if (ldflags_found)
+           {
+             target = $0
+             target_found = 1
+           }
+         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
+      printf "module_cxx_deps=\"%s\"\n", cxx_deps
+    }
+
+  src = ""
+  name = ""
+  kind = ""
+  includes = ""
+  cflags = ""
+  ldflags = ""
+  name_found = ""
+  src_found = ""
+  includes_found = ""
+  cflags_found = ""
+  ldflags_found = ""
+  target_found = ""
+}
+
+/^End Imports$/ {
+  imports = ""
+  # Strip off leading whitespace.
+  gsub (/^[ \t]+/, "", makefile_imports)
+  printf "module_imports=\"%s\"\n", makefile_imports
+  makefile_imports = ""
+}
diff --git a/configure.ac b/configure.ac
index 79f5a468dd9..c77fab3eefd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -26,6 +26,25 @@ dnl Note this is parsed by (at least) make-dist and 
lisp/cedet/ede/emacs.el.
 AC_INIT([GNU Emacs], [30.0.50], [bug-gnu-emacs@gnu.org], [],
   [https://www.gnu.org/software/emacs/])
 
+if test "$XCONFIGURE" = "android"; then
+  # configure is being called recursively to configure Emacs for
+  # Android!
+  AC_MSG_NOTICE([called to recursively configure Emacs for Android.])
+  # Set CC to ANDROID_CC and CFLAGS to ANDROID_CFLAGS.
+  CC=$ANDROID_CC
+  # Set -Wno-implicit-function-declaration.  Building Emacs for older
+  # versions of Android requires configure tests to fail if the
+  # functions are not defined, as the Android library in the NDK
+  # defines subroutines that are not available in the headers being
+  # used.
+  CFLAGS="$ANDROID_CFLAGS -Werror=implicit-function-declaration"
+  # Don't explicitly enable support for large files unless Emacs is
+  # being built for API 21 or later.  Otherwise, mmap does not work.
+  AS_IF([test "$ANDROID_SDK" -lt "21"], [
+    enable_largefile=no
+    enable_year2038=no])
+fi
+
 dnl Set emacs_config_options to the options of 'configure', quoted for the 
shell,
 dnl and then quoted again for a C string.  Separate options with spaces.
 dnl Add some environment variables, if they were passed via the environment
@@ -126,7 +145,64 @@ MAKE=$ac_cv_path_MAKE
 export MAKE
 
 dnl Canonicalize the configuration name.
+if test "$XCONFIGURE" = "android"; then
+  dnl Set host to whatever Android system Emacs is being configured
+  dnl for.  Determine this by looking at the output of ANDROID_CC.
+
+  AC_MSG_CHECKING([the cross-compiler's target])
+  cc_target=`${CC} -v 2>&1 | sed -n 's/Target: //p'`
+  case "$cc_target" in
+    *android*) host_alias=$cc_target
+    ;;
+    *) AC_MSG_ERROR([The cross compiler does not compile for Android.
+Please verify that you specified the correct compiler in the ANDROID_CC
+variable when you ran configure.])
+    ;;
+  esac
+  AC_MSG_RESULT([$host_alias])
+fi
+
 AC_CANONICAL_HOST
+AC_CANONICAL_BUILD
+
+AS_IF([test "$XCONFIGURE" = "android"],[
+  # Initialize the Android NDK build system.  Make sure to use the
+  # passed through NDK path.
+  # Make sure to pass through the CFLAGS, as older versions of the
+  # NDK require them to be able to find system includes.
+  with_ndk_path="$android_ndk_path"
+  with_ndk_cxx_shared="$android_ndk_cxx_shared"
+  with_ndk_cxx="$android_ndk_cxx"
+  ndk_INIT([$android_abi], [$ANDROID_SDK], [cross/ndk-build],
+           [$ANDROID_CFLAGS])
+
+  # At the same time, configure libexec with the build directory
+  # set to `exec'.
+  AS_MKDIR_P([exec])
+
+  # Enter exec and configure it, using the C compiler as both the
+  # assembler and the linker.  Determine the absolute name of the
+  # source directory.
+  # N.B. that the linker is actually cc, so pass -nostdlib, lest
+  # the crt be linked in.  Likewise for as.
+
+  AS_CASE([$srcdir], [.], [emacs_srcdir=`pwd`],
+    [[[\\/]* | ?:[\\/]*]], [emacs_srcdir=$srcdir],
+    [*], [emacs_srcdir=`pwd`/$srcdir])
+
+  AC_MSG_NOTICE([configuring in `exec'])
+
+  OLDCWD=`pwd`
+  cd exec
+  $CONFIG_SHELL $emacs_srcdir/exec/configure           \
+               --host=$host "CC=$CC" "LD=$CC" "AS=$CC" \
+               "AR=$AR" "CFLAGS=$CFLAGS"
+  emacs_val=$?
+  cd $OLDCWD
+
+  AS_IF([test "$emacs_val" != "0"],
+    [AC_MSG_ERROR([failed to configure in `exec'])])
+])
 
 case $host in
  *-mingw*)
@@ -207,6 +283,13 @@ AC_ARG_WITH([all],
   [with_features=$withval],
   [with_features=yes])
 
+dnl ARCH_INDEPENDENT_CONFIG_FILES(FILE...)
+dnl Like AC_CONFIG_FILES(FILE).  However, do not generate this
+dnl   if configure is being called recursively in preparation
+dnl   for cross-compilation.
+AC_DEFUN([ARCH_INDEPENDENT_CONFIG_FILES], [
+  AS_IF([test "$XCONFIGURE" != "android"], [AC_CONFIG_FILES([$1])])])
+
 dnl OPTION_DEFAULT_OFF(NAME, HELP-STRING)
 dnl Create a new --with option that defaults to being disabled.
 dnl NAME is the base name of the option.  The shell variable with_NAME
@@ -260,24 +343,33 @@ AC_ARG_WITH([mailutils],
       installed])],
   [],
   [with_mailutils=$with_features
-   if test "$with_mailutils" = yes; then
-     (movemail --version) >/dev/null 2>&1 || with_mailutils=no
-   fi])
-if test "$with_mailutils" = no; then
-  with_mailutils=
-fi
+   AS_IF([test "$with_mailutils" = yes],
+     [AS_IF([test "x$XCONFIGURE" != "xandroid" \
+             && test "$with_android" = "no"],
+       [(movemail --version) >/dev/null 2>&1 || with_mailutils=no],
+       [dnl Don't check for movemail if cross-compiling.
+        dnl instead, default to false.
+        with_mailutils=no])])])
+AS_IF([test "$with_mailutils" = no],
+  [with_mailutils=])
+
+AS_IF([test x"$with_mailutils" = xyes],
+  [AC_DEFINE([HAVE_MAILUTILS], [1],
+     [Define to 1 if Emacs was configured with mailutils])])
+
 AC_SUBST([with_mailutils])
 
 AC_ARG_WITH([pop],
   [AS_HELP_STRING([--with-pop],
      [Support POP mail retrieval if Emacs movemail is used (not recommended,
       as Emacs movemail POP is insecure).  This is the default only on
-      native MS-Windows.])],
+      native MS-Windows and Android.])],
   [],
-  [case $host in
-     *-mingw*) with_pop=yes;;
-     *) with_pop=no-by-default;;
-   esac])
+  dnl Enable movemail POP support on Android as GNU Mailutils is
+  dnl normally unavailable on that platform.
+  [AS_CASE([$host],
+     [*-mingw*|*android*], [with_pop=yes],
+     [with_pop=no-by-default])])
 if test "$with_pop" = yes; then
    AC_DEFINE([MAIL_USE_POP])
 fi
@@ -499,6 +591,12 @@ OPTION_DEFAULT_ON([threads],[don't compile with elisp 
threading support])
 OPTION_DEFAULT_OFF([cygwin32-native-compilation],[use native compilation on 
32-bit Cygwin])
 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([shared-user-id],
+  [AS_HELP_STRING([--with-shared-user-id=ID],
+    [use the given shared user ID in Android builds])])
 
 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)])],
@@ -615,37 +713,51 @@ do
 done
 IFS="$ac_save_IFS"
 
-if test x$ac_enable_checking != x ; then
-  AC_DEFINE([ENABLE_CHECKING], [1],
-[Define to 1 if expensive run-time data type and consistency checks are 
enabled.])
-fi
-if $CHECK_STRUCTS; then
-  AC_DEFINE([CHECK_STRUCTS], [1],
-    [Define this to check whether someone updated the portable dumper
-     code after changing the layout of a structure that it uses.
-     If you change one of these structures, check that the pdumper.c
-     code is still valid, and update the pertinent hash in pdumper.c
-     by manually copying the hash from the newly-generated dmpstruct.h.])
-fi
-AC_SUBST([CHECK_STRUCTS])
-if test x$ac_gc_check_stringbytes != x ; then
-  AC_DEFINE([GC_CHECK_STRING_BYTES], [1],
-[Define this temporarily to hunt a bug.  If defined, the size of
-   strings is redundantly recorded in sdata structures so that it can
-   be compared to the sizes recorded in Lisp strings.])
-fi
-if test x$ac_gc_check_string_overrun != x ; then
-  AC_DEFINE([GC_CHECK_STRING_OVERRUN], [1],
-[Define this to check for short string overrun.])
-fi
-if test x$ac_gc_check_string_free_list != x ; then
-  AC_DEFINE([GC_CHECK_STRING_FREE_LIST], [1],
-[Define this to check the string free list.])
-fi
-if test x$ac_glyphs_debug != x ; then
-  AC_DEFINE([GLYPH_DEBUG], [1],
-[Define this to enable glyphs debugging code.])
-fi
+# This environment variable is used to signal that checking should be
+# enabled on Android.  When that happens, simply enable checking for
+# the cross-compiled Android binary.
+
+AS_IF([test "x$XCONFIGURE" = "xandroid" \
+       && test "x$android_enable_checking" = "xyes"],
+  [ac_enable_checking=yes])
+
+# There is little point in enabling checking in the build machine if
+# cross-compiling for Android.
+AS_IF([test -z "$with_android" || test -n "$XCONFIGURE"],[
+  if test x$ac_enable_checking != x ; then
+    AC_DEFINE([ENABLE_CHECKING], [1],
+  [Define to 1 if expensive run-time data type and consistency checks are 
enabled.])
+  fi
+  if $CHECK_STRUCTS; then
+    AC_DEFINE([CHECK_STRUCTS], [1],
+      [Define this to check whether someone updated the portable dumper
+       code after changing the layout of a structure that it uses.
+       If you change one of these structures, check that the pdumper.c
+       code is still valid, and update the pertinent hash in pdumper.c
+       by manually copying the hash from the newly-generated dmpstruct.h.])
+  fi
+  AC_SUBST([CHECK_STRUCTS])
+  if test x$ac_gc_check_stringbytes != x ; then
+    AC_DEFINE([GC_CHECK_STRING_BYTES], [1],
+  [Define this temporarily to hunt a bug.  If defined, the size of
+     strings is redundantly recorded in sdata structures so that it can
+     be compared to the sizes recorded in Lisp strings.])
+  fi
+  if test x$ac_gc_check_string_overrun != x ; then
+    AC_DEFINE([GC_CHECK_STRING_OVERRUN], [1],
+  [Define this to check for short string overrun.])
+  fi
+  if test x$ac_gc_check_string_free_list != x ; then
+    AC_DEFINE([GC_CHECK_STRING_FREE_LIST], [1],
+  [Define this to check the string free list.])
+  fi
+  if test x$ac_glyphs_debug != x ; then
+    AC_DEFINE([GLYPH_DEBUG], [1],
+  [Define this to enable glyphs debugging code.])
+  fi
+],[AS_IF([test "x$ac_enable_checking" != x],
+    [android_enable_checking=yes
+     export android_enable_checking])])
 
 dnl The name of this option is unfortunate.  It predates, and has no
 dnl relation to, the "sampling-based elisp profiler" added in 24.3.
@@ -683,6 +795,532 @@ AC_ARG_ENABLE([build-details],
   [test "$enableval" = no && BUILD_DETAILS=--no-build-details])
 AC_SUBST([BUILD_DETAILS])
 
+# JAVA_PUSH_LINT(OPT)
+# -------------------
+# Check if javac supports the diagnostic flag -Xlint:OPT.
+# If it does, add it to WARN_JAVAFLAGS.
+
+AC_DEFUN([JAVA_PUSH_LINT],
+[
+  AC_CACHE_CHECK([whether Java compiler accepts -Xlint:$1],
+    [emacs_cv_javac_knows_lint_$1],
+    AS_IF([rm -f conftest.class
+cat << EOF > conftest.java
+
+class conftest
+{
+
+}
+
+EOF
+("$JAVAC" -Xlint:$1 conftest.java 2>&AS_MESSAGE_LOG_FD) \
+  && rm -f conftest.class], [emacs_cv_javac_knows_lint_$1=yes],
+     [emacs_cv_javac_knows_lint_$1=no]))
+
+  AS_IF([test "$emacs_cv_javac_knows_lint_$1" = "yes"],
+    [WARN_JAVAFLAGS="$WARN_JAVAFLAGS -Xlint:$1"])
+])
+
+# Start Android configuration.  This is done in three steps:
+
+# First, the SDK tools needed to build the Android package on the host
+# are found.
+
+# Then, configure is called inside itself with the NDK C and C++
+# compilers, and the Makefiles generated, along with config.h, are
+# renamed to end with .android.
+
+# Finally, configure continues to configure the Emacs binary that will
+# run on the host.
+
+ANDROID=
+JAVAC=
+AAPT=
+JARSIGNER=
+APKSIGNER=
+ZIPALIGN=
+DX=
+ANDROID_JAR=
+ANDROID_ABI=
+WARN_JAVAFLAGS=
+ANDROID_SHARED_USER_ID=
+ANDROID_SHARED_USER_NAME=
+
+# This is a list of Makefiles that have alternative versions for
+# Android.
+android_makefiles="lib/Makefile lib/gnulib.mk lib-src/Makefile src/Makefile"
+
+# This is whether or not to package mailutils into the executable.
+emacs_use_mailutils=
+
+AC_ARG_VAR([ANDROID_CC], [The Android C cross-compiler.])
+AC_ARG_VAR([SDK_BUILD_TOOLS], [Name of directory holding Android SDK 
build-tools.])
+AC_ARG_VAR([ANDROID_CFLAGS], [Flags given to the Android C cross-compiler.])
+AC_ARG_VAR([JAVAC], [Java compiler path.  Used for Android.])
+AC_ARG_VAR([JARSIGNER], [Java package signer path.  Used for Android.])
+AC_ARG_VAR([APKSIGNER], [Android package signer path.  Used for Android.])
+AC_ARG_VAR([SDK_BUILD_TOOLS], [Path to the Android SDK build tools.])
+
+if test "$with_android" = "yes"; then
+   AC_MSG_ERROR([Please specify the path to the Android.jar file, like so:
+
+  ./configure --with-android=/path/to/android.jar
+
+along with the path to the SDK build-tools (this is the directory with
+tools such as aapt, dx, and aidl):
+
+              SDK_BUILD_TOOLS=/path/to/sdk-build-tools
+
+The cross-compiler should then be specified:
+
+              ANDROID_CC=/path/to/armv7a-linux-androideabi19-clang
+
+In addition, you may pass any special arguments to the cross-compiler
+via the ANDROID_CFLAGS environment variable.])
+elif test "$with_android" = "no" || test "$with_android" = ""; then
+  ANDROID=no
+else
+  AC_CHECK_PROGS([JAVAC], [javac])
+  if test "$JAVAC" = ""; then
+    AC_MSG_ERROR([The Java compiler required to build Emacs was not found.
+Please make sure `javac' can be found on your path, or alternatively specify
+the path to your Java compiler before configuring Emacs, like so:
+
+  JAVAC=/opt/jdk/bin/javac ./configure --with-android])
+  fi
+
+  AC_CHECK_PROGS([JARSIGNER], [jarsigner])
+  if test "$JARSIGNER" = ""; then
+    AC_MSG_ERROR([The Java package signing utility was not found.
+Please make sure `jarsigner' can be found on your path, or alternatively
+specify its location before configuring Emacs, like so:
+
+  JARSIGNER=/opt/jdk/bin/jarsigner ./configure --with-android])
+  fi
+
+  AC_CACHE_CHECK([whether the Java compiler works],
+    [emacs_cv_working_javac],
+    AS_IF([rm -f conftest.class
+cat << EOF > conftest.java
+
+import android.app.Activity;
+import android.os.Bundle;
+
+class conftest extends Activity
+{
+  @Override
+  public void
+  onCreate (Bundle savedInstanceState)
+  {
+    super.onCreate (savedInstanceState);
+  }
+}
+
+EOF
+("$JAVAC" -classpath "$with_android" -target 1.7 -source 1.7 conftest.java \
+  -d . >&AS_MESSAGE_LOG_FD 2>&1) && test -s conftest.class && rm -f 
conftest.class],
+          [emacs_cv_working_javac=yes],
+          [emacs_cv_working_javac=no]))
+
+  if test "$emacs_cv_working_javac" = "no"; then
+    AC_MSG_ERROR([The Java compiler does not work, or you did not specify
+a valid path to android.jar.  See config.log for more details.])
+  fi
+
+  AC_CACHE_CHECK([whether android.jar is new enough],
+    [emacs_cv_android_s_or_later],
+    AS_IF([rm -f conftest.class
+cat << EOF > conftest.java
+
+import android.os.Build;
+
+class conftest
+{
+  private static int test = Build.VERSION_CODES.TIRAMISU;
+}
+
+EOF
+("$JAVAC" -classpath "$with_android" -target 1.7 -source 1.7 conftest.java \
+  -d . >&AS_MESSAGE_LOG_FD 2>&1) && test -s conftest.class && rm -f 
conftest.class],
+          [emacs_cv_android_s_or_later=yes],
+          [emacs_cv_android_s_or_later=no]))
+
+  if test "$emacs_cv_android_s_or_later" = "no"; then
+    AC_MSG_ERROR([Emacs must be built with an android.jar file produced for \
+Android 13 (Tiramisu) or later.])
+  fi
+
+  dnl See if the Java compiler supports the `--release' option which
+  dnl makes it check for and prevent using features introduced after
+  dnl Java 1.7.
+
+  AC_CACHE_CHECK([whether javac accepts --release 7],
+    [emacs_cv_javac_release_7], AS_IF([rm -f conftest.class
+cat << EOF > conftest.java
+
+class conftest
+{
+
+}
+
+EOF
+("$JAVAC" --release 7 conftest.java 2>&AS_MESSAGE_LOG_FD) \
+  && rm -f conftest.class],
+      [emacs_cv_javac_release_7=yes],
+      [emacs_cv_javac_release_7=no]))
+
+  if test "$emacs_cv_javac_release_7" = "yes"; then
+    WARN_JAVAFLAGS="$WARN_JAVAFLAGS --release 7"
+  else
+    dnl If not, just make sure the generated bytecode is correct.
+    WARN_JAVAFLAGS="$WARN_JAVAFLAGS -target 1.7 -source 1.7"
+  fi
+
+  dnl Enable some useful Java linting options.
+  JAVA_PUSH_LINT([deprecation])
+  JAVA_PUSH_LINT([cast])
+  JAVA_PUSH_LINT([divzero])
+  JAVA_PUSH_LINT([nonempty])
+  JAVA_PUSH_LINT([empty])
+  JAVA_PUSH_LINT([finally])
+  JAVA_PUSH_LINT([overrides])
+  JAVA_PUSH_LINT([path])
+  JAVA_PUSH_LINT([serial])
+  JAVA_PUSH_LINT([unchecked])
+
+  # Get the name of the android.jar file.
+  ANDROID_JAR="$with_android"
+
+  # Substitute this into java/Makefile.
+  AC_SUBST([WARN_JAVAFLAGS])
+
+  AC_PATH_PROGS([AAPT], [aapt], [], "${SDK_BUILD_TOOLS}:$PATH")
+  if test "$AAPT" = ""; then
+    AC_MSG_ERROR([The Android asset packaging tool was not found.
+Please verify that the path to the SDK build tools you specified is correct])
+  fi
+
+  AC_PATH_PROGS([APKSIGNER], [apksigner], [], "${SDK_BUILD_TOOLS}:$PATH")
+  if test "$APKSIGNER" = ""; then
+    AC_MSG_ERROR([The Android package signing tool was not found.
+Please verify that the path to the SDK build tools you specified is correct])
+  fi
+
+  AC_PATH_PROGS([D8], [d8], [], "${SDK_BUILD_TOOLS}:$PATH")
+  if test "D8" = ""; then
+    AC_MSG_ERROR([The Android dexer was not found.
+Please verify that the path to the SDK build tools you specified is correct])
+  fi
+
+  AC_PATH_PROGS([ZIPALIGN], [zipalign], [], "${SDK_BUILD_TOOLS}:$PATH")
+  if test "ZIPALIGN" = ""; then
+    AC_MSG_ERROR([The Android ZIP archive alignment utility was not found.
+Please verify that the path to the SDK build tools you specified is correct]);
+  fi
+
+  dnl Now configure Emacs to generate binaries for Android.  After the
+  dnl configuration completes, move the generated Makefiles.
+
+  if test "$ANDROID_CC" = ""; then
+    AC_MSG_ERROR([Please specify the path to the Android cross-compiler
+for your machine.  For example:
+
+  ANDROID_CC=/path/to/armv7a-linux-androideabi19-clang    \\
+    ./configure --with-android])
+  fi
+
+  dnl Obtain the cross compiler's target to find out where binaries go
+  dnl in the resulting package.
+
+  AC_MSG_CHECKING([for the kind of Android system Emacs is being built for])
+  cc_target=`${ANDROID_CC} -v 2>&1 | sed -n 's/Target: //p'`
+  case "$cc_target" in
+[
+    *i[3-6]86*) android_abi=x86
+      ;;
+    *x86_64*) android_abi=x86_64
+      ;;
+    *aarch64*) android_abi=arm64-v8a
+      ;;
+    *arm*v7a*) android_abi=armeabi-v7a
+      ;;
+    *mips64*) android_abi=mips64
+      ;;
+    *mips*) android_abi=mips
+      ;;
+    *arm*) android_abi=armeabi
+      ;;
+]
+    *) AC_MSG_ERROR([configure could not determine the type of Android \
+binary Emacs is being configured for.  Please port this configure script \
+to your Android system, or verify that you specified the correct compiler \
+in the ANDROID_CC variable when you ran configure.
+
+The compiler target is: $cc_target])
+      ;;
+  esac
+  AC_MSG_RESULT([$android_abi])
+
+  ANDROID_ABI=$android_abi
+
+  dnl Obtain the minimum SDK version of the resulting Emacs binary
+  dnl built with this NDK.
+
+  ANDROID_MIN_SDK=8
+  AC_MSG_CHECKING([for the lowest Android version Emacs can run on])
+  [android_sdk=`echo "$cc_target" | grep -oE 'android([0-9][0-9]?)'`]
+
+  if test -n "$android_sdk"; then
+    android_sdk=`echo "$android_sdk" | sed -n 's/android//p'`
+    AC_MSG_RESULT([$android_sdk])
+    ANDROID_MIN_SDK=$android_sdk
+  else
+    # This is probably GCC.
+    [ cat << EOF > conftest.c
+#include <android/api-level.h>
+extern const char *foo;
+
+int
+main (void)
+{
+#if __ANDROID_API__ < 7
+   foo = "emacs_api_6";
+#elif __ANDROID_API__ < 8
+   foo = "emacs_api_7";
+#elif __ANDROID_API__ < 9
+   foo = "emacs_api_8";
+#elif __ANDROID_API__ < 10
+  foo = "emacs_api_9";
+#elif __ANDROID_API__ < 11
+  foo = "emacs_api_10";
+#elif __ANDROID_API__ < 12
+  foo = "emacs_api_11";
+#elif __ANDROID_API__ < 13
+  foo = "emacs_api_12";
+#elif __ANDROID_API__ < 14
+   foo = "emacs_api_13";
+#elif __ANDROID_API__ < 15
+   foo = "emacs_api_14";
+#elif __ANDROID_API__ < 16
+   foo = "emacs_api_15";
+#elif __ANDROID_API__ < 17
+   foo = "emacs_api_16";
+#elif __ANDROID_API__ < 18
+   foo = "emacs_api_17";
+#elif __ANDROID_API__ < 19
+   foo = "emacs_api_18";
+#elif __ANDROID_API__ < 20
+   foo = "emacs_api_19";
+#elif __ANDROID_API__ < 21
+   foo = "emacs_api_20";
+#elif __ANDROID_API__ < 22
+   foo = "emacs_api_21";
+#elif __ANDROID_API__ < 23
+   foo = "emacs_api_22";
+#elif __ANDROID_API__ < 24
+   foo = "emacs_api_23";
+#elif __ANDROID_API__ < 25
+   foo = "emacs_api_24";
+#elif __ANDROID_API__ < 26
+   foo = "emacs_api_25";
+#elif __ANDROID_API__ < 27
+   foo = "emacs_api_26";
+#elif __ANDROID_API__ < 28
+   foo = "emacs_api_27";
+#elif __ANDROID_API__ < 29
+   foo = "emacs_api_28";
+#elif __ANDROID_API__ < 30
+   foo = "emacs_api_29";
+#elif __ANDROID_API__ < 31
+   foo = "emacs_api_30";
+#elif __ANDROID_API__ < 32
+   foo = "emacs_api_31";
+#elif __ANDROID_API__ < 33
+   foo = "emacs_api_32";
+#elif __ANDROID_API__ < 34
+   foo = "emacs_api_33";
+#else
+   foo = "emacs_api_future";
+#endif
+}
+EOF]
+
+    AC_CACHE_VAL([emacs_cv_android_api],
+                [$ANDROID_CC $ANDROID_CFLAGS -c conftest.c -o conftest.o \
+                 && emacs_cv_android_api=`grep -ao -E                    \
+                    "emacs_api_([[0-9][0-9]]?|future)" conftest.o`])
+    android_sdk="$emacs_cv_android_api"
+    rm -rf conftest.c conftest.o
+
+    # If this version of the NDK requires __ANDROID_API__ to be
+    # specified, then complain to the user.
+    if test "$android_sdk" = "emacs_api_future"; then
+       AC_MSG_ERROR([The version of Android to build for was not specified.
+You must tell the Android compiler what version of Android to build for,
+by defining the __ANDROID_API__ preprocessor macro in ANDROID_CC, like so:
+
+    ANDROID_CC="/path/to/ndk/arm-linux-android-gcc -D__ANDROID_API__=8"])
+    fi
+
+    if test -n "$android_sdk"; then
+       android_sdk=`echo $android_sdk | sed -n 's/emacs_api_//p'`
+       AC_MSG_RESULT([$android_sdk])
+       ANDROID_MIN_SDK=$android_sdk
+    else
+      AC_MSG_RESULT([unknown ($cc_target); assuming 8])
+      AC_MSG_ERROR([configure could not determine the versions of Android \
+a binary built with this compiler will run on.  The generated application \
+package will likely install on older systems but crash on startup.])
+      android_sdk=8
+    fi
+  fi
+  AC_SUBST([ANDROID_MIN_SDK])
+
+  # Now tell java/Makefile if Emacs is being built for Android 4.3 or
+  # earlier.
+  ANDROID_SDK_18_OR_EARLIER=
+  if test "$android_sdk" -le "18"; then
+     ANDROID_SDK_18_OR_EARLIER=yes
+  fi
+  AC_SUBST([ANDROID_SDK_18_OR_EARLIER])
+
+  # Likewise for Android 2.2.
+  ANDROID_SDK_8_OR_EARLIER=
+  if test "$android_sdk" -le "8"; then
+     ANDROID_SDK_8_OR_EARLIER=yes
+  fi
+  AC_SUBST([ANDROID_SDK_8_OR_EARLIER])
+
+  # Save confdefs.h and config.log for now.
+  mv -f confdefs.h _confdefs.h
+  mv -f config.log _config.log
+
+  # Make sure these files are removed upon exit.
+  trap "rm -rf _confdefs.h _config.log" 0
+
+  # 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"
+  passthrough="$passthrough --with-json=$with_json"
+  passthrough="$passthrough --with-jpeg=$with_jpeg"
+  passthrough="$passthrough --with-xml2=$with_xml2"
+  passthrough="$passthrough --with-sqlite3=$with_sqlite3"
+  passthrough="$passthrough --with-gnutls=$with_gnutls"
+  passthrough="$passthrough --with-tiff=$with_tiff"
+  passthrough="$passthrough --with-selinux=$with_selinux"
+  passthrough="$passthrough --with-modules=$with_modules"
+  passthrough="$passthrough --with-tree-sitter=$with_tree_sitter"
+  passthrough="$passthrough --with-imagemagick=$with_imagemagick"
+  passthrough="$passthrough --with-lcms2=$with_lcms2"
+  passthrough="$passthrough --with-mailutils=$with_mailutils"
+  passthrough="$passthrough --with-pop=$with_pop"
+  passthrough="$passthrough --with-harfbuzz=$with_harfbuzz"
+
+  # Now pass through some checking options.
+  emacs_val="--enable-check-lisp-object-type=$enable_check_lisp_object_type"
+  passthrough="$passthrough $emacs_val"
+
+  AS_IF([test "x$with_mailutils" = "xyes"], [emacs_use_mailutils=yes])
+  AC_SUBST([emacs_use_mailutils])
+
+  AS_IF([XCONFIGURE=android ANDROID_CC="$ANDROID_CC"           \
+         ANDROID_SDK="$android_sdk" android_abi=$android_abi   \
+        android_ndk_path="$with_ndk_path"                      \
+        android_ndk_cxx_shared="$with_ndk_cxx_shared"          \
+        android_ndk_cxx="$android_ndk_cxx"                     \
+        $CONFIG_SHELL $0 $passthrough], [],
+    [AC_MSG_ERROR([Failed to cross-configure Emacs for android.])])
+
+  # Now set ANDROID to yes.
+  ANDROID=yes
+
+  for makefile in $android_makefiles; do
+    AC_MSG_NOTICE([Generating $makefile.android])
+    mv -f "$makefile" "$makefile.android"
+  done
+
+  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
+
+  # Move the Android config.log to config.log.android.  */
+  mv -f config.log config.log.android
+
+  # And _config.log back.
+  mv -f _config.log config.log
+fi
+
+AC_SUBST([ANDROID])
+AC_SUBST([JAVAC])
+AC_SUBST([AAPT])
+AC_SUBST([D8])
+AC_SUBST([ZIPALIGN])
+AC_SUBST([ANDROID_JAR])
+AC_SUBST([ANDROID_ABI])
+
+if test "$XCONFIGURE" = "android"; then
+  ANDROID=yes
+
+  # Enable cross compiling.
+  cross_compiling=yes
+fi
+
+AC_SUBST([XCONFIGURE])
+
+if test "$ANDROID" = "yes"; then
+  # When --with-android is specified, almost all build options must be
+  # disabled, both within the recursive invocation of configure and
+  # outside.
+  with_xpm=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
+    with_json=no
+    with_jpeg=no
+    with_xml2=no
+    with_sqlite3=no
+    with_gnutls=no
+    with_tiff=no
+    with_selinux=no
+    with_modules=no
+    with_tree_sitter=no
+    with_imagemagick=no
+    with_lcms2=no
+    with_mailutils=no
+    with_pop=no
+    with_harfbuzz=no
+  fi
+
+  with_rsvg=no
+  with_libsystemd=no
+  with_cairo=no
+  with_xft=no
+  with_libotf=no
+  with_gpm=no
+  with_dbus=no
+  with_gsettings=no
+  with_threads=no
+  with_ns=no
+
+  # zlib is available in android.
+fi
+
 dnl This used to use changequote, but, apart from 'changequote is evil'
 dnl per the autoconf manual, we can speed up autoconf somewhat by quoting
 dnl the great gob of text.  Thus it's not processed for possible expansion.
@@ -706,6 +1344,11 @@ dnl quotation begins
 opsys='' unported=no
 case "${canonical}" in
 
+  ## Android
+  *linux-android* )
+    opsys=android
+  ;;
+
   ## GNU/Linux and similar ports
   *-*-linux* )
     opsys=gnu-linux
@@ -872,6 +1515,7 @@ AC_DEFUN([_AC_PROG_CC_C89], [$2])
 
 dnl Sets GCC=yes if using gcc.
 AC_PROG_CC([gcc cc cl clang "$XCRUN gcc" "$XCRUN clang"])
+
 if test -n "$XCRUN"; then
   AC_CHECK_PROGS([AR], [ar "$XCRUN ar"])
   test -n "$AR" && export AR
@@ -909,6 +1553,7 @@ AC_DEFUN_ONCE([gl_STDLIB_H],
 # Initialize gnulib right after choosing the compiler.
 dnl Amongst other things, this sets AR and ARFLAGS.
 gl_EARLY
+ndk_LATE
 
 if test "$ac_test_CFLAGS" != set; then
   # It's helpful to have C macros available to GDB, so prefer -g3 to -g
@@ -1128,6 +1773,13 @@ AS_IF([test $gl_gcc_warnings = no],
     nw="$nw -Wsuggest-attribute=format"
   fi
 
+  # If Emacs is being built for Android and many functions are
+  # currently stubbed out for operation on the build machine, disable
+  # -Wsuggest-attribute=noreturn.
+
+  AS_IF([test "$ANDROID" = "yes"],
+    [nw="$nw -Wsuggest-attribute=noreturn"])
+
   gl_MANYWARN_ALL_GCC([ws])
   gl_MANYWARN_COMPLEMENT([ws], [$ws], [$nw])
   for w in $ws; do
@@ -1151,6 +1803,7 @@ AS_IF([test $gl_gcc_warnings = no],
     gl_WARN_ADD([-Wno-null-pointer-arithmetic])
     gl_WARN_ADD([-Wno-implicit-const-int-float-conversion])
     gl_WARN_ADD([-Wno-int-in-bool-context])
+    gl_WARN_ADD([-Wno-shift-overflow])
   fi
 
   # This causes too much noise in the MinGW build
@@ -1273,7 +1926,7 @@ else
   AM_DEFAULT_VERBOSITY=0
 fi
 AC_SUBST([AM_DEFAULT_VERBOSITY])
-AC_CONFIG_FILES([src/verbose.mk])
+ARCH_INDEPENDENT_CONFIG_FILES([src/verbose.mk])
 
 dnl Some other nice autoconf tests.
 AC_PROG_INSTALL
@@ -1698,7 +2351,6 @@ AC_CACHE_CHECK([for math library],
          d = frexp (d, &i);
          d = ldexp (d, i);
          d = log (d);
-         d = log2 (d);
          d = log10 (d);
          d = pow (d, d);
          d = rint (d);
@@ -1767,11 +2419,18 @@ AC_DEFINE_UNQUOTED([SYSTEM_TYPE], ["$SYSTEM_TYPE"],
   [The type of system you are compiling for; sets 'system-type'.])
 AC_SUBST([SYSTEM_TYPE])
 
+# Check for pw_gecos in struct passwd; this is known to be missing on
+# Android.
+
+AC_CHECK_MEMBERS([struct passwd.pw_gecos], [], [], [#include <pwd.h>])
 
 pre_PKG_CONFIG_CFLAGS=$CFLAGS
 pre_PKG_CONFIG_LIBS=$LIBS
 
-PKG_PROG_PKG_CONFIG([0.9.0])
+dnl pkg-config does not work when cross-compiling for Android.
+if test "${ANDROID}" != "yes"; then
+  PKG_PROG_PKG_CONFIG([0.9.0])
+fi
 
 dnl EMACS_CHECK_MODULES([GSTUFF], [gtk+-2.0 >= 1.3 glib = 1.3.4])
 dnl acts like PKG_CHECK_MODULES([GSTUFF], [gtk+-2.0 >= 1.3 glib = 1.3.4],
@@ -1781,10 +2440,25 @@ 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])])])])
+
+dnl EMACS_CHECK_LIB(NAME, FUNCTION, ACTION-IF-FOUND, ACTION-IF-NOT-FOUND,
+dnl OTHER-LIBRARIES, INCLUDES)
+dnl ---------------------------------------------------------------------
+dnl This is like AC_CHECK_LIB; however, there is no default action, and
+dnl when cross-configuring for Android, AC_CHECK_DECLS is called with NAME
+dnl and INCLUDES instead, as the library being checked against will likely
+dnl be built together with Emacs.
+AC_DEFUN([EMACS_CHECK_LIB],
+  [AS_IF([test -n "$ndk_INITIALIZED"],
+    [AC_CHECK_DECL([$2], [$3], [$4], [$6])],
+    [AC_CHECK_LIB([$1], [$2], [$3], [$4], [$5])])])
 
 HAVE_SOUND=no
 if test "${with_sound}" != "no"; then
@@ -1957,14 +2631,93 @@ AC_SUBST([AUTO_DEPEND])
 
 window_system=none
 
+ANDROID_OBJ=
+ANDROID_LIBS=
+# ANDROID_CFLAGS is a precious variable used to pass information to
+# the cross-compiler.
+ANDROID_BUILD_CFLAGS=
+REALLY_ANDROID=
+CM_OBJ="cm.o"
+
+AS_IF([test "$ANDROID" = "yes"],[
+  window_system=android
+  no_x=yes
+  ANDROID_OBJ="androidterm.o androidfns.o androidfont.o androidmenu.o"
+  ANDROID_OBJ="$ANDROID_OBJ android.o"
+  ANDROID_LIBS=
+  CM_OBJ=
+
+  AC_DEFINE([HAVE_ANDROID], [1], [Define to 1 if Emacs is being built
+with Android support])
+
+  AS_IF([test "$XCONFIGURE" != "android"], [
+    AC_DEFINE([ANDROID_STUBIFY], [1], [Define to 1 if Emacs is being built
+for Android, but all API calls need to be stubbed out])
+
+    # Now set any shared user ID that was specified.
+    AS_IF([test -n "$with_shared_user_id"],
+      [emacs_val=$with_shared_user_id
+       emacs_val=`AS_ECHO(["$with_shared_user_id"]) \
+                  | sed -e 's/"/\\"/'`
+       emacs_val="\"$emacs_val\""
+       ANDROID_SHARED_USER_ID="android:sharedUserId=$emacs_val"
+       # `android:sharedUserName' is required for sharedUserID to work
+       # on recent Android releases.  It does not otherwise affect the
+       # behavior of any code.
+       emacs_val="\"@string/shared_user_name\""
+       ANDROID_SHARED_USER_NAME="android:sharedUserLabel=$emacs_val"])],[
+    # Emacs will be built as a shared library, and a wrapper around it
+    # will also be built for the benefit of applications.  This
+    # requires Emacs be built as a position independent executable.
+    ANDROID_BUILD_CFLAGS="-fPIC -fvisibility=hidden"
+
+    # Graphics code in sfntfont-android.c benefits heavily from
+    # vectorization.
+    ANDROID_BUILD_CFLAGS="$ANDROID_BUILD_CFLAGS -ftree-vectorize"
+
+    # Link with libraries required for Android support.
+    # API 9 and later require `-landroid' for the asset manager.
+    # API 8 uses an emulation via the JNI.
+    AS_IF([test "$ANDROID_SDK" -lt "9"],
+      [ANDROID_LIBS="-llog -ljnigraphics"],
+      [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 and androidvfs.o.
+    ANDROID_OBJ="$ANDROID_OBJ androidselect.o androidvfs.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])])
+
+AC_SUBST([ANDROID])
+AC_SUBST([ANDROID_OBJ])
+AC_SUBST([ANDROID_LIBS])
+AC_SUBST([ANDROID_LDFLAGS])
+AC_SUBST([ANDROID_BUILD_CFLAGS])
+AC_SUBST([ANDROID_SHARED_USER_ID])
+AC_SUBST([ANDROID_SHARED_USER_NAME])
+
 if test "${with_pgtk}" = "yes"; then
   window_system=pgtk
 fi
 
-
-AC_PATH_X
-if test "$no_x" != yes && test "${with_pgtk}" != "yes"; then
-  window_system=x11
+if test "${ANDROID}" != "yes"; then
+  AC_PATH_X
+  if test "$no_x" != yes && test "${with_pgtk}" != "yes"; then
+    window_system=x11
+  fi
 fi
 
 LD_SWITCH_X_SITE_RPATH=
@@ -2280,7 +3033,6 @@ NTDIR=
 LIBS_ECLIENT=
 LIB_WSOCK32=
 NTLIB=
-CM_OBJ="cm.o"
 XARGS_LIMIT=
 if test "${HAVE_W32}" = "yes"; then
   AC_DEFINE([HAVE_NTGUI], [1], [Define to use native MS Windows GUI.])
@@ -2443,7 +3195,11 @@ dnl use the toolkit if we have gtk, or X11R5 or newer.
   haiku )
     term_header=haikuterm.h
   ;;
+  android )
+    term_header=androidterm.h
+  ;;
 esac
+
 AC_SUBST([HAVE_PGTK])
 
 if test "$window_system" = none && test "X$with_x" != "Xno"; then
@@ -2744,7 +3500,6 @@ fail;
   fi
 fi
 
-
 ### Use -lrsvg-2 if available, unless '--with-rsvg=no' is specified.
 HAVE_RSVG=no
 if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" \
@@ -2774,7 +3529,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"
 
@@ -2788,12 +3544,13 @@ if test "${with_webp}" != "no"; then
       CFLAGS="$CFLAGS $WEBP_CFLAGS"
       LIBS="$LIBS $WEBP_LIBS"
 
-      AC_CHECK_FUNC([WebPGetInfo], [],
-        [WEBP_MODULE="$WEBP_MODULE libwebpdecoder >= $WEBP_REQUIRED"
-        HAVE_WEBP=no
-        AS_UNSET([WEBP_LIBS])
-        AS_UNSET([WEBP_CFLAGS])
-        EMACS_CHECK_MODULES([WEBP], [$WEBP_MODULE])])
+      AS_IF([test "$REALLY_ANDROID" != "yes"], [
+       AC_CHECK_FUNC([WebPGetInfo], [],
+         [WEBP_MODULE="$WEBP_MODULE libwebpdecoder >= $WEBP_REQUIRED"
+          HAVE_WEBP=no
+          AS_UNSET([WEBP_LIBS])
+          AS_UNSET([WEBP_CFLAGS])
+          EMACS_CHECK_MODULES([WEBP], [$WEBP_MODULE])])])
 
       CFLAGS=$OLD_CFLAGS
       LIBS=$OLD_LIBS
@@ -2803,7 +3560,6 @@ if test "${with_webp}" != "no"; then
    fi
    if test $HAVE_WEBP = yes; then
       AC_DEFINE([HAVE_WEBP], [1], [Define to 1 if using libwebp.])
-      CFLAGS="$CFLAGS $WEBP_CFLAGS"
       # Windows loads libwebp dynamically
       if test "${opsys}" = "mingw32"; then
        WEBP_LIBS=
@@ -2813,33 +3569,53 @@ fi
 
 ### Use -lsqlite3 if available, unless '--with-sqlite3=no'
 HAVE_SQLITE3=no
+SQLITE3_LIBS=
+SQLITE3_CFLAGS=
 if test "${with_sqlite3}" != "no"; then
-   AC_CHECK_LIB([sqlite3], [sqlite3_open_v2],
-     [HAVE_SQLITE3=yes],
-     [HAVE_SQLITE3=no])
-   if test "$HAVE_SQLITE3" = "yes"; then
-     SQLITE3_LIBS=-lsqlite3
-     AC_SUBST([SQLITE3_LIBS])
-     LIBS="$SQLITE3_LIBS $LIBS"
-     AC_DEFINE([HAVE_SQLITE3], [1],
-       [Define to 1 if you have the libsqlite3 library (-lsqlite).])
-     # Windows loads libsqlite dynamically
-     if test "${opsys}" = "mingw32"; then
-        SQLITE3_LIBS=
+   if test "${REALLY_ANDROID}" = "yes"; then
+     ndk_SEARCH_MODULE([sqlite3], [SQLITE3], [HAVE_SQLITE3=yes])
+
+     if test "$HAVE_SQLITE3" = "yes"; then
+       SAVE_CFLAGS="$CFLAGS"
+       CFLAGS="$CFLAGS $SQLITE3_CFLAGS"
+       AC_CHECK_DECL([sqlite3_open_v2], [HAVE_SQLITE=yes],
+         [HAVE_SQLITE3=no], [#include <sqlite3.h>])
+       CFLAGS="$SAVE_CFLAGS"
      fi
-     AC_CHECK_LIB([sqlite3], [sqlite3_load_extension],
-       [HAVE_SQLITE3_LOAD_EXTENSION=yes],
-       [HAVE_SQLITE3_LOAD_EXTENSION=no])
-     if test "$HAVE_SQLITE3_LOAD_EXTENSION" = "yes"; then
-       AC_DEFINE([HAVE_SQLITE3_LOAD_EXTENSION], [1],
-        [Define to 1 if sqlite3 supports loading extensions.])
+   else
+     AC_CHECK_LIB([sqlite3], [sqlite3_open_v2],
+       [HAVE_SQLITE3=yes],
+       [HAVE_SQLITE3=no])
+     if test "$HAVE_SQLITE3" = "yes"; then
+       SQLITE3_LIBS=-lsqlite3
+       LIBS="$SQLITE3_LIBS $LIBS"
+       # Windows loads libsqlite dynamically
+       if test "${opsys}" = "mingw32"; then
+         SQLITE3_LIBS=
+       fi
+       AC_CHECK_LIB([sqlite3], [sqlite3_load_extension],
+        [HAVE_SQLITE3_LOAD_EXTENSION=yes],
+        [HAVE_SQLITE3_LOAD_EXTENSION=no])
+       if test "$HAVE_SQLITE3_LOAD_EXTENSION" = "yes"; then
+        AC_DEFINE([HAVE_SQLITE3_LOAD_EXTENSION], [1],
+          [Define to 1 if sqlite3 supports loading extensions.])
+       fi
      fi
-   fi
+  fi
+
+  if test "$HAVE_SQLITE3" = "yes"; then
+    AC_DEFINE([HAVE_SQLITE3], [1],
+      [Define to 1 if you have the libsqlite3 library (-lsqlite).])
+  fi
 fi
 
+AC_SUBST([SQLITE3_LIBS])
+AC_SUBST([SQLITE3_CFLAGS])
+
 HAVE_IMAGEMAGICK=no
 if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" || test 
"${HAVE_W32}" = "yes" || \
-   test "${HAVE_BE_APP}" = "yes" || test "${window_system}" = "pgtk"; then
+   test "${HAVE_BE_APP}" = "yes" || test "${window_system}" = "pgtk" || \
+   test "${REALLY_ANDROID}" = "yes"; then
   if test "${with_imagemagick}" != "no"; then
     if test -n "$BREW"; then
       # Homebrew doesn't link ImageMagick 6 by default, so make sure
@@ -2862,14 +3638,23 @@ if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = 
"yes" || test "${HAVE_W32}"
       OLD_LIBS=$LIBS
       CFLAGS="$CFLAGS $IMAGEMAGICK_CFLAGS"
       LIBS="$IMAGEMAGICK_LIBS $LIBS"
-      AC_CHECK_FUNCS([MagickRelinquishMemory MagickExportImagePixels \
-                     MagickMergeImageLayers MagickAutoOrientImage])
+      if test "$REALLY_ANDROID" != "yes"; then
+       AC_CHECK_FUNCS([MagickRelinquishMemory MagickExportImagePixels \
+                       MagickMergeImageLayers MagickAutoOrientImage])
+      else
+        # AC_CHECK_FUNCS doesn't work for Android dependencies because
+       # they are built alongside Emacs.
+       AC_CHECK_DECLS([MagickRelinquishMemory,MagickExportImagePixels,
+MagickMergeImageLayers,MagickAutoOrientImage],
+                      [], [], [#include <MagickWand/MagickWand.h>])
+      fi
       CFLAGS=$OLD_CFLAGS
       LIBS=$OLD_LIBS
       # Check that ImageMagick links.  It does not link on Fedora 25
       # with './configure CC=clang', as pkg-config outputs flags like
       # -lomp that work for GCC but not Clang.
-      if test "$ac_cv_func_MagickRelinquishMemory" != yes; then
+      if test "$ac_cv_func_MagickRelinquishMemory" != yes \
+         && test "$REALLY_ANDROID" != "yes"; then
        HAVE_IMAGEMAGICK=no
       fi
     fi
@@ -3143,19 +3928,29 @@ if test "${HAVE_GTK}" = "yes"; then
 fi
 AC_SUBST([USE_STARTUP_NOTIFICATION])
 
-dnl SELinux is available for GNU/Linux only.
+dnl SELinux is available for Linux kernel based systems only.
+dnl These include GNU/Linux and Android.
 HAVE_LIBSELINUX=no
 LIBSELINUX_LIBS=
+LIBSELINUX_CFLAGS=
 if test "${with_selinux}" = "yes"; then
-   AC_CHECK_LIB([selinux], [lgetfilecon],
-     [HAVE_LIBSELINUX=yes],
-     [HAVE_LIBSELINUX=no])
+   if test "$REALLY_ANDROID" = "yes"; then
+     ndk_SEARCH_MODULE([libselinux], [LIBSELINUX],
+       [HAVE_LIBSELINUX=yes])
+   else
+     AC_CHECK_LIB([selinux], [lgetfilecon],
+       [HAVE_LIBSELINUX=yes],
+       [HAVE_LIBSELINUX=no])
+   fi
    if test "$HAVE_LIBSELINUX" = yes; then
       AC_DEFINE([HAVE_LIBSELINUX], [1], [Define to 1 if using SELinux.])
-      LIBSELINUX_LIBS=-lselinux
+      if test "$REALLY_ANDROID" != "yes"; then
+        LIBSELINUX_LIBS=-lselinux
+      fi
    fi
 fi
 AC_SUBST([LIBSELINUX_LIBS])
+AC_SUBST([LIBSELINUX_CFLAGS])
 
 HAVE_GNUTLS=no
 if test "${with_gnutls}" != "no" ; then
@@ -3843,17 +4638,23 @@ else
 fi
 if test "${HAVE_X11}" = "yes" && test "${HAVE_FREETYPE}" = "yes" \
         || test "$window_system" = "pgtk" \
-        || test "${HAVE_W32}" = "yes"; then
+        || test "${HAVE_W32}" = "yes" \
+       || test "$REALLY_ANDROID" = "yes"; then
   if test "${with_harfbuzz}" != "no"; then
     EMACS_CHECK_MODULES([HARFBUZZ], [harfbuzz >= $harfbuzz_required_ver])
-    if test "$HAVE_HARFBUZZ" = "yes"; then
+    AS_IF([test "$HAVE_HARFBUZZ" = "yes"],[
       AC_DEFINE([HAVE_HARFBUZZ], [1], [Define to 1 if using HarfBuzz.])
       ### mingw32 and Cygwin-w32 don't use -lharfbuzz, since they load
       ### the library dynamically.
-      if test "${HAVE_W32}" = "yes"; then
-        HARFBUZZ_LIBS=
-      fi
-    fi
+      AS_IF([test "${HAVE_W32}" = "yes"], [HARFBUZZ_LIBS=])
+      ## Now check for `hb_font_set_var_named_instance'.
+      OLD_CFLAGS=$CFLAGS
+      CFLAGS="$HARFBUZZ_CFLAGS $CFLAGS"
+      EMACS_CHECK_LIB([harfbuzz], [hb_font_set_var_named_instance],
+        [AC_DEFINE([HAVE_HB_FONT_SET_VAR_NAMED_INSTANCE], [1],
+         [Define to 1 if `hb_font_set_var_named_instance' is present.])],
+       [], [$HARFBUZZ_LIBS], [#include <hb.h>])
+      CFLAGS=$OLD_CFLAGS])
   fi
 fi
 
@@ -4036,51 +4837,66 @@ AC_SUBST([LIBXPM])
 ### Use -ljpeg if available, unless '--with-jpeg=no'.
 HAVE_JPEG=no
 LIBJPEG=
+JPEG_CFLAGS=
 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],
-      [OLD_LIBS=$LIBS
-       for emacs_cv_jpeglib in yes -ljpeg no; do
-        case $emacs_cv_jpeglib in
-          yes) ;;
-          no) break;;
-          *) LIBS="$LIBS $emacs_cv_jpeglib";;
-        esac
-        AC_LINK_IFELSE(
-          [AC_LANG_PROGRAM(
-             [[#undef HAVE_STDLIB_H /* Avoid config.h/jpeglib.h collision.  */
-               #include <stdio.h> /* jpeglib.h needs FILE and size_t.  */
-               #include <jpeglib.h>
-               #include <jerror.h>
-               char verify[JPEG_LIB_VERSION < 62 ? -1 : 1];
-               struct jpeg_decompress_struct cinfo;
-             ]],
-             [[
-               jpeg_create_decompress (&cinfo);
-               WARNMS (&cinfo, JWRN_JPEG_EOF);
-               jpeg_destroy_decompress (&cinfo);
-             ]])],
-          [emacs_link_ok=yes],
-          [emacs_link_ok=no])
-        LIBS=$OLD_LIBS
-        test $emacs_link_ok = yes && break
-       done])
-    if test "$emacs_cv_jpeglib" != no; then
-      HAVE_JPEG=yes
-      AC_DEFINE([HAVE_JPEG], [1],
-       [Define to 1 if you have the jpeg library (typically -ljpeg).])
-      ### mingw32 doesn't use -ljpeg, since it loads the library
-      ### dynamically when needed, and doesn't want a run-time
-      ### dependency on the jpeglib DLL.
-      test "$emacs_cv_jpeglib" != yes && test "${opsys}" != "mingw32" \
-      && LIBJPEG=$emacs_cv_jpeglib
+    if test "${REALLY_ANDROID}" = "yes"; then
+      # Look for libjpeg using the NDK.
+      ndk_SEARCH_MODULE([libjpeg], [JPEG], [HAVE_JPEG=yes])
+
+      if test "$HAVE_JPEG" = "yes"; then
+        LIBJPEG="$JPEG_LIBS"
+
+       AC_DEFINE([HAVE_JPEG], [1],
+         [Define to 1 if you have the jpeg library (typically -ljpeg).])
+      fi
+    else
+      AC_CACHE_CHECK([for jpeglib 6b or later],
+       [emacs_cv_jpeglib],
+       [OLD_LIBS=$LIBS
+        for emacs_cv_jpeglib in yes -ljpeg no; do
+          case $emacs_cv_jpeglib in
+            yes) ;;
+            no) break;;
+            *) LIBS="$LIBS $emacs_cv_jpeglib";;
+          esac
+          AC_LINK_IFELSE(
+            [AC_LANG_PROGRAM(
+               [[#undef HAVE_STDLIB_H /* Avoid config.h/jpeglib.h collision.  
*/
+                 #include <stdio.h> /* jpeglib.h needs FILE and size_t.  */
+                 #include <jpeglib.h>
+                 #include <jerror.h>
+                 char verify[JPEG_LIB_VERSION < 62 ? -1 : 1];
+                 struct jpeg_decompress_struct cinfo;
+               ]],
+               [[
+                 jpeg_create_decompress (&cinfo);
+                 WARNMS (&cinfo, JWRN_JPEG_EOF);
+                 jpeg_destroy_decompress (&cinfo);
+               ]])],
+            [emacs_link_ok=yes],
+            [emacs_link_ok=no])
+          LIBS=$OLD_LIBS
+          test $emacs_link_ok = yes && break
+        done])
+      if test "$emacs_cv_jpeglib" != no; then
+       HAVE_JPEG=yes
+       AC_DEFINE([HAVE_JPEG], [1],
+         [Define to 1 if you have the jpeg library (typically -ljpeg).])
+       ### mingw32 doesn't use -ljpeg, since it loads the library
+       ### dynamically when needed, and doesn't want a run-time
+       ### dependency on the jpeglib DLL.
+       test "$emacs_cv_jpeglib" != yes && test "${opsys}" != "mingw32" \
+       && LIBJPEG=$emacs_cv_jpeglib
+      fi
     fi
   fi
 fi
 AC_SUBST([LIBJPEG])
+AC_SUBST([JPEG_CFLAGS])
 
 HAVE_LCMS2=no
 LCMS2_CFLAGS=
@@ -4153,50 +4969,43 @@ if test $window_system = pgtk; then
   esac
 fi
 
-if test "${with_modules}" != "no"; then
-  case $opsys in
-    gnu|gnu-linux)
-      LIBMODULES="-ldl"
-      HAVE_MODULES=yes
-      ;;
-    cygwin|mingw32|darwin)
-      HAVE_MODULES=yes
-      ;;
-    *)
-      # BSD systems have dlopen in libc.
-      AC_CHECK_FUNC([dlopen], [HAVE_MODULES=yes])
-      ;;
-  esac
-
-  if test "${HAVE_MODULES}" = no; then
-    AC_MSG_ERROR([Dynamic modules are not supported on your system])
-  else
-    SAVE_LIBS=$LIBS
-    LIBS="$LIBS $LIBMODULES"
-    AC_CHECK_FUNCS([dladdr dlfunc])
-    LIBS=$SAVE_LIBS
-  fi
-fi
+AS_IF([test "x$with_modules" != "xno"],
+  [AS_CASE(["$opsys"],
+    [gnu|gnu-linux],
+      [LIBMODULES="-ldl"
+       HAVE_MODULES=yes],
+    [cygwin|mingw32|darwin],
+      [HAVE_MODULES=yes],
+    # BSD systems have dlopen in libc.
+    [AC_CHECK_FUNC([dlopen], [HAVE_MODULES=yes])])
+
+  AS_IF([test "x$HAVE_MODULES" = "xno"],
+    [AS_IF([test "$with_modules" = "ifavailable"],
+      [AC_MSG_WARN([Dynamic modules are not supported on your system])],
+      [AC_MSG_ERROR([Dynamic modules are not supported on your system])])],
+    [SAVE_LIBS=$LIBS
+     LIBS="$LIBS $LIBMODULES"
+     AC_CHECK_FUNCS([dladdr dlfunc])
+     LIBS=$SAVE_LIBS])])
+
+AS_IF([test "x$HAVE_MODULES" = xyes],
+   [MODULES_OBJ="emacs-module.o"
+    NEED_DYNLIB=yes
+    AC_DEFINE([HAVE_MODULES], [1], [Define to 1 if dynamic modules are 
enabled])
+    AC_DEFINE_UNQUOTED([MODULES_SUFFIX], ["$MODULES_SUFFIX"],
+      [System extension for dynamic libraries])
+    AS_IF([test -n "$MODULES_SECONDARY_SUFFIX"],
+      [AC_DEFINE_UNQUOTED([MODULES_SECONDARY_SUFFIX],
+       ["$MODULES_SECONDARY_SUFFIX"],
+       [Alternative system extension for dynamic libraries.])])])
 
-if test "${HAVE_MODULES}" = yes; then
-   MODULES_OBJ="emacs-module.o"
-   NEED_DYNLIB=yes
-   AC_DEFINE([HAVE_MODULES], [1], [Define to 1 if dynamic modules are enabled])
-   AC_DEFINE_UNQUOTED([MODULES_SUFFIX], ["$MODULES_SUFFIX"],
-     [System extension for dynamic libraries])
-   if test -n "${MODULES_SECONDARY_SUFFIX}"; then
-     AC_DEFINE_UNQUOTED([MODULES_SECONDARY_SUFFIX],
-       ["$MODULES_SECONDARY_SUFFIX"],
-       [Alternative system extension for dynamic libraries.])
-   fi
-fi
 AC_SUBST([MODULES_OBJ])
 AC_SUBST([LIBMODULES])
 AC_SUBST([HAVE_MODULES])
 AC_SUBST([MODULES_SUFFIX])
 AC_SUBST([MODULES_SECONDARY_SUFFIX])
 
-AC_CONFIG_FILES([src/emacs-module.h])
+ARCH_INDEPENDENT_CONFIG_FILES([src/emacs-module.h])
 AC_SUBST_FILE([module_env_snippet_25])
 AC_SUBST_FILE([module_env_snippet_26])
 AC_SUBST_FILE([module_env_snippet_27])
@@ -4386,7 +5195,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
@@ -4453,6 +5263,7 @@ AC_SUBST([PNG_CFLAGS])
 ### mingw32 doesn't use -ltiff, since it loads the library dynamically.
 HAVE_TIFF=no
 LIBTIFF=
+TIFF_CFLAGS=
 if test "${opsys}" = "mingw32"; then
   if test "${with_tiff}" != "no"; then
     AC_CHECK_HEADER([tiffio.h], [HAVE_TIFF=yes], [HAVE_TIFF=no])
@@ -4463,28 +5274,42 @@ if test "${opsys}" = "mingw32"; then
   fi
 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
   if test "${with_tiff}" != "no"; then
-    AC_CHECK_HEADER([tiffio.h],
-      [tifflibs="-lz -lm"
-      # At least one tiff package requires the jpeg library.
-      if test "${HAVE_JPEG}" = yes; then tifflibs="-ljpeg $tifflibs"; fi
-      AC_CHECK_LIB([tiff], [TIFFGetVersion], [HAVE_TIFF=yes], [],
-       [$tifflibs])])
+    if test "${REALLY_ANDROID}" != "yes"; then
+      AC_CHECK_HEADER([tiffio.h],
+       [tifflibs="-lz -lm"
+       # At least one tiff package requires the jpeg library.
+       if test "${HAVE_JPEG}" = yes; then tifflibs="-ljpeg $tifflibs"; fi
+       AC_CHECK_LIB([tiff], [TIFFGetVersion], [HAVE_TIFF=yes], [],
+         [$tifflibs])])
+    else
+      ndk_SEARCH_MODULE([libtiff], [TIFF], [HAVE_TIFF=yes])
+
+      if test "$HAVE_TIFF" = "yes"; then
+        LIBTIFF="$TIFF_LIBS"
+      fi
+    fi
   fi
 
   if test "${HAVE_TIFF}" = "yes"; then
     AC_DEFINE([HAVE_TIFF], [1],
       [Define to 1 if you have the tiff library (-ltiff).])
-    dnl FIXME -lz -lm, as per libpng?
-    LIBTIFF=-ltiff
+
+    if test "$REALLY_ANDROID" != "yes"; then
+      dnl FIXME -lz -lm, as per libpng?
+      LIBTIFF=-ltiff
+    fi
   fi
 fi
 AC_SUBST([LIBTIFF])
+AC_SUBST([TIFF_CFLAGS])
 
 ### 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
@@ -4497,6 +5322,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.
@@ -4516,12 +5342,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=
@@ -4860,10 +5694,13 @@ if test "${with_xml2}" != "no"; then
   fi
   if test "${HAVE_LIBXML2}" = "yes"; then
     if test "${opsys}" != "mingw32"; then
-      AC_CHECK_LIB([xml2], [htmlReadMemory],
+      SAVE_CFLAGS=$CFLAGS
+      CFLAGS="$CFLAGS $LIBXML2_CFLAGS"
+      EMACS_CHECK_LIB([xml2], [htmlReadMemory],
        [HAVE_LIBXML2=yes],
        [HAVE_LIBXML2=no],
-        [$LIBXML2_LIBS])
+        [$LIBXML2_LIBS], [#include <libxml/HTMLparser.h>])
+      CFLAGS="$SAVE_CFLAGS"
     else
       LIBXML2_LIBS=""
     fi
@@ -4989,7 +5826,7 @@ OLD_LIBS=$LIBS
 LIBS="$LIB_PTHREAD $LIB_MATH $LIBS"
 AC_CHECK_FUNCS([accept4 fchdir gethostname \
 getrusage get_current_dir_name \
-lrand48 random rint trunc \
+lrand48 random rint tcdrain trunc \
 select getpagesize setlocale newlocale \
 getrlimit setrlimit shutdown \
 pthread_sigmask strsignal setitimer \
@@ -4999,6 +5836,40 @@ getpwent endpwent getgrent endgrent \
 renameat2 \
 cfmakeraw cfsetspeed __executable_start log2 pthread_setname_np \
 pthread_set_name_np])
+
+if test "$ac_cv_func_cfmakeraw" != "yes"; then
+  # On some systems (Android), cfmakeraw is inline, so AC_CHECK_FUNCS
+  # cannot find it.  Check if some code including termios.h and using
+  # cfmakeraw builds.
+  AC_CACHE_CHECK([whether cfmakeraw is inline],
+   [emacs_cv_func_cfmakeraw_inline],
+   [AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+       [[#include <termios.h>]],
+       [[&cfmakeraw;]])],
+     [emacs_cv_func_cfmakeraw_inline=yes],
+     [emacs_cv_func_cfmakeraw_inline=no])])
+
+  if test "$emacs_cv_func_cfmakeraw_inline" = "yes"; then
+     # Define HAVE_CFMAKERAW again.
+     AC_DEFINE([HAVE_CFMAKERAW], [1])
+  fi
+fi
+
+if test "$ac_cv_func_cfsetspeed" != "yes"; then
+  AC_CACHE_CHECK([whether cfsetspeed is inline],
+   [emacs_cv_func_cfsetspeed_inline],
+   [AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+       [[#include <termios.h>]],
+       [[&cfsetspeed;]])],
+     [emacs_cv_func_cfsetspeed_inline=yes],
+     [emacs_cv_func_cfsetspeed_inline=no])])
+
+  if test "$emacs_cv_func_cfsetspeed_inline" = "yes"; then
+     # Define HAVE_CFSETSPEED again.
+     AC_DEFINE([HAVE_CFSETSPEED], [1])
+  fi
+fi
+
 LIBS=$OLD_LIBS
 
 if test "$ac_cv_func_pthread_setname_np" = "yes"; then
@@ -5050,8 +5921,8 @@ if test "$with_unexec" = yes && test "$opsys" = "haiku"; 
then
 Please use the portable dumper instead.])
 fi
 
-# Dump loading
-AC_CHECK_FUNCS([posix_madvise])
+# Dump loading.  Android lacks posix_madvise.
+AC_CHECK_FUNCS([posix_madvise madvise])
 
 dnl Cannot use AC_CHECK_FUNCS
 AC_CACHE_CHECK([for __builtin_frame_address],
@@ -5104,7 +5975,7 @@ AC_DEFUN([tputs_link_source], [
 # than to expect to find it in ncurses.
 # Also we need tputs and friends to be able to build at all.
 AC_CACHE_CHECK([for library containing tputs], [emacs_cv_tputs_lib],
-[if test "${opsys}" = "mingw32"; then
+[if test "${opsys}" = "mingw32" || test "$opsys" = "android"; then
   emacs_cv_tputs_lib='none required'
 else
   # curses precedes termcap because of AIX (Bug#9736#35) and OpenIndiana.
@@ -5171,7 +6042,7 @@ fail;
     fi
     ;;
 
-  mingw32)
+  mingw32 | android)
     TERMINFO=no
     LIBS_TERMCAP=
     ;;
@@ -5722,6 +6593,25 @@ case $opsys in
     AC_DEFINE([FIRST_PTY_LETTER], ['p'])
     ;;
 
+  android )
+    AC_DEFINE([PTY_ITERATION], [int i; for (i = 0; i < 1; ++i)])
+    dnl grantpt may be defined in libc but not declared.  The same
+    dnl goes for posix_openpt.  When that is the case, it means that
+    dnl grantpt or posix_openpt cannot actually be used.
+    AC_CHECK_DECLS([grantpt, posix_openpt])
+    AS_IF([test "x$ac_cv_have_decl_grantpt" = xyes],
+      [AC_DEFINE([PTY_TTY_NAME_SPRINTF],
+        [{ char *ptyname = 0; sigset_t blocked; sigemptyset (&blocked); 
sigaddset (&blocked, SIGCHLD); pthread_sigmask (SIG_BLOCK, &blocked, 0); if 
(grantpt (fd) != -1 && unlockpt (fd) != -1) ptyname = ptsname(fd); 
pthread_sigmask (SIG_UNBLOCK, &blocked, 0); if (!ptyname) { emacs_close (fd); 
return -1; } snprintf (pty_name, PTY_NAME_SIZE, "%s", ptyname); }])],
+      [AC_DEFINE([PTY_TTY_NAME_SPRINTF],
+        [{ char *ptyname = 0; sigset_t blocked; sigemptyset (&blocked); 
sigaddset (&blocked, SIGCHLD); pthread_sigmask (SIG_BLOCK, &blocked, 0); if 
(unlockpt (fd) != -1) ptyname = ptsname(fd); pthread_sigmask (SIG_UNBLOCK, 
&blocked, 0); if (!ptyname) { emacs_close (fd); return -1; } snprintf 
(pty_name, PTY_NAME_SIZE, "%s", ptyname); }])])
+    AS_IF([test "x$ac_cv_have_decl_posix_openpt" = xyes],
+      [AC_DEFINE([PTY_OPEN],
+        [do { fd = posix_openpt (O_RDWR | O_CLOEXEC | O_NOCTTY); if (fd < 0 && 
errno == EINVAL) fd = posix_openpt (O_RDWR | O_NOCTTY); } while (false)])
+       AC_DEFINE([PTY_NAME_SPRINTF], [])],
+      [AC_DEFINE([PTY_NAME_SPRINTF], [])
+       AC_DEFINE([PTY_OPEN], [fd = getpt ()])])
+    ;;
+
   gnu-linux | gnu-kfreebsd | dragonfly | freebsd | openbsd | netbsd | darwin | 
nacl )
     dnl if HAVE_GRANTPT
     if test "x$ac_cv_func_grantpt" = xyes; then
@@ -6129,6 +7019,9 @@ AC_DEFINE_UNQUOTED([COPYRIGHT], ["$copyright"],
   [Short copyright string for this version of Emacs.])
 AC_SUBST([copyright])
 
+# This is needed for gnulib's printf modules.
+CFLAGS="$CFLAGS -DHAVE_CONFIG_H"
+
 ### Specify what sort of things we'll be editing into Makefile and config.h.
 ### Use configuration here uncanonicalized to avoid exceeding size limits.
 AC_SUBST([version])
@@ -6402,6 +7295,38 @@ gl_INIT
 CFLAGS=$SAVE_CFLAGS
 LIBS=$SAVE_LIBS
 
+# Set up libgmp on Android.  Make sure to override what gnulib has
+# found.
+LIBGMP_CFLAGS=
+if test "$REALLY_ANDROID" = "yes" && test "$with_libgmp" != "no"; then
+  HAVE_LIBGMP=no
+  ndk_SEARCH_MODULE([libgmp], [LIBGMP], [HAVE_LIBGMP=yes])
+
+  if test "$HAVE_LIBGMP" = "yes"; then
+    SAVE_CFLAGS="$CFLAGS"
+    CFLAGS="$CFLAGS $LIBGMP_CFLAGS"
+    unset ac_cv_header_gmp_h
+    unset ac_cv_header_gmp_gmp_h
+    AC_CHECK_HEADERS([gmp.h gmp/gmp.h], [break])
+    CFLAGS="$SAVE_CFLAGS"
+    GL_GENERATE_GMP_H=
+    GL_GENERATE_GMP_H_CONDITION=
+    GL_GENERATE_GMP_GMP_H=
+    GL_GENERATE_GMP_GMP_H_CONDITION=
+    GL_GENERATE_MINI_GMP_H=
+    GL_GENERATE_MINI_GMP_H_CONDITION=
+
+    if test "$ac_cv_header_gmp_h" != "no" \
+       || test "$ac_cv_header_gmp_gmp_h" != "no"; then
+       HAVE_LIBGMP=yes
+       GL_GENERATE_GMP_H=false
+       LIBGMP="$LIBGMP_LIBS"
+       GMP_H=
+    fi
+  fi
+fi
+AC_SUBST([LIBGMP_CFLAGS])
+
 # timer_getoverrun needs the same library as timer_settime
 OLD_LIBS=$LIBS
 LIBS="$LIB_TIMER_TIME $LIBS"
@@ -6528,7 +7453,7 @@ if test "$window_system" != "none"; then
     [Define if you poll periodically to detect C-g.])
   WINDOW_SYSTEM_OBJ="fontset.o fringe.o image.o"
 
-  if test "$window_system" = "x11"; then
+  if test "$window_system" = "x11" || test "$REALLY_ANDROID" = "yes"; then
     AC_DEFINE([HAVE_TEXT_CONVERSION], [1],
       [Define if the window system has text conversion support.])
     WINDOW_SYSTEM_OBJ="$WINDOW_SYSTEM_OBJ textconv.o"
@@ -6537,6 +7462,35 @@ fi
 
 AC_SUBST([WINDOW_SYSTEM_OBJ])
 
+# Some systems have MB_CUR_MAX defined to a call to
+# __ctype_get_mb_cur_max, but do not have __ctype_get_mb_cur_max in
+# libc.  Check for that situation and define MB_CUR_MAX to something
+# sane.
+
+AC_CHECK_FUNC([__ctype_get_mb_cur_max])
+
+AC_CACHE_CHECK([whether MB_CUR_MAX is defined to function that won't link],
+  [emacs_cv_broken_mb_cur_max],
+  [AC_EGREP_CPP(__ctype_get_mb_cur_max, [
+#include <stdlib.h>
+#ifndef MB_CUR_MAX
+#define MB_CUR_MAX -1
+#endif
+static int foo = MB_CUR_MAX;
+], [AS_IF([test "$ac_cv_func___ctype_get_mb_cur_max" = "yes"],
+     [emacs_cv_broken_mb_cur_max=no],
+     [emacs_cv_broken_mb_cur_max=yes])],
+  [emacs_cv_broken_mb_cur_max=no])])
+
+AS_IF([test "$emacs_cv_broken_mb_cur_max" = "yes"],
+ # Define this to 4, which is right for Android.
+ [AS_CASE([$opsys], [android],
+   [AC_DEFINE([REPLACEMENT_MB_CUR_MAX], [4],
+      [Define to MB_CUR_MAX if stdlib.h is broken.])],
+   [AC_MSG_ERROR([MB_CUR_MAX does not work on your system.
+Please modify configure.ac to set an appropriate value, then
+send your change to bug-gnu-emacs@gnu.org])])])
+
 AH_TOP([/* GNU Emacs site configuration template file.
 
 Copyright (C) 1988, 1993-1994, 1999-2002, 2004-2021
@@ -6657,7 +7611,12 @@ done
 AC_DEFINE_UNQUOTED([EMACS_CONFIG_FEATURES], ["${emacs_config_features}"],
   [Summary of some of the main features enabled by configure.])
 
+# This is just a printable representation of the shared user ID.
+android_shared_user=
+AS_IF([test -n 
"$with_shared_user_id"],[android_shared_user="($with_shared_user_id)"])
+
 AS_ECHO(["  Does Emacs use -lXaw3d?                                 
${HAVE_XAW3D}
+  Is Emacs being built for Android?                       ${ANDROID} 
${android_shared_user}
   Does Emacs use the X Double Buffer Extension?           ${HAVE_XDBE}
   Does Emacs use -lXpm?                                   ${HAVE_XPM}
   Does Emacs use -ljpeg?                                  ${HAVE_JPEG}
@@ -6762,12 +7721,15 @@ fi
 AC_CONFIG_FILES([Makefile lib/gnulib.mk])
 
 dnl config.status treats $srcdir specially, so I think this is ok...
-AC_CONFIG_FILES([$srcdir/doc/man/emacs.1])
+ARCH_INDEPENDENT_CONFIG_FILES([$srcdir/doc/man/emacs.1])
+
+AC_CONFIG_FILES([lib/Makefile lib-src/Makefile oldXMenu/Makefile src/Makefile
+                 lwlib/Makefile nextstep/Makefile nt/Makefile])
+ARCH_INDEPENDENT_CONFIG_FILES([doc/emacs/Makefile doc/misc/Makefile
+                               doc/lispintro/Makefile doc/lispref/Makefile
+                              lisp/Makefile leim/Makefile])
 
-m4_define([subdir_makefiles],
-  [lib/Makefile lib-src/Makefile oldXMenu/Makefile doc/emacs/Makefile 
doc/misc/Makefile doc/lispintro/Makefile doc/lispref/Makefile src/Makefile 
lwlib/Makefile lisp/Makefile leim/Makefile nextstep/Makefile nt/Makefile])
-SUBDIR_MAKEFILES="subdir_makefiles"
-AC_CONFIG_FILES(subdir_makefiles)
+SUBDIR_MAKEFILES="lib/Makefile lib-src/Makefile oldXMenu/Makefile src/Makefile 
lwlib/Makefile nextstep/Makefile nt/Makefile doc/emacs/Makefile 
doc/misc/Makefile doc/lispintro/Makefile doc/lispref/Makefile lisp/Makefile 
leim/Makefile"
 
 dnl The test/ directory is missing if './make-dist --no-tests' was used.
 opt_makefile=test/Makefile
@@ -6775,24 +7737,27 @@ if test -f "$srcdir/$opt_makefile.in"; then
   SUBDIR_MAKEFILES="$SUBDIR_MAKEFILES $opt_makefile"
   dnl Again, it's best not to use a variable.  Though you can add
   dnl ", [], [opt_makefile='$opt_makefile']" and it should work.
-  AC_CONFIG_FILES([test/Makefile])
-  AC_CONFIG_FILES([test/manual/noverlay/Makefile])
+  ARCH_INDEPENDENT_CONFIG_FILES([test/Makefile])
+  ARCH_INDEPENDENT_CONFIG_FILES([test/manual/noverlay/Makefile])
 fi
 opt_makefile=test/infra/Makefile
 if test -f "$srcdir/$opt_makefile.in"; then
   SUBDIR_MAKEFILES="$SUBDIR_MAKEFILES $opt_makefile"
   dnl Again, it's best not to use a variable.  Though you can add
   dnl ", [], [opt_makefile='$opt_makefile']" and it should work.
-  AC_CONFIG_FILES([test/infra/Makefile])
+  ARCH_INDEPENDENT_CONFIG_FILES([test/infra/Makefile])
 fi
 
+if test "$ANDROID" = "yes"; then
+  SUBDIR_MAKEFILES="$SUBDIR_MAKEFILES java/Makefile cross/Makefile"
+fi
 
 dnl The admin/ directory used to be excluded from tarfiles.
 if test -d $srcdir/admin; then
   SUBDIR_MAKEFILES="$SUBDIR_MAKEFILES admin/charsets/Makefile 
admin/unidata/Makefile admin/grammars/Makefile"
-  AC_CONFIG_FILES([admin/charsets/Makefile])
-  AC_CONFIG_FILES([admin/unidata/Makefile])
-  AC_CONFIG_FILES([admin/grammars/Makefile])
+  ARCH_INDEPENDENT_CONFIG_FILES([admin/charsets/Makefile])
+  ARCH_INDEPENDENT_CONFIG_FILES([admin/unidata/Makefile])
+  ARCH_INDEPENDENT_CONFIG_FILES([admin/grammars/Makefile])
 fi                              dnl -d admin
 
 
@@ -6803,65 +7768,112 @@ AC_SUBST([SUBDIR_MAKEFILES_IN])
 SMALL_JA_DIC=$with_small_ja_dic
 AC_SUBST([SMALL_JA_DIC])
 
-dnl You might wonder (I did) why epaths.h is generated by running make,
-dnl rather than just letting configure generate it from epaths.in.
-dnl One reason is that the various paths are not fully expanded (see above);
-dnl e.g., gamedir='${localstatedir}/games/emacs'.
-dnl Secondly, the GNU Coding standards require that one should be able
-dnl to run 'make prefix=/some/where/else' and override the values set
-dnl by configure.  This also explains the 'move-if-change' test and
-dnl the use of force in the 'epaths-force' rule in Makefile.in.
-AC_CONFIG_COMMANDS([src/epaths.h], [
-if test "${opsys}" = "mingw32"; then
-  ${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile epaths-force-w32
-elif test "$HAVE_NS" = "yes" && test "$EN_NS_SELF_CONTAINED" = "yes"; then
-  ${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile 
epaths-force-ns-self-contained
-else
-  ${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile epaths-force
-fi || AC_MSG_ERROR(['src/epaths.h' could not be made.])
-], [GCC="$GCC" CPPFLAGS="$CPPFLAGS" opsys="$opsys" HAVE_NS="$HAVE_NS"
-    EN_NS_SELF_CONTAINED="$EN_NS_SELF_CONTAINED"])
-
-dnl NB we have to cheat and use the ac_... version because abs_top_srcdir
-dnl is not yet set, sigh.  Or we could use ../$srcdir/src/.gdbinit,
-dnl or a symlink?
-AC_CONFIG_COMMANDS([src/.gdbinit], [
-if test ! -f src/.gdbinit && test -f "$srcdir/src/.gdbinit"; then
-  AS_ECHO(["source $ac_abs_top_srcdir/src/.gdbinit"]) > src/.gdbinit
-fi
-])
+dnl The following commands are run on the host system when building
+dnl Emacs.
+
+if test "$XCONFIGURE" != "android"; then
+  dnl You might wonder (I did) why epaths.h is generated by running
+  dnl make, rather than just letting configure generate it from
+  dnl epaths.in.  One reason is that the various paths are not fully
+  dnl expanded (see above); e.g.,
+  dnl gamedir='${localstatedir}/games/emacs'.  Secondly, the GNU
+  dnl Coding standards require that one should be able to run 'make
+  dnl prefix=/some/where/else' and override the values set by
+  dnl configure.  This also explains the 'move-if-change' test and the
+  dnl use of force in the 'epaths-force' rule in Makefile.in.
+  AC_CONFIG_COMMANDS([src/epaths.h], [
+  if test "${opsys}" = "mingw32"; then
+    ${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile epaths-force-w32
+  elif test "$HAVE_NS" = "yes" && test "$EN_NS_SELF_CONTAINED" = "yes"; then
+    ${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile 
epaths-force-ns-self-contained
+  else
+    ${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile epaths-force
+  fi || AC_MSG_ERROR(['src/epaths.h' could not be made.])
+  ], [GCC="$GCC" CPPFLAGS="$CPPFLAGS" opsys="$opsys" HAVE_NS="$HAVE_NS"
+      EN_NS_SELF_CONTAINED="$EN_NS_SELF_CONTAINED"])
+
+  dnl NB we have to cheat and use the ac_... version because abs_top_srcdir
+  dnl is not yet set, sigh.  Or we could use ../$srcdir/src/.gdbinit,
+  dnl or a symlink?
+  AC_CONFIG_COMMANDS([src/.gdbinit], [
+  if test ! -f src/.gdbinit && test -f "$srcdir/src/.gdbinit"; then
+    AS_ECHO(["source $ac_abs_top_srcdir/src/.gdbinit"]) > src/.gdbinit
+  fi
+  ])
 
-dnl Perhaps this would be better named doc-emacs-emacsver.texi?
-dnl See comments for etc-refcards-emacsver.tex.
-dnl Since we get a doc/emacs directory generated anyway, for the Makefile,
-dnl it is not quite the same.  But we are generating in $srcdir.
-AC_CONFIG_COMMANDS([doc/emacs/emacsver.texi], [
-${MAKE-make} -s --no-print-directory -C doc/emacs doc-emacsver || \
-AC_MSG_ERROR(['doc/emacs/emacsver.texi' could not be made.])
-])
+  dnl Perhaps this would be better named doc-emacs-emacsver.texi?
+  dnl See comments for etc-refcards-emacsver.tex.
+  dnl Since we get a doc/emacs directory generated anyway, for the Makefile,
+  dnl it is not quite the same.  But we are generating in $srcdir.
+  AC_CONFIG_COMMANDS([doc/emacs/emacsver.texi], [
+  ${MAKE-make} -s --no-print-directory -C doc/emacs doc-emacsver || \
+  AC_MSG_ERROR(['doc/emacs/emacsver.texi' could not be made.])
+  ])
 
-dnl If we give this the more natural name, etc/refcards/emacsver.texi,
-dnl then a directory etc/refcards is created in the build directory,
-dnl which is probably harmless, but confusing (in out-of-tree builds).
-dnl (If we were to generate etc/refcards/Makefile, this might change.)
-dnl It is really $srcdir/etc/refcards/emacsver.tex that we generate.
-AC_CONFIG_COMMANDS([etc-refcards-emacsver.tex], [
-${MAKE-make} -s MAKEFILE_NAME=do-not-make-Makefile etc-emacsver || \
-AC_MSG_ERROR(['etc/refcards/emacsver.tex' could not be made.])
-])
+  dnl If we give this the more natural name, etc/refcards/emacsver.texi,
+  dnl then a directory etc/refcards is created in the build directory,
+  dnl which is probably harmless, but confusing (in out-of-tree builds).
+  dnl (If we were to generate etc/refcards/Makefile, this might change.)
+  dnl It is really $srcdir/etc/refcards/emacsver.tex that we generate.
+  AC_CONFIG_COMMANDS([etc-refcards-emacsver.tex], [
+  ${MAKE-make} -s MAKEFILE_NAME=do-not-make-Makefile etc-emacsver || \
+  AC_MSG_ERROR(['etc/refcards/emacsver.tex' could not be made.])
+  ])
 
-if test $AUTO_DEPEND = yes; then
-   for dir in $AUTODEPEND_PARENTS; do
-     AS_MKDIR_P([$dir/deps])
-   done
-fi
-if $gl_gnulib_enabled_dynarray || $gl_gnulib_enabled_scratch_buffer; then
-  AS_MKDIR_P([lib/malloc])
   if test $AUTO_DEPEND = yes; then
-    AS_MKDIR_P([lib/deps/malloc])
+     for dir in $AUTODEPEND_PARENTS; do
+       AS_MKDIR_P([$dir/deps])
+       AS_MKDIR_P([cross/$dir/deps])
+     done
   fi
+  if $gl_gnulib_enabled_dynarray || $gl_gnulib_enabled_scratch_buffer; then
+    AS_MKDIR_P([lib/malloc])
+    AS_MKDIR_P([cross/lib/malloc])
+    if test $AUTO_DEPEND = yes; then
+      AS_MKDIR_P([lib/deps/malloc])
+      AS_MKDIR_P([cross/lib/deps/malloc])
+    fi
+  fi
+
+  dnl Make cross/lib, which various Makefiles in cross expect to
+  dnl always exist.
+  AS_MKDIR_P([cross/lib])
+  AS_MKDIR_P([cross/lib/malloc])
+  AS_MKDIR_P([cross/lib/sys])
+  AS_MKDIR_P([cross/lib-src])
+
+  dnl Make cross/etc; this directory will hold the documentation file
+  dnl holding doc strings for Android specific C files that aren't
+  dnl built during the initial compilation of Emacs for the build
+  dnl machine.
+  AS_MKDIR_P([cross/etc])
+
+  dnl Link gnulib files to cross/lib as well.
+  dnl af_alg.h and lib/save-cwd.h are copied manually from
+  dnl gnulib, and as such aren't specified in gl_FILE_LIST.
+  emacs_files='gl_FILE_LIST lib/af_alg.h lib/save-cwd.h'
+  dnl These files are specific to Emacs.
+  emacs_files="$emacs_files lib/fingerprint.c lib/fingerprint.h \
+                 lib/save-cwd.c lib/openat-die.c lib/save-cwd.c \
+                lib/min-max.h"
+  for file in $emacs_files; do
+    AS_IF([expr "X${file}J" : "Xlib/.*[[ch]]J" >/dev/null],
+      [AS_IF([test -f "$srcdir/$file"],
+        [AC_CONFIG_LINKS([cross/$file:$file])])])
+  done
 fi
 
+# Make java/Makefile
+ARCH_INDEPENDENT_CONFIG_FILES([java/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/Makefile.in b/cross/Makefile.in
new file mode 100644
index 00000000000..a7b5700440e
--- /dev/null
+++ b/cross/Makefile.in
@@ -0,0 +1,190 @@
+### @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/>.
+
+top_srcdir = @top_srcdir@
+srcdir = @srcdir@
+top_builddir = @top_builddir@
+builddir = @builddir@
+
+-include $(top_builddir)/src/verbose.mk
+
+# Cross-compiling Emacs for Android.
+
+# The cross compiled binaries are built by having ``variant''
+# Makefiles generated at configure-time.  First,
+# $(top_builddir)/src/Makefile.android,
+# $(top_builddir)/lib/Makefile.android,
+# $(top_builddir)/lib/gnulib.mk.android and
+# $(top_builddir)/lib-src/Makefile.android are copied to their usual
+# locations in this directory.
+
+# N.B. that LIB_SRCDIR is actually relative to builddir, because that
+# is where the gnulib files get linked.
+
+LIB_SRCDIR = $(realpath $(builddir)/lib)
+LIB_TOP_SRCDIR = $(realpath $(top_srcdir))
+
+SRC_SRCDIR = $(realpath $(top_srcdir)/src)
+SRC_TOP_SRCDIR = $(realpath $(top_srcdir))
+
+LIB_SRC_SRCDIR = $(realpath $(top_srcdir)/lib-src)
+LIB_SRC_TOP_SRCDIR = $(realpath $(top_src))
+
+# This is a list of binaries to build and install in lib-src.
+
+LIBSRC_BINARIES = lib-src/etags lib-src/ctags lib-src/emacsclient \
+                 lib-src/ebrowse lib-src/hexl lib-src/movemail
+
+CLEAN_SUBDIRS = src lib-src lib etc
+
+.PHONY: all
+all: lib/libgnu.a src/libemacs.so src/android-emacs $(LIBSRC_BINARIES)
+
+# This Makefile relies on builddir and top_builddir being relative
+# paths in *.android.
+
+# This file is used to tell lib/gnulib.mk when
+# $(top_builddir)/config.status changes.
+config.status: $(top_builddir)/config.status
+       $(AM_V_GEN) touch config.status
+
+src/verbose.mk: $(srcdir)/verbose.mk.android
+       $(AM_V_SILENT) cp -f $(srcdir)/verbose.mk.android \
+         src/verbose.mk
+
+# Gnulib, make-fingerprint and make-docfile must be built before
+# entering any of the rules below, or they will get the Android
+# versions of many headers.
+
+.PHONY: $(top_builddir)/lib/libgnu.a
+$(top_builddir)/lib/libgnu.a:
+       $(MAKE) -C $(top_builddir)/lib libgnu.a
+
+.PHONY: $(top_builddir)/lib-src/make-fingerprint
+$(top_builddir)/lib-src/make-fingerprint: $(top_builddir)/lib/libgnu.a
+       $(MAKE) -C $(top_builddir)/lib-src make-fingerprint
+
+.PHONY: $(top_builddir)/lib-src/make-docfile
+$(top_builddir)/lib-src/make-docfile: $(top_builddir)/lib/libgnu.a
+       $(MAKE) -C $(top_builddir)/lib-src make-docfile
+
+PRE_BUILD_DEPS=$(top_builddir)/lib/libgnu.a            \
+              $(top_builddir)/lib-src/make-fingerprint \
+              $(top_builddir)/lib-src/make-docfile
+
+lib/config.h: $(top_builddir)/src/config.h.android
+       $(AM_V_GEN) cp -f -p $(top_builddir)/src/config.h.android       \
+         lib/config.h
+
+lib-src/config.h: $(top_builddir)/src/config.h.android
+       $(AM_V_GEN) cp -f -p $(top_builddir)/src/config.h.android       \
+         lib-src/config.h
+
+# Figure out where build-aux is.
+# Then, replace the build-aux directory with its actual location,
+# in case MKDIR_P points there.
+
+relative_buildaux_dir := $(subst /,\/,$(top_srcdir)/build-aux)
+
+lib/gnulib.mk: $(top_builddir)/lib/gnulib.mk.android
+       $(AM_V_GEN) \
+         sed -e 's/^srcdir =.*$$/srcdir = $(subst /,\/,$(LIB_SRCDIR))/g' \
+             -e 's/$(relative_buildaux_dir)/$(subst 
/,\/,../$(top_builddir))\/build-aux/g' \
+           < $(top_builddir)/lib/gnulib.mk.android > $@
+
+lib/Makefile: $(top_builddir)/lib/Makefile.android
+       $(AM_V_GEN) \
+         sed -e 's/^top_srcdir =.*$$/top_srcdir = $(subst 
/,\/,$(LIB_TOP_SRCDIR))/g' \
+             -e 's/^srcdir =.*$$/srcdir = $(subst /,\/,$(LIB_SRCDIR))/g' \
+             -e 's/^VPATH =.*$$/VPATH = $(subst /,\/,$(LIB_SRCDIR))/g' \
+               < $(top_builddir)/lib/Makefile.android > $@
+
+# What is needed to build gnulib.
+LIB_DEPS = lib/config.h lib/gnulib.mk lib/Makefile
+
+.PHONY: lib/libgnu.a
+lib/libgnu.a: src/verbose.mk config.status $(LIB_DEPS) $(PRE_BUILD_DEPS)
+       $(MAKE) -C lib libgnu.a
+
+# Edit srcdir and top_srcdir to the right locations.
+# Edit references to ../admin/unidata to read ../../admin/unidata.
+# Next, edit libsrc to the location at top_srcdir! It is important
+# that src/Makefile uses the binaries there, instead of any
+# cross-compiled binaries at ./lib-src.
+# Edit out anything saying -I($(top_srcdir)/lib) into
+# -I$../(srcdir)/lib; that should be covered by -I$(lib)
+
+src/Makefile: $(top_builddir)/src/Makefile.android
+       $(AM_V_GEN) \
+       sed -e 's/^srcdir =.*$$/srcdir = $(subst /,\/,$(SRC_SRCDIR))/g' \
+           -e 's/^top_srcdir =.*$$/top_srcdir = $(subst 
/,\/,$(LIB_TOP_SRCDIR))/g' \
+           -e 's/\.\.\/admin\/unidata/..\/..\/admin\/unidata/g' \
+           -e 's/\.\.\/admin\/charsets/..\/..\/admin\/charsets/g' \
+           -e 's/^libsrc =.*$$/libsrc = \.\.\/\.\.\/lib-src/g' \
+           -e 's/libsrc =.*$$/libsrc = \.\.\/\.\.\/lib-src/g' \
+           -e 's/-I\$$(top_srcdir)\/lib/-I..\/$(subst /,\/,$(srcdir))\/lib/g' \
+           < $(top_builddir)/src/Makefile.android > $@
+
+src/config.h: $(top_builddir)/src/config.h.android
+       $(AM_V_GEN) cp -f -p $< $@
+
+.PHONY: src/android-emacs src/libemacs.so
+
+src/libemacs.so: src/Makefile src/config.h src/verbose.mk \
+  lib/libgnu.a $(PRE_BUILD_DEPS)
+       $(MAKE) -C src libemacs.so
+
+src/android-emacs: src/Makefile src/config.h lib/libgnu.a \
+  $(PRE_BUILD_DEPS)
+       $(MAKE) -C src android-emacs
+
+# Edit out SCRIPTS, it interferes with the build.
+# Make BASE_CFLAGS also include cross/lib as well as ../lib.
+
+lib-src/Makefile: $(top_builddir)/lib-src/Makefile.android
+       $(AM_V_GEN) \
+       sed -e 's/-I\$${srcdir}\/\.\.\/lib//g' \
+           -e 's/^srcdir=.*$$/srcdir = $(subst /,\/,$(LIB_SRC_SRCDIR))/g' \
+           -e 's/^top_srcdir=.*$$/top_srcdir = $(subst 
/,\/,$(LIB_SRC_TOP_SRCDIR))/g' \
+           -e 's/^SCRIPTS=.*$$/SCRIPTS=/g' \
+           -e 's/-I\.\.\/lib/-I..\/lib -I..\/$(subst /,\/,$(srcdir))\/lib/g' \
+         < $(top_builddir)/lib-src/Makefile.android > $@
+
+.PHONY: $(LIBSRC_BINARIES)
+$(LIBSRC_BINARIES) &: src/verbose.mk $(top_builddir)/$@ lib/libgnu.a \
+  lib-src/config.h lib-src/Makefile $(PRE_BUILD_DEPS)
+# Finally, go into lib-src and make everything being built
+       $(MAKE) -C lib-src $(foreach bin,$(LIBSRC_BINARIES),$(notdir $(bin)))
+
+.PHONY: clean maintainer-clean distclean
+clean:
+       for dir in $(CLEAN_SUBDIRS); do \
+         find $$dir -type f -delete;   \
+       done
+       rm -rf lib/config.h lib-src/config.h
+# ndk-build won't have been generated in a non-Android build.
+       -make -C ndk-build clean
+
+maintainer-clean distclean bootstrap-clean: clean
+# Remove links created by configure.
+       for dir in $(CLEAN_SUBDIRS); do \
+         find $$dir -type l -delete;   \
+       done
+       rm -rf lib/Makefile lib/gnulib.mk ndk-build/Makefile
+       rm -rf ndk-build/ndk-build.mk Makefile
diff --git a/cross/README b/cross/README
new file mode 100644
index 00000000000..3ec6f2c0b3c
--- /dev/null
+++ b/cross/README
@@ -0,0 +1,5 @@
+This directory holds Makefiles and other required assets to build an
+Emacs binary independently for another toolchain.
+
+The directory ndk-build also contains an implementation of the Android
+`ndk-build' build system.
diff --git a/cross/langinfo.h b/cross/langinfo.h
new file mode 100644
index 00000000000..b296ba8db80
--- /dev/null
+++ b/cross/langinfo.h
@@ -0,0 +1,20 @@
+/* Replacement langinfo.h file for building GNU Emacs on Android.
+
+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/>.  */
+
+#define nl_langinfo(ignore)  "ASCII"
diff --git a/cross/ndk-build/Makefile.in b/cross/ndk-build/Makefile.in
new file mode 100644
index 00000000000..cdf18471ff3
--- /dev/null
+++ b/cross/ndk-build/Makefile.in
@@ -0,0 +1,144 @@
+### @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_CXX = @NDK_BUILD_CXX@
+        NDK_BUILD_AR = @NDK_BUILD_AR@
+      NDK_BUILD_NASM = @NDK_BUILD_NASM@
+    NDK_BUILD_CFLAGS = @NDK_BUILD_CFLAGS@
+
+# 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))
+
+# Remove duplicate modules as well.  These can occur when a single
+# module imports a module and also declares it in
+# LOCAL_SHARED_LIBRARIES.
+NDK_BUILD_MODULES := $(call uniqify,$(NDK_BUILD_MODULES))
+
+# Define CFLAGS for compiling C++ code; this involves removing all
+# -std=NNN options.
+NDK_BUILD_CFLAGS_CXX := $(filter-out -std=%,$(NDK_BUILD_CFLAGS))
+
+define subr-1
+
+# Define ndk-build functions.  Many of these are identical to those in
+# build-aux/ndk-build-helper.mk.
+
+# NDK_LAST_MAKEFILE is the last Makefile that was included.
+NDK_LAST_MAKEFILE = $$(lastword $$(filter %Android.mk,$$(MAKEFILE_LIST)))
+
+# local-makefile is the current Makefile being loaded.
+local-makefile = $$(NDK_LAST_MAKEFILE)
+
+# my-dir is a function that returns the Android module directory.  If
+# no Android.mk has been loaded, use the directory of the Makefile
+# being included.
+my-dir = $$(or $$(and $$(local-makefile),$$(dir $$(local-makefile))),$(dir 
$(1)))
+
+# Return all Android.mk files under the first arg.
+all-makefiles-under = $$(wildcard $$(1)/*/Android.mk)
+
+# Return all Android.mk files in subdirectories of this Makefile's
+# location.
+all-subdir-makefiles = $$(call all-makefiles-under,$$(call my-dir))
+
+# 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 :=
+LOCAL_ASM_RULE_DEFINED :=
+LOCAL_ASM_RULE :=
+
+# 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/README b/cross/ndk-build/README
new file mode 100644
index 00000000000..aca2e7230bf
--- /dev/null
+++ b/cross/ndk-build/README
@@ -0,0 +1,353 @@
+NDK BUILD SYSTEM IMPLEMENTATION
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+See the end of the file for license conditions.
+
+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
+
+seven 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
+  cross/ndk-build/ndk-resolve.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)
+    BUILD_AUXDIR=$ndk_AUX_DIR # the build-aux directory
+    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), possibly other
+``Android.mk'' files, (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 seventh line is either empty, or the name of a dependency on
+    the C++ standard library.  This is used to determine whether or
+    not Emacs will include the C++ standard library in the application
+    package.
+
+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"
+module_cxx_deps=""
+module_imports=""
+
+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.
+
+In some cases, an ``Android.mk'' file may chose to import a module
+defined in ``--with-ndk-path'', but not defined inside its own
+``Android.mk'' file.  build-aux/ndk-build-helper.mk defines the
+`import-module' function to add the modules being imported to a
+variable, which is then printed out after ``ndk-build-helper.mk''
+completes.  For example, libxml2 imports the ``libicucc'' module,
+which results in the following text being printed:
+
+Building shared
+libxml2
+/home/oldosfan/libxml2/SAX.c /home/oldosfan/libxml2/entities.c 
/home/oldosfan/libxml2/encoding.c /home/oldosfan/libxml2/error.c 
/home/oldosfan/libxml2/parserInternals.c /home/oldosfan/libxml2/parser.c 
/home/oldosfan/libxml2/tree.c /home/oldosfan/libxml2/hash.c 
/home/oldosfan/libxml2/list.c /home/oldosfan/libxml2/xmlIO.c 
/home/oldosfan/libxml2/xmlmemory.c /home/oldosfan/libxml2/uri.c 
/home/oldosfan/libxml2/valid.c /home/oldosfan/libxml2/xlink.c 
/home/oldosfan/libxml2/debugXML.c /home/oldo [...]
+
+
+  -L/home/oldosfan/emacs-dev/emacs-android/cross/ndk-build -l:libxml2_emacs.so 
-l:libicuuc_emacs.so
+libxml2_emacs.so libicuuc_emacs.so
+End
+Start Imports
+libicuuc
+End Imports
+
+Upon encountering the ``Start Imports'' section,
+build-aux/ndk-module-extract.awk collects all imports until it
+encounters the line ``End Imports'', at which point it prints:
+
+module_imports="libicuuc"
+
+Then, if the list of imports is not empty, ndk_CHECK_MODULES
+additionally calls itself for each import before appending the
+module's own ``Android.mk'', ensuring that the module's imported
+dependencies are included by $ndk_DIR/Makefile before itself.
+
+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.
+
+In doing so, they both include the file ndk-resolve.mk.
+ndk-resolve.mk is expected to recursively add all of the exported
+CFLAGS and includes of any dependencies to the compiler and linker
+command lines for the module being built.
+
+When building a shared library module, ndk-resolve.mk is also expected
+to define the variables NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) and
+NDK_WHOLE_A_NAMES_$(LOCAL_MODULE), containing all static library
+dependencies' archive files.  They are to be linked in to the
+resulting shared object file.
+
+This is done by including cross/ndk-build/ndk-resolve.mk each time a
+shared or static library module is going to be built.  How is this
+done?
+
+First, ndk-resolve.mk saves the LOCAL_PATH, LOCAL_STATIC_LIBRARIES,
+LOCAL_SHARED_LIBRARIES, LOCAL_EXPORT_CFLAGS and
+LOCAL_EXPORT_C_INCLUDES from the module.
+
+Next, ndk-resolve loops through the dependencies the module has
+specified, appending its CFLAGS and includes to the command line for
+the current module.
+
+Then, that process is repeated for each such dependency which has not
+already been resolved, until all dependencies have been resolved.
+
+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
+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/>.
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..d60802da1d0
--- /dev/null
+++ b/cross/ndk-build/ndk-build-shared-library.mk
@@ -0,0 +1,171 @@
+# 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)))
+
+# Objects for shared libraries are prefixed with `-shared-' in
+# addition to the name of the module, because a common practice in
+# Android.mk files written by Google is to define two modules with the
+# same name but of different types.
+objname = $(1)-shared-$(subst /,_,$(2).o)
+
+# LOCAL_SRC_FILES sometimes contains absolute file names.  Filter them
+# out with this function.  If $(2), this is a file relative to the
+# build directory.
+maybe-absolute = $(or $(and $(2),$(1)),$(and $(wildcard 
$(1)),$(1)),$(LOCAL_PATH)/$(1))
+
+# Here are the default flags to link shared libraries with.
+NDK_SO_DEFAULT_LDFLAGS := -lc -lm
+
+define single-object-target
+
+ifeq (x$(suffix $(1)),x.c)
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call 
maybe-absolute,$(1),$(2))
+       $(NDK_BUILD_CC) -c $$< -o $$@ $(NDK_CFLAGS_$(LOCAL_MODULE)) 
$(NDK_BUILD_CFLAGS) $(call LOCAL_C_ADDITIONAL_FLAGS,$(1))
+
+else
+ifeq (x$(suffix $(1)),x.$(or $(LOCAL_CPP_EXTENSION),cpp))
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1))
+       $(NDK_BUILD_CXX) -c $$< -o $$@ $(NDK_CFLAGS_$(LOCAL_MODULE)) 
$(NDK_BUILD_CFLAGS_CXX) $(NDK_CXXFLAGS_$(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))): $(call 
maybe-absolute,$(1),$(2))
+       $(NDK_BUILD_CC) -c $$< -o $$@ $(NDK_ASFLAGS_$(LOCAL_MODULE))
+
+else
+ifneq (x$(suffix $(1)),x.asm)
+ifeq (x$(suffix $(1)),x.cc)
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call 
maybe-absolute,$(1),$(2))
+       $(NDK_BUILD_CXX) -c $$< -o $$@ $(NDK_CFLAGS_$(LOCAL_MODULE)) 
$(NDK_BUILD_CFLAGS_CXX) $(NDK_CXXFLAGS_$(LOCAL_MODULE))
+
+else
+$$(error Unsupported suffix: $(suffix $(1)))
+endif
+else
+ifneq (x$(LOCAL_ASM_RULE_DEFINED),x)
+# Call this function to define a rule that will generate $(1) from
+# $(2), a ``.asm'' file.  This is an Emacs extension.
+
+$(call LOCAL_ASM_RULE,$(call objname,$(LOCAL_MODULE),$(basename 
$(1))),$(LOCAL_PATH)/$(strip $(1)))
+
+else
+ifeq ($(findstring x86,$(NDK_BUILD_ARCH)),)
+$$(error Trying to build nasm file on non-Intel platform!)
+else
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(LOCAL_PATH)/$(1)
+       $(NDK_BUILD_NASM) -felf$(findstring 64,$(NDK_BUILD_ARCH)) -o $$@ -i 
$(LOCAL_PATH) -i $$(dir $$<) $(NDK_ASFLAGS_$(LOCAL_MODULE)) $$<
+
+endif
+endif
+endif
+endif
+endif
+endif
+
+ALL_OBJECT_FILES$(LOCAL_MODULE) += $(call objname,$(LOCAL_MODULE),$(basename 
$(1)))
+
+endef
+
+define single-neon-target
+
+# Define rules for the target.
+$$(eval $$(call single-object-target,$(patsubst %.neon,%,$(1)),))
+
+endef
+
+# Make sure to not add a prefix to local includes that already specify
+# $(LOCAL_PATH).
+NDK_CFLAGS_$(LOCAL_MODULE)      := $(addprefix -I,$(LOCAL_C_INCLUDES))
+NDK_CFLAGS_$(LOCAL_MODULE)      += -fPIC -iquote $(LOCAL_PATH) 
$(LOCAL_EXPORT_CFLAGS) $(LOCAL_CFLAGS) $(LOCAL_CFLAGS_$(NDK_BUILD_ARCH))
+NDK_ASFLAGS_$(LOCAL_MODULE) := $(LOCAL_ASFLAGS) 
$(LOCAL_ASFLAGS_$(NDK_BUILD_ARCH)) $(and $(findstring 
clang,$(NDK_BUILD_CC)),$(LOCAL_CLANG_ASFLAGS_$(NDK_BUILD_ARCH)))
+NDK_LDFLAGS_$(LOCAL_MODULE) := $(LOCAL_LDLIBS) $(LOCAL_LDFLAGS)
+NDK_CXXFLAGS_$(LOCAL_MODULE) := $(LOCAL_CPPFLAGS) $(LOCAL_RTTI_FLAG)
+
+# Now look for features in LOCAL_CPP_FEATURES and enable them.
+
+ifneq ($(findstring exceptions,$(LOCAL_CPPFLAGS)),)
+NDK_CXXFLAGS_$(LOCAL_MODULE) += -fexceptions
+endif
+
+ifneq ($(findstring rtti,$(LOCAL_CPPFLAGS)),)
+NDK_CXXFLAGS_$(LOCAL_MODULE) += -frtti
+endif
+
+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
+
+ifeq ($(findstring lib,$(LOCAL_MODULE)),lib)
+LOCAL_MODULE_FILENAME := $(LOCAL_MODULE)_emacs
+else
+LOCAL_MODULE_FILENAME := lib$(LOCAL_MODULE)_emacs
+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
+
+# Record this module's dependencies and exported includes and CFLAGS,
+# and then add that of its dependencies.
+
+include $(srcdir)/ndk-resolve.mk
+
+# 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) :=
+
+# Now filter out code that is built with neon.  Define rules to build
+# those separately.
+NEON_SOURCE_FILES := $(filter %.neon,$(ALL_SOURCE_FILES))
+ALL_SOURCE_FILES  := $(filter-out %.neon,$(ALL_SOURCE_FILES))
+
+$(foreach source,$(ALL_SOURCE_FILES),$(eval $(call 
single-object-target,$(source),)))
+$(foreach source,$(NEON_SOURCE_FILES),$(eval $(call 
single-neon-target,$(source))))
+
+# Now define the rule to build the shared library.  Shared libraries
+# link with all of the archive files from the static libraries on
+# which they depend, and also any shared libraries they depend on.
+
+define define-module-rule
+$(LOCAL_MODULE_FILENAME): $(ALL_OBJECT_FILES$(LOCAL_MODULE)) 
$(NDK_LOCAL_A_NAMES_$(LOCAL_MODULE)) $(NDK_WHOLE_A_NAMES_$(LOCAL_MODULE)) 
$(NDK_LOCAL_SO_NAMES_$(LOCAL_MODULE))
+       $(NDK_BUILD_CC) $(1) $(2) -o $$@ -shared $(NDK_LDFLAGS_$(LOCAL_MODULE)) 
$(NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE)) $(NDK_SO_DEFAULT_LDFLAGS) $(foreach 
so,$(NDK_LOCAL_SO_NAMES_$(LOCAL_MODULE)),-L $(abspath $(CURDIR)) -l:$(so))
+endef
+
+NDK_WHOLE_ARCHIVE_PREFIX = -Wl,--whole-archive
+NDK_WHOLE_ARCHIVE_SUFFIX = -Wl,--no-whole-archive
+
+$(eval $(call define-module-rule,$(ALL_OBJECT_FILES$(LOCAL_MODULE)) 
$(NDK_LOCAL_A_NAMES_$(LOCAL_MODULE)),$(and $(strip 
$(NDK_WHOLE_A_NAMES_$(LOCAL_MODULE))),$(NDK_WHOLE_ARCHIVE_PREFIX) 
$(NDK_WHOLE_A_NAMES_$(LOCAL_MODULE)) $(NDK_WHOLE_ARCHIVE_SUFFIX))))
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..98afd864ed6
--- /dev/null
+++ b/cross/ndk-build/ndk-build-static-library.mk
@@ -0,0 +1,142 @@
+# 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)-static-$(subst /,_,$(2).o)
+maybe-absolute = $(or $(and $(2),$(1)),$(and $(wildcard 
$(1)),$(1)),$(LOCAL_PATH)/$(1))
+
+define single-object-target
+
+ifeq (x$(suffix $(1)),x.c)
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call 
maybe-absolute,$(1),$(2))
+       $(NDK_BUILD_CC) -c $$< -o $$@ $(NDK_BUILD_CFLAGS) 
$(NDK_CFLAGS_$(LOCAL_MODULE)) $(call LOCAL_C_ADDITIONAL_FLAGS,$(1))
+
+else
+ifeq (x$(suffix $(1)),x.$(or $(LOCAL_CPP_EXTENSION),cpp))
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call 
maybe-absolute,$(1),$(2))
+       $(NDK_BUILD_CXX) -c $$< -o $$@ $(NDK_BUILD_CFLAGS_CXX) 
$(NDK_CFLAGS_$(LOCAL_MODULE)) $(NDK_CXXFLAGS_$(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))): $(call 
maybe-absolute,$(1),$(2))
+       $(NDK_BUILD_CC) -c $$< -o $$@ $(NDK_ASFLAGS_$(LOCAL_MODULE))
+
+else
+ifneq (x$(suffix $(1)),x.asm)
+ifeq (x$(suffix $(1)),x.cc)
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call 
maybe-absolute,$(1),$(2))
+       $(NDK_BUILD_CXX) -c $$< -o $$@ $(NDK_BUILD_CFLAGS_CXX) 
$(NDK_CFLAGS_$(LOCAL_MODULE)) $(NDK_CXXFLAGS_$(LOCAL_MODULE))
+
+else
+$$(error Unsupported suffix: $(suffix $(1)))
+endif
+else
+ifneq (x$(LOCAL_ASM_RULE_DEFINED),x)
+# Call this function to define a rule that will generate $(1) from
+# $(2), a ``.asm'' file.  This is an Emacs extension.
+
+$(call LOCAL_ASM_RULE,$(call objname,$(LOCAL_MODULE),$(basename 
$(1))),$(LOCAL_PATH)/$(strip $(1)))
+
+else
+ifeq ($(findstring x86,$(NDK_BUILD_ARCH)),)
+$$(error Trying to build nasm file on non-Intel platform!)
+else
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call 
maybe-absolute,$(1),$(2))
+       $(NDK_BUILD_NASM) -felf$(findstring 64,$(NDK_BUILD_ARCH)) -o $$@ -i 
$(LOCAL_PATH) -i $$(dir $$<) $(NDK_ASFLAGS_$(LOCAL_MODULE)) $$<
+
+endif
+endif
+endif
+endif
+endif
+endif
+
+ALL_OBJECT_FILES$(LOCAL_MODULE) += $(call objname,$(LOCAL_MODULE),$(basename 
$(1)))
+endef
+
+define single-neon-target
+
+# Define rules for the target.
+$$(eval $$(call single-object-target,$(patsubst %.neon,%,$(1)),))
+
+endef
+
+NDK_CFLAGS_$(LOCAL_MODULE)  := $(addprefix -I,$(LOCAL_C_INCLUDES))
+NDK_CFLAGS_$(LOCAL_MODULE)  += -fPIC -iquote $(LOCAL_PATH) 
$(LOCAL_EXPORT_CFLAGS) $(LOCAL_CFLAGS) $(LOCAL_CFLAGS_$(NDK_BUILD_ARCH))
+NDK_ASFLAGS_$(LOCAL_MODULE) := $(LOCAL_ASFLAGS) 
$(LOCAL_ASFLAGS_$(NDK_BUILD_ARCH)) $(and $(findstring 
clang,$(NDK_BUILD_CC)),$(LOCAL_CLANG_ASFLAGS_$(NDK_BUILD_ARCH)))
+NDK_LDFLAGS_$(LOCAL_MODULE) := $(LOCAL_LDLIBS) $(LOCAL_LDFLAGS)
+NDK_CXXFLAGS_$(LOCAL_MODULE) := $(LOCAL_CPPFLAGS) $(LOCAL_RTTI_FLAG)
+ALL_OBJECT_FILES$(LOCAL_MODULE) :=
+
+# Now look for features in LOCAL_CPP_FEATURES and enable them.
+
+ifneq ($(findstring exceptions,$(LOCAL_CPPFLAGS)),)
+NDK_CXXFLAGS_$(LOCAL_MODULE) += -fexceptions
+endif
+
+ifneq ($(findstring rtti,$(LOCAL_CPPFLAGS)),)
+NDK_CXXFLAGS_$(LOCAL_MODULE) += -frtti
+endif
+
+
+ifeq ($(NDK_BUILD_ARCH)$(NDK_ARM_MODE),armarm)
+NDK_CFLAGS ::= -marm
+else
+ifeq ($(NDK_BUILD_ARCH),arm)
+NDK_CFLAGS ::= -mthumb
+endif
+endif
+
+ifeq ($(findstring lib,$(LOCAL_MODULE)),lib)
+LOCAL_MODULE_FILENAME := $(LOCAL_MODULE)
+else
+LOCAL_MODULE_FILENAME := lib$(LOCAL_MODULE)
+endif
+
+LOCAL_MODULE_FILENAME := $(LOCAL_MODULE_FILENAME).a
+
+# Record this module's dependencies and exported includes and CFLAGS,
+# and then add that of its dependencies.
+
+include $(srcdir)/ndk-resolve.mk
+
+# Then define rules to build all objects.
+ALL_SOURCE_FILES := $(LOCAL_SRC_FILES) $(LOCAL_SRC_FILES_$(NDK_BUILD_ARCH))
+
+# Now filter out code that is built with neon.  Define rules to build
+# those separately.
+NEON_SOURCE_FILES := $(filter %.neon,$(ALL_SOURCE_FILES))
+ALL_SOURCE_FILES  := $(filter-out %.neon,$(ALL_SOURCE_FILES))
+
+# This defines all dependencies.
+ALL_OBJECT_FILES$(LOCAL_MODULE) =
+
+$(foreach source,$(ALL_SOURCE_FILES),$(eval $(call 
single-object-target,$(source),)))
+$(foreach source,$(NEON_SOURCE_FILES),$(eval $(call 
single-neon-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.mk.in b/cross/ndk-build/ndk-build.mk.in
new file mode 100644
index 00000000000..57006901721
--- /dev/null
+++ b/cross/ndk-build/ndk-build.mk.in
@@ -0,0 +1,68 @@
+### @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 get and build
+# prerequisites.
+
+NDK_BUILD_MODULES = @NDK_BUILD_MODULES@
+NDK_BUILD_CXX_SHARED = @NDK_BUILD_CXX_SHARED@
+NDK_BUILD_ANY_CXX_MODULE = @NDK_BUILD_ANY_CXX_MODULE@
+NDK_BUILD_SHARED =
+NDK_BUILD_STATIC =
+
+define uniqify
+$(if $1,$(firstword $1) $(call uniqify,$(filter-out $(firstword $1),$1)))
+endef
+
+# Remove duplicate modules.  These can occur when a single module
+# imports a module and also declares it in LOCAL_SHARED_LIBRARIES.
+NDK_BUILD_MODULES := $(call uniqify,$(NDK_BUILD_MODULES))
+
+# Here are all of the files to build.
+NDK_BUILD_ALL_FILES := $(foreach file,$(NDK_BUILD_MODULES), \
+                        $(top_builddir)/cross/ndk-build/$(file))
+
+# The C++ standard library must be extracted from the Android NDK
+# directories and included in the application package, if any module
+# requires the C++ standard library.
+
+ifneq ($(NDK_BUILD_ANY_CXX_MODULE),)
+NDK_BUILD_SHARED += $(NDK_BUILD_CXX_SHARED)
+endif
+
+define subr-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))))
+
+# Generate rules to build everything now.
+# Make sure to use the top_builddir currently defined.
+
+NDK_TOP_BUILDDIR := $(top_builddir)
+$(NDK_BUILD_ALL_FILES) &:
+       $(MAKE) -C $(NDK_TOP_BUILDDIR)/cross/ndk-build $(NDK_BUILD_MODULES)
diff --git a/cross/ndk-build/ndk-clear-vars.mk 
b/cross/ndk-build/ndk-clear-vars.mk
new file mode 100644
index 00000000000..7309b7bb513
--- /dev/null
+++ b/cross/ndk-build/ndk-clear-vars.mk
@@ -0,0 +1,57 @@
+# 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.
+
+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_C_INCLUDE_DIRS :=
+LOCAL_EXPORT_LDFLAGS :=
+LOCAL_EXPORT_LDLIBS :=
+
+# AOSP extensions.
+LOCAL_SRC_FILES_$(NDK_BUILD_ARCH) :=
+LOCAL_ASFLAGS_$(NDK_BUILD_ARCH) :=
+LOCAL_CFLAGS_$(NDK_BUILD_ARCH) :=
+LOCAL_ADDITIONAL_DEPENDENCIES :=
+LOCAL_CLANG_ASFLAGS_$(NDK_BUILD_ARCH) :=
+LOCAL_IS_HOST_MODULE :=
+
+# Emacs extensions!
+LOCAL_ASM_RULE_DEFINED :=
+LOCAL_ASM_RULE :=
+LOCAL_C_ADDITIONAL_FLAGS :=
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/cross/ndk-build/ndk-resolve.mk b/cross/ndk-build/ndk-resolve.mk
new file mode 100644
index 00000000000..b29a2c6dc39
--- /dev/null
+++ b/cross/ndk-build/ndk-resolve.mk
@@ -0,0 +1,162 @@
+# 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.
+
+# List of system libraries to ignore.
+NDK_SYSTEM_LIBRARIES = z libz libc c libdl dl stdc++ libstdc++ log liblog 
android libandroid
+
+# Save information.
+NDK_LOCAL_PATH_$(LOCAL_MODULE) := $(LOCAL_PATH)
+NDK_LOCAL_STATIC_LIBRARIES_$(LOCAL_MODULE) := $(LOCAL_STATIC_LIBRARIES) 
$(LOCAL_WHOLE_STATIC_LIBRARIES)
+NDK_LOCAL_WHOLE_LIBRARIES_$(LOCAL_MODULE) := $(LOCAL_WHOLE_STATIC_LIBRARIES)
+NDK_LOCAL_SHARED_LIBRARIES_$(LOCAL_MODULE) := $(LOCAL_SHARED_LIBRARIES)
+NDK_LOCAL_EXPORT_CFLAGS_$(LOCAL_MODULE) := $(LOCAL_EXPORT_CFLAGS)
+NDK_LOCAL_EXPORT_C_INCLUDES_$(LOCAL_MODULE) := $(LOCAL_EXPORT_C_INCLUDES) 
$(LOCAL_EXPORT_C_INCLUDE_DIRS)
+NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) :=
+NDK_WHOLE_A_NAMES_$(LOCAL_MODULE) :=
+NDK_LOCAL_SO_NAMES_$(LOCAL_MODULE) :=
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) :=
+
+# List of all dependencies resolved for this module thus far.
+# Used to avoid infinite recursion.
+# Separate the variable which lists modules for which CFLAGS
+# have been resolved from the variable which lists modules
+# for which library dependencies have been resolved, in order
+# to catch the case where a library dependency is skipped
+# despite its CFLAGS being added.
+NDK_RESOLVED_$(LOCAL_MODULE) :=
+NDK_RESOLVED_CFLAGS_$(LOCAL_MODULE) :=
+
+define ndk-resolve
+
+ifeq ($$(filter $(1)$(and 
$(3),whole),$$(NDK_RESOLVED_CFLAGS_$(LOCAL_MODULE))),)
+# Always mark this module's cflags as having been resolved, even if
+# this is a whole library.
+NDK_RESOLVED_CFLAGS_$(LOCAL_MODULE) += $(1)
+
+NDK_CFLAGS_$(LOCAL_MODULE) += $(NDK_LOCAL_EXPORT_CFLAGS_$(1))
+NDK_CFLAGS_$(LOCAL_MODULE) += $(addprefix 
-I,$(NDK_LOCAL_EXPORT_C_INCLUDES_$(1)))
+endif
+
+ifeq ($$(filter $(1)$(and $(3),whole),$$(NDK_RESOLVED_$(LOCAL_MODULE))),)
+# Now append local libraries, as long as this library isn't a shared
+# library itself.
+ifeq ($(4),)
+
+# Mark this module's library dependencies as having been resolved.
+NDK_RESOLVED_$(LOCAL_MODULE) += $(1)
+
+# If this is a whole library, then mark this as resolved too, and
+# remove the library from the normal static library list.
+ifneq ($(3),)
+NDK_RESOLVED_$(LOCAL_MODULE) += $(1)whole
+endif
+
+# If the module happens to be zlib, then add -lz to the shared library
+# flags.
+ifeq ($(strip $(1)),libz)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -lz
+endif
+
+ifeq ($(strip $(1)),z)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -lz
+endif
+
+# Likewise for libdl.
+ifeq ($(strip $(1)),libdl)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -ldl
+endif
+
+ifeq ($(strip $(1)),dl)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -ldl
+endif
+
+# Likewise for libstdc++.
+ifeq ($(strip $(1)),libstdc++)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -lstdc++
+endif
+
+ifeq ($(strip $(1)),dl)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -lstdc++
+endif
+
+# Likewise for liblog.
+ifeq ($(strip $(1)),liblog)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -llog
+endif
+
+ifeq ($(strip $(1)),log)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -llog
+endif
+
+# Likewise for libandroid.
+ifeq ($(strip $(1)),libandroid)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -landroid
+endif
+
+ifeq ($(strip $(1)),android)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -landroid
+endif
+
+ifeq ($(findstring $(1),$(NDK_SYSTEM_LIBRARIES))$(2)$(3),)
+ifneq ($(findstring lib,$(1)),)
+NDK_LOCAL_SO_NAMES_$(LOCAL_MODULE) += $(1)_emacs.so
+else
+NDK_LOCAL_SO_NAMES_$(LOCAL_MODULE) += lib$(1)_emacs.so
+endif
+endif
+
+ifneq ($(2),)
+ifneq ($(findstring lib,$(1)),)
+NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) += $(1).a
+else
+NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) += lib$(1).a
+endif
+endif
+
+ifneq ($(3),)
+ifneq ($(findstring lib,$(1)),)
+NDK_WHOLE_A_NAMES_$(LOCAL_MODULE) += $(1).a
+else
+NDK_WHOLE_A_NAMES_$(LOCAL_MODULE) += lib$(1).a
+endif
+
+# Remove this archive from the regular archive list, should it already
+# exists.  Any given archive should only appear once, and if an
+# archive has been specified as whole it should always be whole.
+NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) := $$(filter-out 
lib$(1).a,$$(NDK_LOCAL_A_NAMES_$(LOCAL_MODULE)))
+NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) := $$(filter-out 
$(1).a,$$(NDK_LOCAL_A_NAMES_$(LOCAL_MODULE)))
+endif
+endif
+
+$$(foreach module,$$(NDK_LOCAL_STATIC_LIBRARIES_$(1)),$$(eval $$(call 
ndk-resolve,$$(module),1,,$(or $(4),$(if $(2)$(3),,1)))))
+$$(foreach module,$$(NDK_LOCAL_SHARED_LIBRARIES_$(1)),$$(eval $$(call 
ndk-resolve,$$(module),,,$(or $(4),$(if $(2)$(3),,1)))))
+$$(foreach module,$$(NDK_LOCAL_WHOLE_LIBRARIES_$(1)),$$(eval $$(call 
ndk-resolve,$$(module),,1,$(or $(4),$(if $(2)$(3),,1)))))
+endif
+
+endef
+
+# Add shared libraries to the shared object names when they appear as
+# a top level dependency.  However, do not recursively add the names
+# of this module's shared library dependencies, if it is just a shared
+# library, since it will link to those shared libraries itself.
+$(foreach module,$(LOCAL_SHARED_LIBRARIES),$(eval $(call 
ndk-resolve,$(module),,,)))
+$(foreach module,$(LOCAL_STATIC_LIBRARIES),$(eval $(call 
ndk-resolve,$(module),1,,)))
+$(foreach module,$(LOCAL_WHOLE_STATIC_LIBRARIES), $(eval $(call 
ndk-resolve,$(module),,1,)))
diff --git a/src/verbose.mk.in b/cross/verbose.mk.android
similarity index 71%
copy from src/verbose.mk.in
copy to cross/verbose.mk.android
index a4e2aad9325..d41d6b6aac0 100644
--- a/src/verbose.mk.in
+++ b/cross/verbose.mk.android
@@ -1,6 +1,7 @@
-### verbose.mk --- Makefile fragment for GNU Emacs
+### verbose.mk --- Makefile fragment for GNU Emacs during
+### cross-compilation.
 
-## Copyright (C) 2021-2023 Free Software Foundation, Inc.
+## Copyright (C) 2023 Free Software Foundation, Inc.
 
 ## This file is part of GNU Emacs.
 
@@ -18,7 +19,7 @@
 ## along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
 
 # 'make' verbosity.
-V = @AM_DEFAULT_VERBOSITY@
+V = 0
 ifeq (${V},1)
 AM_V_AR =
 AM_V_at =
@@ -26,12 +27,7 @@ AM_V_CC =
 AM_V_CXX =
 AM_V_CCLD =
 AM_V_CXXLD =
-AM_V_ELC =
-AM_V_ELN =
 AM_V_GEN =
-AM_V_GLOBALS =
-AM_V_NO_PD =
-AM_V_RC =
 else
 
 # Whether $(info ...) works.  This is to work around a bug in GNU Make
@@ -49,31 +45,11 @@ have_working_info = $(filter notintermediate,$(value 
.FEATURES))
 # since the bug is not annoying elsewhere.
 
 AM_V_AR      = @$(info $   AR       $@)
-AM_V_at = @
+AM_V_at             = @
 AM_V_CC      = @$(info $   CC       $@)
 AM_V_CXX     = @$(info $   CXX      $@)
 AM_V_CCLD    = @$(info $   CCLD     $@)
 AM_V_CXXLD   = @$(info $   CXXLD    $@)
-
-ifeq ($(HAVE_NATIVE_COMP)-$(NATIVE_DISABLED)-$(ANCIENT),yes--)
-ifneq (,$(have_working_info))
-AM_V_ELC     = @$(info $   ELC+ELN  $@)
-AM_V_ELN     = @$(info $   ELN      $@)
-else
-AM_V_ELC     = @echo "  ELC+ELN " $@;
-AM_V_ELN     = @echo "  ELN     " $@;
-endif
-else
-ifneq (,$(have_working_info))
-AM_V_ELC     = @$(info $   ELC      $@)
-else
-AM_V_ELC     = @echo "  ELC     " $@;
-endif
-AM_V_ELN =
-endif
-
 AM_V_GEN     = @$(info $   GEN      $@)
-AM_V_GLOBALS = @$(info $   GEN      globals.h)
 AM_V_NO_PD = --no-print-directory
-AM_V_RC      = @$(info $   RC       $@)
 endif
diff --git a/doc/emacs/Makefile.in b/doc/emacs/Makefile.in
index 161bdcb1c59..c7415312753 100644
--- a/doc/emacs/Makefile.in
+++ b/doc/emacs/Makefile.in
@@ -146,6 +146,8 @@ EMACSSOURCES= \
        ${srcdir}/glossary.texi \
        ${srcdir}/ack.texi \
        ${srcdir}/kmacro.texi \
+       ${srcdir}/android.texi \
+       ${srcdir}/input.texi \
        $(EMACS_XTRA)
 
 ## Disable implicit rules.
diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi
new file mode 100644
index 00000000000..9d352f3a484
--- /dev/null
+++ b/doc/emacs/android.texi
@@ -0,0 +1,791 @@
+@c This is part of the Emacs manual.
+@c Copyright (C) 2023 Free Software Foundation, Inc.
+@c See file emacs.texi for copying conditions.
+@node Android
+@appendix Emacs and Android
+@cindex Android
+
+  Android is a mobile operating system developed by the Open Handset
+Alliance.  This section describes the peculiarities of using Emacs on
+an Android device running Android 2.2 or later.
+
+  Android devices commonly rely on user input through a touch screen
+or digitizer device and on-screen keyboard.  For more information
+about using such devices with Emacs, @pxref{Other Input Devices}.
+
+@menu
+* What is Android?::            Preamble.
+* Android Startup::             Starting up Emacs on Android.
+* Android File System::         The Android file system.
+* Android Document Providers::  Accessing files from other programs.
+* Android Environment::         Running Emacs under Android.
+* Android Windowing::           The Android window system.
+* Android Fonts::               Font selection under Android.
+* Android Troubleshooting::     Dealing with problems.
+* Android Software::            Getting extra software.
+@end menu
+
+@node What is Android?
+@section Android History
+
+  Android is an operating system for mobile devices developed by the
+Open Handset Alliance, a group of companies interested in developing
+handsets that can run a common set of software.  It is supposedly free
+software.
+
+  Like the X Consortium of times past, the Open Handset Alliance
+believes that ``openness'' (namely, the regular release of the Android
+source code) is simply a tool to increase the popularity of the
+Android platform.  Computer companies normally produce proprietary
+software.  The companies in the Open Handset Alliance are no different
+-- most versions of Android installed on devices are proprietary, by
+virtue of containing proprietary components, that often cannot even be
+replaced by the user.
+
+  Android is not designed to respect users' freedom.  Almost all
+versions of Android (including some which are supposedly free
+software) include support for Digital Restrictions Management,
+technology that is designed to limit users' ability to copy media to
+and from their own devices.  Most Android devices also come with
+proprietary Google applications which are required to run the system,
+and many other Android applications.
+
+  Thus, it must be necessary to consider Android proprietary software
+from a practical standpoint.  That is an injustice.  If you use
+Android, we urge you to switch to a free operating system, if only for
+your freedom's sake.
+
+  We support GNU Emacs on proprietary operating systems because we
+hope this taste of freedom will inspire users to escape from them.
+
+@node Android Startup
+@section Starting Emacs on Android
+
+  Emacs is not installed on Android devices from source code or
+through a package manager.  Instead, Emacs is compiled for Android on
+a different operating system, with the resulting binaries packaged
+into an archive, that is then transferred to the system and installed.
+
+  After being installed, the system places an application icon on the
+desktop (a.k.a@: ``home screen''.)  Emacs then starts up once the
+application icon is clicked.
+
+@cindex ``adb logcat''
+
+  During startup, Emacs will display messages in the system log
+buffer; reading that buffer during start-up requires the Android Debug
+Bridge (@command{adb}) utility to be installed on another computer.
+
+  After enabling the ``USB Debugging'' feature on the Android system,
+and connecting it via USB to another system with the @command{adb}
+utility installed, the log can be viewed by running the following
+command on that other system:
+
+@example
+$ adb logcat | grep -E "(android_run_debug_thread|[Ee]macs)"
+@end example
+
+  Assuming that the @command{adb} utility is installed on a GNU/Linux
+or Unix system, follow the steps below to connect to your device.
+
+@enumerate
+@item
+Enable ``developer options'' on your device, by going to the ``About''
+page in the system settings application and clicking on the ``build
+version'' or ``kernel version'' items five to seven times.
+
+@item
+Open the ``developer options'' settings page, which should be under
+the ``system'' page in the settings application.
+
+@item
+Turn on the switch ``USB debugging''.
+
+@item
+Connect one end of a USB cable to your device, and the other end to
+your computer's USB port.
+
+@item
+Run the command @command{adb shell} on your computer.  This will fail
+or hang because you have not yet granted your computer permission to
+access the connected device.
+
+@item
+Confirm the pop-up displayed on your device asking whether or not it
+should allow access from your computer.
+@end enumerate
+
+  Depending on the versions of Android and @command{adb} installed,
+there may be other ways to establish a connection.  See the official
+documentation at
+@url{https://developer.android.com/studio/command-line/adb} for more
+details.
+
+  Once Emacs starts up, simply running the command @command{logcat} as
+an asynchronous shell command (@pxref{Shell}) will display the log
+buffer.
+
+@cindex emacsclient wrapper, android
+  Since there is no other way to start the @command{emacsclient}
+program (@pxref{Emacs Server}) from another Android program, Emacs
+provides a wrapper around the @command{emacsclient} program, which is
+registered with the system as an application that can open all text
+files.
+
+  When that wrapper is selected as the program with which to open a
+file, it invokes @command{emacsclient} with the options
+@command{--reuse-frame}, @command{--timeout=10}, @command{--no-wait},
+and the name of the file being opened.  Then, upon success, the focus
+is transferred to any open Emacs frame.
+
+  However, if Emacs is not running at the time the wrapper is opened,
+it starts Emacs and gives it the file to open as an argument.  Note
+that if that Emacs in turn does not start the Emacs server, subsequent
+attempts to open the file with the wrapper will fail.
+
+@cindex /content/by-authority directory, android
+  Some files are given to Emacs as ``content identifiers'' that the
+system provides access to outside the normal filesystem APIs.  Emacs
+uses a pseudo-directory named @file{/content/by-authority} to access
+those files.  Do not make any assumptions about the contents of this
+directory, or try to open files in it yourself.
+
+  This feature is not provided on Android 4.3 and earlier, in which
+case such files are copied to a temporary directory before being
+opened.
+
+@node Android File System
+@section What Files Emacs Can Access on Android
+@cindex /assets directory, android
+
+  Emacs exposes a special directory on Android systems: the name of
+the directory is @file{/assets}, and it contains the @file{etc},
+@file{lisp} and @file{info} directories which are normally installed
+in @file{/usr/share/emacs} directory on GNU and Unix systems.  On
+Android systems, the Lisp emulation of @command{ls} (@pxref{ls in
+Lisp}) is also enabled by default, as the @command{ls} binary which
+comes with the system varies by manufacturer and usually does not
+support all of the features required by Emacs.  One copy of
+@command{ls} distributed with some Android systems is even known to
+lack support for the @code{-l} flag.
+
+@cindex limitations of the /assets directory
+
+  This directory exists because Android does not extract the contents
+of application packages on to the file system while unpacking them,
+but instead requires programs like Emacs to access its contents using
+a special ``asset manager'' interface.  Here are the peculiarities
+that result from such an implementation:
+
+@itemize @bullet
+@item
+Subprocesses (such as @command{ls}) can not run from the
+@file{/assets} directory; if you try to run a subprocess with
+@code{current-directory} set to @file{/assets},
+@file{/content/storage} or a subdirectory thereof, it will run from
+the home directory instead.
+
+@item
+There are no @file{.} and @file{..} directories inside the
+@file{/assets} or @file{/content} directory.
+
+@item
+Files in the @file{/assets} directory are always read only, and may be
+read in to memory more than once each time they are opened.
+@end itemize
+
+  Aside from the @file{/assets} directory, Android programs normally
+have access to four other directories.  They are:
+
+@itemize @bullet
+@item
+The @dfn{app data} directory.  This also serves as the home directory
+for Emacs, and is always accessible read-write.
+
+@item
+The @dfn{app library} directory.  This is automatically appended to
+@code{exec-path} and made @code{exec-directory} upon startup, and
+contains utility executables alongside Emacs itself.
+
+@item
+The @dfn{external storage} directory.  This is accessible to Emacs
+when the user grants the ``Files and Media'' permission to Emacs via
+system settings.
+
+@item
+Directories provided by @dfn{document providers} on Android 5.0 and
+later.  These directories exist outside the normal Unix filesystem,
+containing files provided by external programs (@pxref{Android
+Document Providers}.)
+@end itemize
+
+  Despite ordinary installations of Android not having files within
+the (normally read-only) root directory named @file{content} or
+@file{assets}, you may want to access real files by these names if the
+Android installation in use has been customized.  These files will
+conflict with the aformentioned special directories, but can
+nevertheless be accessed by writing their names relative to the
+``parent'' directory of the root directory, as so illustrated:
+@file{/../content}, @file{/../assets}.
+
+  The external storage directory is found at @file{/sdcard}.  The
+other directories are not found at any fixed location (but see below),
+although the app data directory is typically symlinked to
+@file{/data/data/org.gnu.emacs/files}.
+
+@cindex app library directory, android
+@cindex where is emacsclient under android
+  Older versions of Android used to place the app library directory
+under the name @file{lib} in the parent of the app data directory.
+Today, this directory is often placed in a directory with a randomly
+generated name under @file{/data/app}.
+
+  For the convenience of scripts running within applications sharing
+the same user ID as Emacs (which have no access to the
+@code{exec-directory} variable), a fairly considerable effort is made
+at startup to symlink the application library directory to its
+traditional location within the parent of the app data directory.
+
+  If Emacs is reinstalled and the location of the app library
+directory consequentially changes, that symlink will also be updated
+to point to its new location the next time Emacs is started by the
+system.
+
+@cindex temp~unlinked.NNNN files, Android
+  On Android devices running very old (2.6.29) versions of the Linux
+kernel, Emacs needs to create files named starting with
+@file{temp~unlinked} in the the temporary file directory in order to
+read from asset files.  Do not create files with such names yourself,
+or they may be overwritten or removed.
+
+@cindex file system limitations, Android 11
+  On Android 11 and later, the Android system restricts applications
+from accessing files in the @file{/sdcard} directory using
+file-related system calls such as @code{open} and @code{readdir}.
+
+  This restriction is known as ``Scoped Storage'', and supposedly
+makes the system more secure.  Unfortunately, it also means that Emacs
+cannot access files in those directories, despite holding the
+necessary permissions.  Thankfully, the Open Handset Alliance's
+version of Android allows this restriction to be disabled on a
+per-program basis; the corresponding option in the system settings
+panel is:
+
+@example
+System -> Apps -> Special App Access -> All files access -> Emacs
+@end example
+
+  After you disable or enable this setting as appropriate and grant
+Emacs the ``Files and Media'' permission, it will be able to access
+files under @file{/sdcard} as usual.  These settings are not present
+on some proprietary versions of Android.
+
+@node Android Document Providers
+@section Accessing Files from Other Programs on Android
+@cindex document providers, Android
+@cindex /content/storage directory, Android
+
+  Android 5.0 introduces a new sort of program, the ``document
+provider'': these programs are small programs that provide access to
+their own files outside both the asset manager and the Unix
+filesystem.  Emacs supports accessing files and directories they
+provide, placing their files within the directory
+@file{/content/storage}.
+
+@findex android-request-directory-access
+  Before Emacs is granted access to any of these directories, it must
+first request the right to access it.  This is done by running the
+command (@pxref{M-x}) @code{android-request-directory-access}, which
+displays a file selection dialog.
+
+  If a directory is selected within this dialog, its contents are
+subsequently made available within a new directory named
+@file{/content/storage/@var{authority}/@var{id}}, where
+@var{authority} is the name of the document provider, and @var{id} is
+a unique identifier assigned to the directory by the document
+provider.
+
+  The same limitations applied to the @file{/assets} directory
+(@pxref{Android File System}) are applied when creating sub-processes
+within those directories, because they do not exist within the Unix
+file-system.  In addition, although Emacs can normally write and
+create files inside these directories, it cannot create symlinks or
+hard links.
+
+  Since document providers are allowed to perform expensive network
+operations to obtain file contents, a file access operation within one
+of these directories has the potential to take a significant amount of
+time.
+
+@node Android Environment
+@section Running Emacs under Android
+
+  From the perspective of users, Android is mostly a single user
+operating system; however, from the perspective of applications and
+Emacs, the system has an overwhelming number of users.
+
+  Each application runs in its own user, with its home directory set
+to its app data directory (@pxref{Android File
+System}.)@footnote{Except in cases where a ``shared user ID'' is
+specified and other applications signed using the same ``package
+signing key'' are installed, in which case Emacs runs as the same user
+and has access to the same files as each of the aformentioned
+applications.}
+
+  Each application is also prohibited from accessing many system
+directories and the app data directories of other applications.
+
+  Emacs comes with several binaries.  While being executable files,
+they are packaged as libraries in the library directory, because
+otherwise the system will not unpack them while Emacs is being
+installed.  This means, instead of specifying @code{ctags} or
+@code{emacsclient} in a subprocess, Lisp code must specify
+@code{libctags.so} or @code{libemacsclient.so} on the command line
+instead when starting either of those programs in a subprocess; to
+determine which names to use, consult the values of the variables
+@code{ctags-program-name}, @code{etags-program-name},
+@code{hexl-program-name}, @code{emacsclient-program-name},
+@code{movemail-program-name}, and @code{ebrowse-program-name}.
+@xref{Subprocess Creation,,, elisp, the Emacs Lisp Reference Manual}.
+
+  The @file{/assets} directory containing Emacs start-up files is
+supposed to be inaccessible to processes not directly created by
+@code{zygote}, the system service responsible for starting
+applications.  Since required Lisp is found in the @file{/assets}
+directory, it would thus follow that it is not possible for Emacs to
+start itself as a subprocess.  A special binary named
+@command{libandroid-emacs.so} is provided with Emacs, and does its
+best to start Emacs for the purpose of running Lisp in batch mode.
+However, the approach it takes was devised by reading Android source
+code, and is not sanctioned by the Android compatibility definition
+documents, so your mileage may vary.
+
+@cindex call-process, Android
+@vindex android-use-exec-loader
+  Android 10 and later also prohibit Emacs itself from running
+executables inside the app data directory, obstensibly for security
+reasons.  On these systems, Emacs normally applies a workaround;
+however, this workaround requires running all sub-processes through
+another subprocess which implements an executable loader and applies
+process tracing to all its children, which may prove to be problematic
+for various different reasons.  In that case, the workaround can be
+disabled by changing the variable @code{android-use-exec-loader} to
+@code{nil}.
+
+  When this workaround is in effect, process IDs retrieved through the
+@code{process-id} function will be that of the executable loader
+process; its child will belong to the same process group as the
+loader.  As a result, @code{interrupt-process}, and other related
+functions will work correctly, but using the process ID returned by
+@code{process-id} for other purposes will not.
+
+  One side effect of the mechanism by which process tracing is carried
+out is that job control facilities inside inferior shells
+(@pxref{Interactive Shell}) will not be able to stop processes, and
+sending the @code{SIGSTOP} signal to a subprocess created by Emacs
+will appear to have no effect.
+
+  In addition, Android 12 also terminates subprocesses which are
+consuming CPU while Emacs itself is in the background.  The system
+determines which processes are consuming too much CPU in intervals of
+five minutes, and terminates the process that has consumed the most
+CPU time.
+
+  Android 12.1 and Android 13 provide an option to disable this
+behavior; to use it, enable ``USB debugging'' (@pxref{Android
+Startup}) connect the Android system to another computer, and run:
+
+@example
+$ adb shell "settings put global settings_enable_monitor_phantom_procs false"
+@end example
+
+@section Running Emacs in the Background
+@cindex emacs killed, android
+@cindex emacs in the background, android
+
+  Application processes are treated as disposable entities by the
+system.  When all Emacs frames move to the background, Emacs might be
+terminated by the system at any time, for the purpose of saving system
+resources.
+
+  On Android 7.1 and earlier, Emacs tells the system to treat it as a
+``background service''.  The system will try to avoid killing Emacs
+unless the system is stressed for memory.
+
+  Android 8.0 removed the ability for background services to receive
+such special treatment.  However, Emacs applies a workaround: the
+system considers applications that create a permanent notification to
+be performing active work, and will avoid killing such applications.
+Thus, on those systems, Emacs displays a permanent notification for as
+long as it is running.  Once the notification is displayed, it can be
+safely hidden through the system settings without resulting in Emacs
+being killed.
+
+  However, it is not guaranteed that the system will not kill Emacs
+even if a notification is being displayed.  While the Open Handset
+Alliance's sample implementation of Android behaves correctly, many
+manufacturers place additional restrictions on program execution in
+the background in their proprietary versions of Android.  There is a
+list of such troublesome manufacturers and sometimes workarounds at
+@url{https://dontkillmyapp.com/}.
+
+@section Android Permissions
+@cindex external storage, android
+
+  Android also defines a permissions system that determines what
+system services Emacs is allowed to access.  Programs must specify
+what permissions they want; what then happens depends on the version
+of Android being used:
+
+@itemize @bullet
+@item
+On Android 5.1 and earlier, Emacs automatically receives the following
+permissions it has requested upon being installed:
+
+@itemize @minus
+@item
+@code{android.permission.READ_CONTACTS}
+@item
+@code{android.permission.WRITE_CONTACTS}
+@item
+@code{android.permission.VIBRATE}
+@item
+@code{android.permission.ACCESS_COARSE_LOCATION}
+@item
+@code{android.permission.ACCESS_NETWORK_STATE}
+@item
+@code{android.permission.INTERNET}
+@item
+@code{android.permission.SET_WALLPAPER}
+@item
+@code{android.permission.READ_EXTERNAL_STORAGE}
+@item
+@code{android.permission.WRITE_EXTERNAL_STORAGE}
+@item
+@code{android.permission.SEND_SMS}
+@item
+@code{android.permission.RECEIVE_SMS}
+@item
+@code{android.permission.RECEIVE_MMS}
+@item
+@code{android.permission.WRITE_SMS}
+@item
+@code{android.permission.READ_SMS}
+@item
+@code{android.permission.NFC}
+@item
+@code{android.permission.TRANSMIT_IR}
+@item
+@code{android.permission.READ_PHONE_STATE}
+@item
+@code{android.permission.WAKE_LOCK}
+@item
+@code{android.permission.FOREGROUND_SEVICE}
+@item
+@code{android.permission.REQUEST_INSTALL_PACKAGES}
+@item
+@code{android.permission.REQUEST_DELETE_PACKAGES}
+@item
+@code{android.permission.SYSTEM_ALERT_WINDOW}
+@item
+@code{android.permission.RECORD_AUDIO}
+@item
+@code{android.permission.CAMERA}
+@end itemize
+
+While most of these permissions are left unused by Emacs itself, they
+are declared by Emacs as they could be useful for other programs; for
+example, the permission to access contacts may be useful for EUDC.
+
+@item
+On Android 6.0 and later, Emacs only receives the following
+permissions upon installation:
+
+@itemize @minus
+@item
+@code{android.permission.VIBRATE}
+@item
+@code{android.permission.ACCESS_NETWORK_STATE}
+@item
+@code{android.permission.INTERNET}
+@item
+@code{android.permission.SET_WALLPAPER}
+@item
+@code{android.permission.NFC}
+@item
+@code{android.permission.TRANSMIT_IR}
+@item
+@code{android.permission.WAKE_LOCK}
+@item
+@code{android.permission.POST_NOTIFICATIONS}
+@end itemize
+
+Other permissions must be granted by the user through the system
+settings application.  Consult the manufacturer of your device for
+more details, as how to do this varies by device.
+@end itemize
+
+@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
+time.  On larger devices, the system allows up to four windows to be
+tiled on the screen at any time.
+
+  Windows on Android do not continue to exist indefinitely after they
+are created.  Instead, the system may choose to close windows that are
+not on screen in order to save memory, with the assumption that the
+program will save its contents to disk and restore them later, when
+the user asks for it to be opened again.  As this is obviously not
+possible with Emacs, Emacs separates the resources associated with a
+frame from its system window.
+
+  Each system window created (including the initial window created
+during Emacs startup) is appended to a list of windows that do not
+have associated frames.  When a frame is created, Emacs looks up any
+window within that list, and displays the contents of the frame
+within; if there is no window at all, then one is created.  Likewise,
+when a new window is created by the system, Emacs places the contents
+of any frame that is not already displayed within a window inside.
+When a frame is closed, the corresponding system window is also
+closed.  Upon startup, the system creates a window itself (within
+which Emacs displays the first window system frame shortly
+thereafter.)  Emacs differentiates between that window and windows
+created on behalf of other frames to determine what to do when the
+system window associated with a frame is closed:
+
+@itemize @bullet
+@item
+When the system closes the window created during application startup
+in order to save memory, Emacs retains the frame for when that window
+is created later.
+
+@item
+When the user closes the window created during application startup,
+and the window was not previously closed by the system in order to
+save resources, Emacs deletes any frame displayed within that window.
+
+@item
+When the user or the system closes any window created by Emacs on
+behalf of a specific frame, Emacs deletes the frame displayed within
+that window.
+@end itemize
+
+@cindex windowing limitations, android
+@cindex frame parameters, android
+Emacs only supports a limited subset of GUI features on Android; the
+limitations are as follows:
+
+@itemize @bullet
+@item
+Scroll bars are not supported, as they are close to useless on Android
+devices.
+
+@item
+The @code{alpha}, @code{alpha-background}, @code{z-group},
+@code{override-redirect}, @code{mouse-color}, @code{title},
+@code{wait-for-wm}, @code{sticky}, @code{undecorated} and
+@code{tool-bar-position} frame parameters (@pxref{Frame Parameters,,,
+elisp, the Emacs Lisp Reference Manual}) are unsupported.
+
+@item
+On Android 4.0 and earlier, the @code{fullscreen} frame parameter is
+always @code{maximized} for top-level frames; on later versions of
+Android, it can also be @code{fullscreen}.
+@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 Emacs is only able to set selections to plain text.
+
+  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
+Between Android 3.0 and Android 9.0, Emacs is able to access the
+clipboard whenever it wants, and @code{gui-selection-owner-p} always
+returns accurate results.
+
+@item
+Under Android 10.0 and later, Emacs can only access clipboard data
+when one of its frames has the input focus, and
+@code{gui-selection-owner-p} always returns @code{nil} for the
+clipboard selection.
+@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.
+
+@vindex android-pass-multimedia-buttons-to-system
+@cindex volume/multimedia buttons, Android
+  The volume keys are normally reserved by Emacs and used to provide
+the ability to quit Emacs without a physical keyboard
+(@pxref{On-Screen Keyboards}.)  However, if you want them to adjust
+the volume instead, you can set the variable
+@code{android-pass-multimedia-buttons-to-system} to a non-@code{nil}
+value; note that you will no longer be able to quit Emacs using the
+volume buttons in that case.
+
+@cindex dialog boxes, android
+  Emacs is unable to display dialog boxes (@pxref{Dialog Boxes}) while
+it does not have the input focus on Android 6.0 or later.  If this is
+important to you, this ability can be restored by granting Emacs
+permission to display over other programs.  Normally, this can be done
+from the:
+
+@example
+System -> Apps -> Emacs -> More -> Display over other apps
+@end example
+
+menu in the system settings, but this procedure may vary by device.
+
+@cindex keyboard modifiers, android
+  There is a direct relation between physical modifier keys and Emacs
+modifiers (@pxref{Modifier Keys}) reported within key events, subject
+to a single exception: if @key{Alt} on your keyboard is depressed,
+then the @key{Meta} modifier will be reported by Emacs in its place,
+and vice versa.  This irregularity is since most keyboards posses no
+special @key{Meta} key, and the @key{Alt} modifier is seldom employed
+in Emacs.
+
+  Bear in mind that Android uses a different name for the @key{Super}
+modifier: it is referred to as @key{SYM} on Android keyboards and
+within the Settings keymap menu.
+
+@node Android Fonts
+@section Font Backends and Selection under Android
+@cindex fonts, android
+
+  Emacs supports two font backends under Android: they are respectively
+named @code{sfnt-android} and @code{android}.
+
+  Upon startup, Emacs enumerates all the TrueType format fonts in the
+directories @file{/system/fonts} and @file{/product/fonts}, and the
+@file{fonts} directory (@dfn{user fonts directory}) inside the Emacs
+home directory.  Emacs assumes there will always be a font named
+``Droid Sans Mono'', and then defaults to using this font.  These
+fonts are then displayed by the @code{sfnt-android} font driver.
+
+  When running on Android, Emacs currently lacks support for OpenType
+fonts.  This means that only a subset of the fonts installed on the
+system are currently available to Emacs.  If you are interested in
+lifting this limitation, please contact @email{emacs-devel@@gnu.org}.
+
+  If the @code{sfnt-android} font driver fails to find any fonts at
+all, Emacs falls back to the @code{android} font driver.  This is a
+very lousy font driver, because of limitations and inaccuracies in the
+font metrics provided by the Android platform.  In that case, Emacs
+uses the ``Monospace'' typeface configured on your system; this should
+always be Droid Sans Mono.
+
+@cindex TrueType GX fonts, android
+@cindex distortable fonts, android
+
+  Like on X systems, Emacs supports distortable fonts under Android.
+These fonts (also termed ``TrueType GX fonts'', ``variable fonts'',
+and ``multiple master fonts'') provide multiple different styles
+(``Bold'', ``Italic'', etc) using a single font file.
+
+  When a user-installed distortable font is found, each font that a
+previously discovered font provided will no longer be used.  In
+addition, any previously specified distortable fonts with the same
+family name are also removed.  When a conventional font is found, any
+previous conventional font with the same style and family will be
+removed; distortable fonts with the same family will no longer be used
+to provide that style.
+
+@node Android Troubleshooting
+@section Troubleshooting Startup Problems on Android
+@cindex troubleshooting, android
+
+@cindex emacs -Q, android
+@cindex emacs --debug-init, android
+  Since Android has no command line, there is normally no way to
+specify command-line arguments when starting Emacs.  This is very
+nasty when you make a mistake in your Emacs initialization files that
+prevents Emacs from starting up at all, as the system normally
+prevents other programs from accessing Emacs's home directory.
+@xref{Initial Options}.
+
+  However, Emacs can be started with the equivalent of either the
+option @code{--quick}, or @code{--debug-init} through a special
+preferences screen.  Under Android 7.0 and later, this can be accessed
+through the Emacs ``app info'' page in the system settings program; on
+older systems, this is displayed as a separate icon on the desktop
+labeled ``Emacs options''.
+
+  Consult the manufacturer of your device for more details, as how to
+do this varies by device.
+
+@cindex dumping, android
+  The first time any given copy of Emacs starts on a device, it spends
+a while loading the preloaded Lisp files which normally come with
+Emacs.  This produces a ``dump file'' (@pxref{Initial Options}) in the
+files directory, containing an identifier unique to that copy of
+Emacs.
+
+  The next time that same copy of Emacs starts up, it simply loads the
+data contained in that dump file, greatly reducing start up time.
+
+  If by some unforeseen circumstance the dump file is corrupted, Emacs
+can crash.  If that happens, the dump file stored in the Emacs files
+directory can be erased through the preferences screen described
+above.
+
+@cindex accessing Emacs directories, Android
+  Emacs supports an alternative method of rescuing broken Emacs
+installations on Android 4.4 and later: Emacs exports a ``documents
+provider'' which accesses the contents of Emacs's home directory, that
+can then be accessed by any file manager program.
+
+  If you can find out how to open that documents provider in the file
+manager that comes with your device, you can rename, delete, or edit
+your initialization or dump files from there instead.
+
+@node Android Software
+@section Installing Extra Software on Android
+@cindex installing extra software on Android
+@cindex installing Unix software on Android
+
+  Android includes an extremely limited set of Unix-like command line
+tools in a default installation.  Several projects exist to argument
+this selection, providing options that range from improved
+reproductions of Unix command-line utilities to package repositories
+containing extensive collections of free GNU and Unix software.
+
+  @uref{http://busybox.net, Busybox} provides Unix utilities and
+limited replicas of certain popular GNU programs such as
+@command{wget} in a single statically-linked Linux binary, which is
+capable of running under Android.
+
+  @uref{https://termux.dev, Termux} provides a package manager based
+on the Debian project's @command{dpkg} system and a set of package
+repositories containing substantial amounts of free software for Unix
+systems, including compilers, debuggers, and runtimes for languages
+such as C, C++, Java, Python and Common Lisp.  These packages are
+normally installed from within a purpose-built terminal emulator
+application, but Emacs can access them if it is built with the same
+application signing key as the Termux terminal emulator, and with its
+``shared user ID'' set to the package name of the terminal emulator
+program.  The @file{java/INSTALL} within the Emacs distribution
+explains how to build Emacs in this fashion.
+
+  @uref{https://github.com/termux/termux-packages, termux-packages}
+provides the package definitions that are used by Termux to generate
+their package repositories, which may also be independently compiled
+for installation within Emacs's home directory.
+
+  In addition to the projects mentioned above, statically linked
+binaries for most Linux kernel-based systems can also be run on
+Android.
diff --git a/doc/emacs/dired.texi b/doc/emacs/dired.texi
index 77c4e09c826..244dd7eb525 100644
--- a/doc/emacs/dired.texi
+++ b/doc/emacs/dired.texi
@@ -684,6 +684,19 @@ cause trouble.  For example, after renaming one or more 
files,
 @code{dired-undo} restores the original names in the Dired buffer,
 which gets the Dired buffer out of sync with the actual contents of
 the directory.
+
+@item touchscreen-hold
+@kindex touchscreen-hold @r{(Dired)}
+@findex dired-click-to-select-mode
+@findex dired-enable-click-to-select-mode
+Enter a ``click to select'' mode, where using the mouse button
+@kbd{mouse-2} on a file name will cause its mark to be toggled.  This
+mode is useful when performing file management using a touch screen
+device.
+
+It is enabled when a ``hold'' gesture (@pxref{Touchscreens}) is
+detected over a file name, and is automatically disabled once a Dired
+command operates on the marked files.
 @end table
 
 @node Operating on Files
diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi
index 0efd99261ac..7a21eb49e24 100644
--- a/doc/emacs/emacs.texi
+++ b/doc/emacs/emacs.texi
@@ -223,7 +223,9 @@ Appendices
 * Antinews::            Information about Emacs version 28.
 * Mac OS / GNUstep::     Using Emacs under macOS and GNUstep.
 * Haiku::               Using Emacs on Haiku.
+* Android::             Using Emacs on Android.
 * Microsoft Windows::   Using Emacs on Microsoft Windows and MS-DOS.
+* Other Input Devices:: Using Emacs with other input devices.
 * Manifesto::           What's GNU?  Gnu's Not Unix!
 
 * Glossary::            Terms used in this manual.
@@ -1260,6 +1262,23 @@ Emacs and Haiku
 * Haiku Basics::        Basic Emacs usage and installation under Haiku.
 * Haiku Fonts::         The various options for displaying fonts on Haiku.
 
+Emacs and Android
+
+* What is Android?::            Preamble.
+* Android Startup::             Starting up Emacs on Android.
+* Android File System::         The Android file system.
+* Android Document Providers::  Accessing files from other programs.
+* Android Environment::         Running Emacs under Android.
+* Android Windowing::           The Android window system.
+* Android Fonts::               Font selection under Android.
+* Android Troubleshooting::     Dealing with problems.
+* Android Software::            Getting extra software.
+
+Emacs and Unconventional Input Devices
+
+* Touchscreens::        Using Emacs on touchscreens.
+* On-Screen Keyboards:: Using Emacs with virtual keyboards.
+
 Emacs and Microsoft Windows/MS-DOS
 
 * Windows Startup::     How to start Emacs on Windows.
@@ -1630,8 +1649,10 @@ Lisp programming.
 @include anti.texi
 @include macos.texi
 @include haiku.texi
+@include android.texi
 @c Includes msdos-xtra.
 @include msdos.texi
+@include input.texi
 @include gnu.texi
 @include glossary.texi
 @ifnottex
diff --git a/doc/emacs/frames.texi b/doc/emacs/frames.texi
index ce631561be7..a968c2a97c5 100644
--- a/doc/emacs/frames.texi
+++ b/doc/emacs/frames.texi
@@ -1333,6 +1333,21 @@ Parameters,,, elisp, The Emacs Lisp Reference Manual}.  
On macOS the
 tool bar is hidden when the frame is put into fullscreen, but can be
 displayed by moving the mouse pointer to the top of the screen.
 
+@vindex modifier-bar-mode
+@findex modifier-bar-mode
+@cindex displaying modifier keys in the tool bar
+@cindex mode, Modifier Bar
+@cindex Modifier Bar
+  Keyboards often lack one or more of the modifier keys that Emacs
+might want to use, making it difficult or impossible to input key
+sequences that contain them.  Emacs can optionally display a list of
+buttons that act as substitutes for modifier keys within the tool bar;
+these buttons are also referred to as the ``modifier bar''.  Clicking
+an icon within the modifier bar will cause a modifier key to be
+applied to the next keyboard event that is read.  The modifier bar is
+displayed when the global minor mode @code{modifier-bar-mode} is
+enabled; to do so, type @kbd{M-x modifier-bar-mode}.
+
 @node Tab Bars
 @section Tab Bars
 @cindex tab bar mode
@@ -1571,6 +1586,15 @@ the items that operate on the clicked tab.  Dragging the 
tab with
 wheel scrolling switches to the next or previous tab.  Holding down
 the @key{SHIFT} key during scrolling moves the tab to the left or right.
 
+  Touch screen input (@pxref{Other Input Devices}) can also be used to
+operate on tabs.  Long-pressing (@pxref{Touchscreens}) a tab will
+display a context menu with items that operate on the tab that was
+pressed, and long-pressing the tab bar itself will display a context
+menu which lets you create and remove tabs; tapping a tab itself will
+result in that tab's window configuration being selected, and tapping
+a button on the tab bar will behave as if it was clicked with
+@kbd{mouse-1}.
+
 @findex tab-bar-history-mode
   You can enable @code{tab-bar-history-mode} to remember window
 configurations used in every tab, and later restore them.
diff --git a/doc/emacs/input.texi b/doc/emacs/input.texi
new file mode 100644
index 00000000000..671901fea88
--- /dev/null
+++ b/doc/emacs/input.texi
@@ -0,0 +1,179 @@
+@c This is part of the Emacs manual.
+@c Copyright (C) 2023 Free Software Foundation, Inc.
+@c See file emacs.texi for copying conditions.
+@node Other Input Devices
+@appendix Emacs and Unconventional Input Devices
+@cindex other input devices
+
+  Emacs was originally developed with the assumption that its users
+have access to a desktop computer or computer terminal, with a
+keyboard and perhaps a suitable pointing device such as a mouse.
+
+  However, recent developments in the X Window System and operating
+systems such as Android mean that this assumption no longer holds
+true.  Emacs supports input from various other kinds of input devices,
+which is detailed here.
+
+@menu
+* Touchscreens::                Using Emacs on touchscreens.
+* On-Screen Keyboards::         Using Emacs with virtual keyboards.
+@end menu
+
+@node Touchscreens
+@section Using Emacs on touchscreens
+@cindex touchscreen input
+
+  Touchscreen input works by pressing and moving tools (which include
+fingers and some pointing devices--styluses, for example) onto a frame
+in order to manipulate its contents.
+
+  When running under the X Window System or Android, Emacs
+automatically detects and maps the following sequences of movements
+(``gestures'') to common actions:
+
+@itemize @bullet
+@item
+@cindex tapping, touchscreens
+  ``Tapping'', briefly placing and lifting a tool from the display,
+will result in Emacs selecting the window that was tapped, and
+executing any command bound to @code{mouse-1} at that location in the
+window.  If the tap happened on top of a link (@pxref{Mouse
+References}), then Emacs will follow the link instead.
+
+  If a command bound to @code{down-mouse-1} is bound to the location
+where the tap took place, Emacs will execute that command as well.
+
+@item
+@cindex scrolling, touchscreens
+  ``Scrolling'', meaning to place a tool on the display and move it up
+or down, will result in Emacs scrolling the window contents in the
+direction where the tool moves.
+
+  If the tool is moved left or right, Emacs additionally scrolls the
+window horizontally to follow (@pxref{Horizontal Scrolling}.)
+
+@item
+@cindex dragging, touchscreens
+@cindex long-press, touchscreens
+  ``Dragging'', which is performing a @dfn{long-press} by placing a
+tool on the display and leaving it there for a while prior to moving
+the tool around will make Emacs set the point to where the tool was
+and begin selecting text under the tool as it moves around, as if
+@code{mouse-1} were to be held down.  @xref{Mouse Commands}.
+
+@vindex touch-screen-word-select
+@cindex word selection mode, touchscreens
+  Some people find it difficult to position a tool accurately on a
+touch screen display, to the detriment of text selection.  The user
+option @code{touch-screen-word-select} enables ``word selection
+mode'', causing dragging to select the complete word, not only the
+character containing the position of the tool.
+
+@vindex touch-screen-extend-selection
+@cindex extending the selection, touchscreens
+  Similarly, it may be difficult to select all of the text intended
+within a single gesture.  If the user option
+@code{touch-screen-extend-selection} is enabled, taps on the locations
+of the point or the mark within a window will begin a new ``drag''
+gesture, where the region will be extended in the direction of any
+subsequent movement.
+
+@vindex touch-screen-preview-select
+@cindex previewing the region during selection, touchscreens
+  Difficulties in making accurate adjustments to the region can also
+be alleviated by indicating the position of the point relative to its
+containing line within the echo area, since the window cursor may be
+physically obscured by the tool.  If
+@code{touch-screen-preview-select} is non-@code{nil}, the line
+containing point is displayed in the echo area (@pxref{Echo Area})
+during the motion of the tool, followed by another line indicating the
+position of point within the first line.
+@end itemize
+
+@vindex touch-screen-delay
+  By default, Emacs considers a tool as having been left on the
+display long enough to trigger a ``long-press'' after 0.7 seconds, but
+this can be changed by customizing the variable
+@code{touch-screen-delay}.
+
+@node On-Screen Keyboards
+@section Using Emacs with virtual keyboards
+@cindex virtual keyboards
+@cindex on-screen keyboards
+
+  When there is no physical keyboard attached to a system, the
+windowing system typically provides an on-screen keyboard, more often
+known as a ``virtual keyboard'', containing rows of clickable buttons
+that send keyboard input to the application, much like a real keyboard
+would.  This virtual keyboard is hidden by default, as it uses up
+valuable on-screen real estate, and must be opened once the program
+being used is ready to accept keyboard input.
+
+  Under the X Window System, the client that provides the on-screen
+keyboard typically detects when the application is ready to accept
+keyboard input through a set of complex heuristics, and automatically
+displays the keyboard when necessary.
+
+  On other systems such as Android, Emacs must tell the system when it
+is ready to accept keyboard input.  Typically, this is done in
+response to a touchscreen ``tap'' gesture (@pxref{Touchscreens}), or
+once to the minibuffer becomes in use (@pxref{Minibuffer}.)
+
+@vindex touch-screen-set-point-commands
+  When a ``tap'' gesture results in a command being executed, Emacs
+checks to see whether or not the command is supposed to set the point
+by looking for it in the list @code{touch-screen-set-point-commands}.
+If it is, then Emacs looks up whether or not the text under the point
+is read-only; if not, it activates the on-screen keyboard, assuming
+that the user is about to enter text in to the current buffer.
+
+@vindex touch-screen-display-keyboard
+  The user option @code{touch-screen-display-keyboard} forces Emacs to
+always display the on screen keyboard; it may also be set buffer
+locally, which means that Emacs should always display the keyboard
+when the buffer is selected.
+
+  Emacs also provides a set of functions to show or hide the on-screen
+keyboard.  For more details, @pxref{On-Screen Keyboards,,, elisp, The
+Emacs Lisp Reference Manual}.
+
+@cindex quitting, without a keyboard
+  Since it may not be possible for Emacs to display the on screen
+keyboard while it is executing a command, Emacs implements a feature
+on devices with only an on-screen keyboard, by which two rapid clicks
+of a hardware button that is always present on the device results in
+Emacs quitting.  @xref{Quitting}.
+
+@vindex x-quit-keysym
+  Which button is used to do this depends on the window system in use:
+on X, it is defined in the variable @code{x-quit-keysym}, and on
+Android, it is always the volume down button.
+
+@cindex text conversion, keyboards
+  Most input methods designed to work with on-screen keyboards perform
+buffer edits differently from desktop input methods.
+
+  On a conventional desktop windowing system, an input method will
+simply display the contents of any on going character compositions on
+screen, and send the appropriate key events to Emacs after completion.
+
+  However, on screen keyboard input methods directly perform edits to
+the selected window of each frame; this is known as ``text
+conversion'', or ``string conversion'' under the X Window System.
+Emacs enables these input methods whenever the buffer local value of
+@code{text-conversion-style} is non-@code{nil}, normally inside
+derivatives of @code{text-mode} and @code{prog-mode}.
+
+  Text conversion is performed asynchronously whenever Emacs receives
+a request to perform the conversion from the input method, and Emacs
+is not currently reading a key sequence for which one prefix key has
+already been read (@pxref{Keys}.)  After the conversion completes, a
+@code{text-conversion} event is sent.  @xref{Misc Events,,, elisp, the
+Emacs Reference Manual}.
+
+@vindex text-conversion-face
+  If the input method needs to work on a region of the buffer, then
+the region becomes known as the ``composing region'' (or
+``preconversion region''.)  The variable @code{text-conversion-face}
+describes whether or not to display the composing region in a specific
+face.
diff --git a/doc/emacs/windows.texi b/doc/emacs/windows.texi
index e4abdef76be..665fd80e53b 100644
--- a/doc/emacs/windows.texi
+++ b/doc/emacs/windows.texi
@@ -642,6 +642,13 @@ to the window-local tab line of buffers, and clicking on 
the @kbd{x}
 icon of a tab deletes it.  The mouse wheel on the tab line scrolls
 the tabs horizontally.
 
+  Touch screen input (@pxref{Other Input Devices}) can also be used to
+interact with the ``tab line''.  Long-pressing (@pxref{Touchscreens})
+a tab will display a context menu with items that operate on the tab
+that was pressed; tapping a tab itself will result in switching to
+that tab's buffer, and tapping a button on the tab line will behave as
+if it was clicked with @kbd{mouse-1}.
+
 Selecting the previous window-local tab is the same as typing @kbd{C-x
 @key{LEFT}} (@code{previous-buffer}), selecting the next tab is the
 same as @kbd{C-x @key{RIGHT}} (@code{next-buffer}).  Both commands
diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi
index b631baf8ae2..2991799e668 100644
--- a/doc/lispref/commands.texi
+++ b/doc/lispref/commands.texi
@@ -2013,6 +2013,10 @@ the position of the finger when the event occurred.
 This event is sent when @var{point} is created by the user pressing a
 finger against the touchscreen.
 
+Imaginary prefix keys are also affixed to these events
+@code{read-key-sequence} when they originate on top of a special part
+of a frame or window.  @xref{Key Sequence Input}.
+
 @cindex @code{touchscreen-update} event
 @item (touchscreen-update @var{points})
 This event is sent when a point on the touchscreen has changed
@@ -2029,8 +2033,172 @@ raised the finger from the touchscreen.
 intercepted by another program (such as the window manager), and Emacs
 should undo or avoid any editing commands that would otherwise result
 from the touch sequence.
+
+Imaginary prefix keys are also affixed to these events
+@code{read-key-sequence} when they originate on top of a special part
+of a frame or window.
+@end table
+
+If a touchpoint is pressed against the menu bar, then Emacs will not
+generate any corresponding @code{touchscreen-begin} or
+@code{touchscreen-end} events; instead, the menu bar may be displayed
+after @code{touchscreen-end} would have been delivered under other
+circumstances.
+
+@cindex mouse emulation from touch screen events
+When no command is bound to @code{touchscreen-begin},
+@code{touchscreen-end} or @code{touchscreen-update}, Emacs calls a
+``key translation function'' (@pxref{Translation Keymaps}) to
+translate key sequences containing touch screen events into ordinary
+mouse events (@pxref{Mouse Events}.)  Since Emacs doesn't support
+distinguishing events originating from separate mouse devices, it
+assumes that only one touchpoint is active while translation takes
+place; breaking this assumption may lead to unexpected behavior.
+
+Emacs applies two different strategies for translating touch events
+into mouse events, contingent on factors such as the commands bound to
+keymaps that are active at the location of the
+@code{touchscreen-begin} event.  If a command is bound to
+@code{down-mouse-1} at that location, the initial translation consists
+of a single @code{down-mouse-1} event, with subsequent
+@code{touchscreen-update} events translated to mouse motion events
+(@pxref{Motion Events}), and a final @code{touchscreen-end} event
+translated to a @code{mouse-1} or @code{drag-mouse-1} event (unless
+the @code{touchscreen-end} event indicates that the touch sequence has
+been intercepted by another program.)  This is dubbed ``simple
+translation'', and produces a simple correspondence between touchpoint
+motion and mouse motion.
+
+@cindex @code{ignored-mouse-command}, a symbol property
+However, some commands bound to
+@code{down-mouse-1}--@code{mouse-drag-region}, for example--either
+conflict with defined touch screen gestures (such as ``long-press to
+drag''), or with user expectations for touch input, and shouldn't
+subject the touch sequence to simple translation.  If a command whose
+name contains the property (@pxref{Symbol Properties})
+@code{ignored-mouse-command} is encountered or there is no command
+bound to @code{down-mouse-1}, a more irregular form of translation
+takes place: here, Emacs processes touch screen gestures
+(@pxref{Touchscreens,,, emacs, The GNU Emacs Manual}) first, and
+finally attempts to translate touch screen events into mouse events if
+no gesture was detected prior to a closing @code{touchscreen-end}
+event (with its @var{canceled} parameter @code{nil}, as with simple
+translation) and a command is bound to @code{mouse-1} at the location
+of that event.  Before generating the @code{mouse-1} event, point is
+also set to the location of the @code{touchscreen-end} event, and the
+window containing the position of that event is selected, as a
+compromise for packages which assume @code{mouse-drag-region} has
+already set point to the location of any mouse click and selected the
+window where it took place.
+
+To prevent unwanted @code{mouse-1} events arriving after a mouse menu
+is dismissed (@pxref{Mouse Menus}), Emacs also avoids simple
+translation if @code{down-mouse-1} is bound to a keymap, making it a
+prefix key.  In lieu of simple translation, it translates the closing
+@code{touchscreen-end} to a @code{down-mouse-1} event with the
+starting position of the touch sequence, consequentially displaying
+the mouse menu.
+
+@cindex @code{mouse-1-menu-command}, a symbol property
+Since certain commands are also bound to @code{down-mouse-1} for the
+purpose of displaying pop-up menus, Emacs additionally behaves as
+illustrated in the last paragraph if @code{down-mouse-1} is bound to a
+command whose name has the property @code{mouse-1-menu-command}.
+
+@cindex touchscreen gesture events
+If touch gestures are detected during translation, one of the
+following input events may be generated:
+
+@table @code
+@cindex @code{touchscreen-scroll} event
+@item (touchscreen-scroll @var{window} @var{dx} @var{dy})
+If a ``scrolling'' gesture is detected during the translation process,
+each subsequent @code{touchscreen-update} event is translated to a
+@code{touchscreen-scroll} event, where @var{dx} and @var{dy} specify,
+in pixels, the relative motion of the touchpoint from the position of
+the @code{touchscreen-begin} event that started the sequence or the
+last @code{touchscreen-scroll} event, whichever came later.
+
+@cindex @code{touchscreen-hold} event
+@item (touchscreen-hold @var{posn})
+If the single active touchpoint remains stationary for more than
+@code{touch-screen-delay} seconds after a @code{touchscreen-begin} is
+generated, a ``long-press'' gesture is detected during the translation
+process, and a @code{touchscreen-hold} event is sent, with @var{posn}
+set to a mouse position list containing the position of the
+@code{touchscreen-begin} event.
+
+@cindex @code{touchscreen-drag} event
+@item (touchscreen-drag @var{posn})
+If a ``long-press'' gesture is detected while translating the current
+touch sequence or ``drag-to-select'' is being resumed as a result of
+the @code{touch-screen-extend-selection} user option, a
+@code{touchscreen-drag} event is sent upon each subsequent
+@code{touchscreen-update} event with @var{posn} set to the new
+position of the touchpoint.
+
+@cindex @code{touchscreen-restart-drag} event
+@item (touchscreen-restart-drag @var{posn})
+This event is sent upon the start of a touch sequence resulting in the
+continuation of a ``drag-to-select'' gesture (subject to the
+aformentioned user option) with @var{posn} set to the position list of
+the initial @code{touchscreen-begin} event within that touch sequence.
 @end table
 
+@cindex handling touch screen events
+@cindex tap and drag, touch screen gestures
+Several functions are provided for Lisp programs that handle touch
+screen events.  The intended use of the first two functions described
+below is from commands bound directly to @code{touchscreen-begin}
+events; they allow responding to commonly used touch screen gestures
+separately from mouse event translation.
+
+@defun touch-screen-track-tap event &optional update data
+This function is used to track a single ``tap'' gesture originating
+from the @code{touchscreen-begin} event @var{event}, often used to
+set the point or to activate a button.  It waits for a
+@code{touchscreen-end} event with the same touch identifier to arrive,
+at which point it returns @code{t}, signifying the end of the gesture.
+
+If a @code{touchscreen-update} event arrives in the mean time and
+contains at least one touchpoint with the same identifier as in
+@var{event}, the function @var{update} is called with two arguments,
+the list of touchpoints in that @code{touchscreen-update} event, and
+@var{data}.
+
+If any other event arrives in the mean time, @code{nil} is returned.
+The caller should not perform any action in that case.
+@end defun
+
+@defun touch-screen-track-drag event update &optional data
+This function is used to track a single ``drag'' gesture originating
+from the @code{touchscreen-begin} event @code{event}.
+
+It behaves like @code{touch-screen-track-tap}, except that it returns
+@code{no-drag} and refrains from calling @var{update} if the
+touchpoint in @code{event} did not move far enough (by default, 5
+pixels from its position in @code{event}) to qualify as an actual
+drag.
+@end defun
+
+In addition to those two functions, a function is provided for
+commands bound to some types of events generated through mouse event
+translation to prevent unwanted events from being generated after it
+is called.
+
+@defun touch-screen-inhibit-drag
+This function inhibits the generation of @code{touchscreen-drag}
+events during mouse event translation for the duration of the touch
+sequence being translated after it is called.  It must be called from
+a command which is bound to a @code{touchscreen-hold} or
+@code{touchscreen-drag} event, and signals an error otherwise.
+
+Since this function can only be called after a gesture is already
+recognized during mouse event translation, no mouse events will be
+generated from touch events constituting the previously mentioned
+touch sequence after it is called.
+@end defun
+
 @node Focus Events
 @subsection Focus Events
 @cindex focus event
@@ -2167,6 +2335,65 @@ the buffer in which the xwidget will be displayed, using
 A few other event types represent occurrences within the system.
 
 @table @code
+@cindex @code{text-conversion} event
+@item text-conversion
+This kind of event is sent @strong{after} a system-wide input method
+performs an edit to one or more buffers.
+
+@vindex text-conversion-edits
+Once the event is sent, the input method may already have made changes
+to multiple buffers inside many different frames.  To determine which
+buffers have been changed, and what edits have been made to them, use
+the variable @code{text-conversion-edits}, which is set prior to each
+@code{text-conversion} event being sent; it is a list of the form:
+
+@example
+@w{@code{((@var{buffer} @var{beg} @var{end} @var{ephemeral}) ...)}}
+@end example
+
+Where @var{ephemeral} is the buffer which was modified, @var{beg} and
+@var{end} are markers set to the positions of the edit at the time it
+was completed, and @var{ephemeral} is either a string, containing any
+text which was inserted (or any text before point which was deleted),
+@code{t}, meaning that the edit is a temporary edit made by the input
+method, or @code{nil}, meaning that some text was deleted after point.
+
+@vindex text-conversion-style
+Whether or not this event is sent depends on the value of the
+buffer-local variable @code{text-conversion-style}, which determines
+how an input method that wishes to make edits to buffer contents will
+behave.
+
+This variable can have one of three values:
+
+@table @code
+@item nil
+This means that the input method will be disabled entirely, and key
+events will be sent instead of text conversion events.
+
+@item action
+This means that the input method will be enabled, but @key{RET} will
+be sent whenever the input method wants to insert a new line.
+
+@item t
+This, or any other value, means that the input method will be enabled
+and make edits followed by @code{text-conversion} events.
+@end table
+
+@findex set-text-conversion-style
+Changes to the value of this variable will only take effect upon the
+next redisplay after the buffer becomes the selected buffer of a
+frame.  If you need to disable text conversion in a way that takes
+immediate effect, call the function @code{set-text-conversion-style}
+instead.  This has the potential to lock up the input method for a
+significant amount of time, and should be used with care.
+
+@vindex disable-inhibit-text-conversion
+In addition, text conversion is automatically disabled after a prefix
+key is read by the command loop or @code{read-key-sequence}.  This can
+be disabled by setting or binding the variable
+@code{disable-inhibit-text-conversion} to a non-@code{nil} value.
+
 @cindex @code{delete-frame} event
 @item (delete-frame (@var{frame}))
 This kind of event indicates that the user gave the window manager
@@ -2946,7 +3173,7 @@ debugging terminal input.
 @code{read-key-sequence}.  Lisp programs can also call this function;
 for example, @code{describe-key} uses it to read the key to describe.
 
-@defun read-key-sequence prompt &optional continue-echo dont-downcase-last 
switch-frame-ok command-loop
+@defun read-key-sequence prompt &optional continue-echo dont-downcase-last 
switch-frame-ok command-loop disable-text-conversion
 This function reads a key sequence and returns it as a string or
 vector.  It keeps reading events until it has accumulated a complete key
 sequence; that is, enough to specify a non-prefix command using the
@@ -2986,6 +3213,12 @@ key sequence is being read by something that will read 
commands one
 after another.  It should be @code{nil} if the caller will read just
 one key sequence.
 
+The argument @var{disable-text-conversion}, if non-@code{nil}, means
+that system input methods will not directly perform edits to buffer
+text while this key sequence is being read; user input will always
+generated individual key events instead.  @xref{Misc Events} for more
+about text conversion.
+
 In the following example, Emacs displays the prompt @samp{?} in the
 echo area, and then the user types @kbd{C-x C-f}.
 
@@ -3006,7 +3239,7 @@ typed while reading with this function works like any 
other character,
 and does not set @code{quit-flag}.  @xref{Quitting}.
 @end defun
 
-@defun read-key-sequence-vector prompt &optional continue-echo 
dont-downcase-last switch-frame-ok command-loop
+@defun read-key-sequence-vector prompt &optional continue-echo 
dont-downcase-last switch-frame-ok command-loop disable-text-conversion
 This is like @code{read-key-sequence} except that it always
 returns the key sequence as a vector, never as a string.
 @xref{Strings of Events}.
@@ -3054,19 +3287,22 @@ with any other events.
 @cindex @code{right-divider}, prefix key
 @cindex @code{bottom-divider}, prefix key
 @cindex mouse events, in special parts of window or frame
-When mouse events occur in special parts of a window or frame, such as a mode
+@cindex touch screen events, in special parts of window or frame
+When mouse or @code{touchscreen-begin} and @code{touchscreen-end}
+events occur in special parts of a window or frame, such as a mode
 line or a scroll bar, the event type shows nothing special---it is the
 same symbol that would normally represent that combination of mouse
-button and modifier keys.  The information about the window part is kept
-elsewhere in the event---in the coordinates.  But
+button and modifier keys.  The information about the window part is
+kept elsewhere in the event---in the coordinates.  But
 @code{read-key-sequence} translates this information into imaginary
-prefix keys, all of which are symbols: @code{tab-line}, @code{header-line},
-@code{horizontal-scroll-bar}, @code{menu-bar}, @code{tab-bar}, 
@code{mode-line},
-@code{vertical-line}, @code{vertical-scroll-bar}, @code{left-margin},
-@code{right-margin}, @code{left-fringe}, @code{right-fringe},
-@code{right-divider}, and @code{bottom-divider}.  You can define meanings for
-mouse clicks in special window parts by defining key sequences using these
-imaginary prefix keys.
+prefix keys, all of which are symbols: @code{tab-line},
+@code{header-line}, @code{horizontal-scroll-bar}, @code{menu-bar},
+@code{tab-bar}, @code{mode-line}, @code{vertical-line},
+@code{vertical-scroll-bar}, @code{left-margin}, @code{right-margin},
+@code{left-fringe}, @code{right-fringe}, @code{right-divider}, and
+@code{bottom-divider}.  You can define meanings for mouse clicks in
+special window parts by defining key sequences using these imaginary
+prefix keys.
 
 For example, if you call @code{read-key-sequence} and then click the
 mouse on the window's mode line, you get two events, like this:
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index e229935170f..0331c48fa6b 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -2942,8 +2942,9 @@ apply to.  Here are the possible values of 
@var{characteristic}:
 The kind of window system the terminal uses---either @code{graphic}
 (any graphics-capable display), @code{x}, @code{pc} (for the MS-DOS
 console), @code{w32} (for MS Windows 9X/NT/2K/XP), @code{haiku} (for
-Haiku), @code{pgtk} (for pure GTK), or @code{tty} (a non-graphics-capable
-display).  @xref{Window Systems, window-system}.
+Haiku), @code{pgtk} (for pure GTK), @code{android} (for Android), or
+@code{tty} (a non-graphics-capable display).  @xref{Window Systems,
+window-system}.
 
 @item class
 What kinds of colors the terminal supports---either @code{color},
@@ -5287,10 +5288,18 @@ the ``pixel width'' of a character is usually 1, but it 
could be more
 for TABs and double-width CJK characters.)
 
 @item :align-to @var{hpos}
-Specifies that the space should be wide enough to reach @var{hpos}.
-If @var{hpos} is a number, it is measured in units of the normal
-character width.  @var{hpos} can also be a @dfn{pixel width}
-specification (@pxref{Pixel Specification}).
+Specifies that the space should be wide enough to reach the column
+@var{hpos}.  If @var{hpos} is a number, it is a column number, and is
+measured in units of the canonical character width (@pxref{Frame
+Font}).  @var{hpos} can also be a @dfn{pixel width} specification
+(@pxref{Pixel Specification}).  When the current line is wider than
+the window, and is either displayed by one or more continuation lines,
+or is truncated and possibly scrolled horizontally (@pxref{Horizontal
+Scrolling}), @var{hpos} is measured from the beginning of the logical
+line, not from the visual beginning of the screen line.  This way,
+alignment produced by @code{:align-to} is consistent with functions
+that count columns, such as @code{current-column} and
+@code{move-to-column} (@pxref{Columns}).
 @end table
 
   You should use one and only one of the above properties.  You can
@@ -8866,6 +8875,8 @@ Emacs is displaying the frame using MS-DOS direct screen 
writes.
 Emacs is displaying the frame using the Application Kit on Haiku.
 @item pgtk
 Emacs is displaying the frame using pure GTK facilities.
+@item android
+Emacs is displaying the frame on Android.
 @item nil
 Emacs is displaying the frame on a character-based terminal.
 @end table
diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi
index a1d7b51b609..72441c8d442 100644
--- a/doc/lispref/elisp.texi
+++ b/doc/lispref/elisp.texi
@@ -1139,6 +1139,7 @@ Frames
 * Dialog Boxes::            Displaying a box to ask yes or no.
 * Pointer Shape::           Specifying the shape of the mouse pointer.
 * Window System Selections::Transferring text to and from other X clients.
+* Accessing Selections::    The multiple different kinds of selections.
 * Yanking Media::           Yanking things that aren't plain text.
 * Drag and Drop::           Internals of Drag-and-Drop implementation.
 * Color Names::             Getting the definitions of color names.
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index 368def90d85..853ca5ae69e 100644
--- a/doc/lispref/frames.texi
+++ b/doc/lispref/frames.texi
@@ -104,9 +104,11 @@ window of another Emacs frame.  @xref{Child Frames}.
 * Mouse Tracking::              Getting events that say when the mouse moves.
 * Mouse Position::              Asking where the mouse is, or moving it.
 * Pop-Up Menus::                Displaying a menu for the user to select from.
+* On-Screen Keyboards::                Displaying the virtual keyboard.
 * Dialog Boxes::                Displaying a box to ask yes or no.
 * Pointer Shape::               Specifying the shape of the mouse pointer.
 * Window System Selections::    Transferring text to and from other X clients.
+* Accessing Selections::        The multiple different kinds of selections.
 * Yanking Media::               Yanking things that aren't plain text.
 * Drag and Drop::               Internals of Drag-and-Drop implementation.
 * Color Names::                 Getting the definitions of color names.
@@ -700,7 +702,7 @@ The position of the top left corner of the native frame 
specifies the
 indicate that position for the various builds:
 
 @itemize @w{}
-@item (1) non-toolkit, Haiku, and terminal frames
+@item (1) non-toolkit, Android, Haiku, and terminal frames
 
 @item (2) Lucid, Motif, and MS-Windows frames
 
@@ -2408,6 +2410,7 @@ engine), and @code{harfbuzz} (font driver for OTF and TTF 
fonts with
 HarfBuzz text shaping) (@pxref{Windows Fonts,,, emacs, The GNU Emacs
 Manual}).  The @code{harfbuzz} driver is similarly recommended.  On
 Haiku, there can be several font drivers (@pxref{Haiku Fonts,,, emacs,
+The GNU Emacs Manual}), as on Android (@pxref{Android Fonts,,, emacs,
 The GNU Emacs Manual}).
 
 On other systems, there is only one available font backend, so it does
@@ -3753,9 +3756,9 @@ This function displays a pop-up menu and returns an 
indication of
 what selection the user makes.
 
 The argument @var{position} specifies where on the screen to put the
-top left corner of the menu.  It can be either a mouse button event
-(which says to put the menu where the user actuated the button) or a
-list of this form:
+top left corner of the menu.  It can be either a mouse button or
+@code{touchscreen-begin} event (which says to put the menu where the
+user actuated the button) or a list of this form:
 
 @example
 ((@var{xoffset} @var{yoffset}) @var{window})
@@ -3840,6 +3843,30 @@ keymap.  It won't be called if @code{x-popup-menu} 
returns for some
 other reason without displaying a pop-up menu.
 @end defvar
 
+@node On-Screen Keyboards
+@section On-Screen Keyboards
+
+  An on-screen keyboard is a special kind of pop up provided by the
+system, with rows of clickable buttons that act as a real keyboard.
+
+  On certain systems (@pxref{On-Screen Keyboards,,,emacs, The Emacs
+Manual}), Emacs is supposed to display and hide the on screen keyboard
+depending on whether or not the user is about to type something.
+
+@defun frame-toggle-on-screen-keyboard frame hide
+This function displays or hides the on-screen keyboard on behalf of
+the frame @var{frame}.  If @var{hide} is non-@code{nil}, then the
+on-screen keyboard is hidden; otherwise, it is displayed.
+
+It returns whether or not the on screen keyboard @strong{may} have
+been displayed, which should be used to determine whether or not to
+hide the on-screen keyboard later.
+
+This has no effect if the system automatically detects when to display
+the on-screen keyboard, or when it does not provide any on-screen
+keyboard.
+@end defun
+
 @node Dialog Boxes
 @section Dialog Boxes
 @cindex dialog boxes
@@ -4047,6 +4074,542 @@ For backward compatibility, there are obsolete aliases
 names of @code{gui-get-selection} and @code{gui-set-selection} before
 Emacs 25.1.
 
+@node Accessing Selections
+@section Accessing Selections
+
+  @code{gui-get-selection} is able to retrieve multiple different
+kinds of selection data from any number of selections.  However, the
+data types and selections that Emacs understands is not precisely
+specified and differs depending on the window system on which Emacs is
+running.
+
+  At the same time, @code{gui-set-selection} hides a great deal of
+complexity behind its back, at least on some systems: its @var{data}
+argument need not be a string, but is actually given verbatim to
+system specific code.
+
+  Emacs's implementation of selections is most complete on the X
+Window System.  This is both an artifact of history (X was the first
+window system supported by Emacs) and one of technical reasons:
+instead of using selections only to transfer text and multimedia
+content between clients, X uses selections as a general inter-client
+communication system, leading to a great proliferation of selection
+data types.
+
+  Even more confusingly, X also supports another inter-client
+communication mechanism: the Inter-Client Exchange.  However, ICE is
+only used by Emacs to communicate with session managers, and is a
+separate topic.
+
+@menu
+* X Selections::        Selection data types (and more) on X.
+* Other Selections::    How they work on other window systems.
+@end menu
+
+@node X Selections
+@subsection X Selections
+
+  X refrains from defining fixed data types for selection data, or a
+fixed number of selections.  Selections are instead identified by X
+``atoms'', which are unique 29-bit identifiers issued by the X server
+for a corresponding name.  In Emacs, you can simply write a symbol
+with the name of the atom, and Emacs will transparently request these
+identifiers where necessary.
+
+  When a program ``sets'' a selection under X, it actually makes
+itself the ``owner'' of the selection---the X server will then deliver
+selection requests to the program, which is obliged to respond to the
+requesting client with the selection data.
+
+  Similarly, a program does not ``get'' selection data from the X
+server.  Instead, its selection requests are sent to the client with
+the window which last took ownership over the selection, which then
+replies with the requested data.
+
+  Each selection request contains three parameters:
+
+@itemize @bullet
+@item
+The window which requested the selection; this is used to identify the
+@c Not a typo: X spells ``requestor'' with an o.
+requesting program, otherwise known as the @dfn{requestor}.
+
+@item
+An atom identifying the ``target'' to which the owner should convert
+the selection.  It is easiest to think of the conversion target as the
+kind of data that the requestor wants: in selection requests made by
+Emacs, the target is determined by the @dfn{type} argument to
+@code{gui-get-selection}.
+
+@item
+A 32-bit timestamp containing the X server time at which the requestor
+last obtained input.
+@end itemize
+
+  The selection owner responds by tranferring to the requestor a
+series of bytes, 16 bit words, or 32 bit words, along with another
+atom identifying the type of those words.  After requesting a
+selection, Emacs then applies its own interpretation of the data
+format and data type to convert the data transferred by the selection
+owner to a Lisp representation, which @code{gui-get-selection}
+returns.
+
+  By default, Emacs converts selection data consisting of any series
+of bytes to a unibyte string containing those bytes, selection data
+consisting of a single 16-bit or 32-bit word as an unsigned number,
+and selection data consisting of multiple such words as a vector of
+unsigned numbers.  However, Emacs applies special treatment for
+several selection data types:
+
+@table @code
+@item INTEGER
+16-bit or 32-bit words of this type are treated as signed integers,
+instead of unsigned ones.  If there are multiple words in the
+selection data, a vector is returned; otherwise, the integer is
+returned by itself.
+
+@item ATOM
+32-bit words of this type are treated as X atoms, and returned (either
+alone or as vectors) as Lisp symbols containing the names they
+identify.  Invalid atoms are returned as @code{nil}.
+
+@item COMPOUND_TEXT
+@item UTF8_STRING
+@item STRING
+Unibyte strings returned for these data types will have a single
+@code{foreign-selection} text property set to a symbol with the type
+of the selection data.
+@end table
+
+  Each selection owner must return at least two selection targets:
+@code{TARGETS}, which returns a number of atoms describing the
+selection targets that the owner supports, and @code{MULTIPLE}, used
+for internal purposes by X clients.  A selection owner may support any
+number of other targets, some of which may be standardized by the X
+Consortium's
+@url{http://x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html,
+Inter-Client Communication Conventions Manual}, while others, such as
+@code{UTF8_STRING}, were supposed to be standardized by the XFree86
+Project, which unfortunately did not happen.
+
+  Requests for a given selection target may, by convention, return
+data in a specific type, or it may return data in one of several
+types, whichever is most convenient for the selection owner; the
+latter type of selection target is dubbed a @dfn{polymorphic target}.
+A selection target may also return no data at all: by convention, the
+selection owner performs some action a side effect upon responding to
+a selection request with that target, and as such these targets are
+referred to as @dfn{side-effect targets}.
+
+  Here are some selection targets which behave in a reasonably
+standard manner when used with the @code{CLIPBOARD}, @code{PRIMARY},
+or @code{SECONDARY} selections.
+
+@table @code
+@item ADOBE_PORTABLE_DOCUMENT_FORMAT
+This target returns data in Adobe System's ``Portable Document
+Format'' format, as a string.
+
+@item APPLE_PICT
+This target returns data in the ``PICT'' image format used on
+Macintosh computers, as a string.
+
+@item BACKGROUND
+@item BITMAP
+@item COLORMAP
+@item FOREGROUND
+Together, these four targets return integer data necessary to make use
+of a bitmap image stored on the X server: the pixel value of the
+bitmap's background color, the X identifier of the bitmap, the
+colormap inside which the background and foreground are allocated, and
+the pixel value of the bitmap's foreground color.
+
+@item CHARACTER_POSITION
+This target returns two unsigned 32-bit integers of type @code{SPAN}
+describing the start and end positions of the selection data in the
+text field containing it, in bytes.
+
+@item COMPOUND_TEXT
+This target returns a string of type @code{COMPOUND_TEXT} in the X
+Consortium's multi-byte text encoding system.
+
+@item DELETE
+This target returns nothing, but as a side-effect deletes the
+selection contents from any text field containing them.
+
+@item DRAWABLE
+@item PIXMAP
+This target returns a list of unsigned 32-bit integers, each of which
+corresponds to an X server drawable or pixmap.
+
+@item ENCAPSULATED_POSTSCRIPT
+@item _ADOBE_EPS
+This target returns a string containing encapsulated Postscript code.
+
+@item FILE_NAME
+This target returns a string containing one or more file names,
+separated by NULL characters.
+
+@item HOST_NAME
+This target returns a string containing the fully-qualified domain
+name of the machine on which the selection owner is running.
+
+@item USER
+This target returns a string containing the user name of the machine
+on which the selection owner is running.
+
+@item LENGTH
+This target returns an unsigned 32-bit or 16-bit integer containing
+the length of the selection data.
+
+@item LINE_NUMBER
+This target returns two unsigned 32-bit integers of type @code{SPAN}
+describing the line numbers corresponding to the start and end
+positions of the selection data in the text field containing it.
+
+@item MODULE
+This target returns the name of any function containing the selection
+data.  It is mainly used by text editors.
+
+@item STRING
+This target returns the selection data as a string of type
+@code{STRING}, encoded in ISO Latin-1 format, with Unix newline
+characters.
+
+@item C_STRING
+This target returns the selection data as a ``C string''.  This has
+been interpreted as meaning the raw selection data in whatever
+encoding used by the owner, either terminated with a NULL byte or not
+at all, or an ASCII string which may or may not be terminated.
+
+@item UTF8_STRING
+This returns the selection data as a string of type
+@code{UTF8_STRING}, encoded in UTF-8, with unspecified EOL format.
+
+@item TIMESTAMP
+This target returns the X server time at which the selection owner
+took ownership over the selection as a 16-bit or 32-bit word of type
+@code{CARDINAL}.
+
+@item TEXT
+This polymorphic target returns selection data as a string, either
+@code{COMPOUND_TEXT}, @code{STRING}, @code{C_STRING}, or
+@code{UTF8_STRING}, whichever data type is convenient for the
+selection owner.
+@end table
+
+  When a request for the targets @code{STRING}, @code{COMPOUND_TEXT},
+or @code{UTF8_STRING} is made using the function
+@code{gui-get-selection}, and neither @code{selection-coding-system}
+nor @code{next-selection-coding-system} are set, the returned strings
+are additionally decoded using the appropriate coding system for those
+data types: @code{iso-8859-1}, @code{compound-text-with-extensions}
+and @code{utf-8} respectively.
+
+  In addition to the targets specified above (and the many targets
+used by various programs for their own purposes), several popular
+programs and toolkits have decided to define selection data types of
+their own, without consulting the appropriate X standards bodies.
+These targets are usually named after MIME types, such as
+@code{text/html} or @code{image/jpeg}, and have been known to contain:
+
+@itemize @bullet
+@item
+Unterminated, newline terminated, or NULL character terminated file
+names of an image or text file.
+
+@item
+Image or text data in the appropriate format.
+
+@item
+@code{file://} URIs (or possibly newline or NUL terminated lists of
+URIs) leading to files in the appropriate format.
+@end itemize
+
+  These selection targets were first used by Netscape, but are now
+found in all kinds of programs, especially those based on recent
+versions of the GTK+ or Qt toolkits.
+
+  Emacs is also capable of acting as a selection owner.  When
+@code{gui-set-selection} is called, the selection data specified is
+not transferred to the X server; instead, Emacs records it internally
+and obtains ownership of the selection.
+
+@defvar selection-converter-alist
+  Alist of selection targets to ``selection converter'' functions.
+When a selection request is received, Emacs looks up the selection
+converter associated with the requested selection target.
+
+  The selection converter is called with three arguments: the symbol
+corresponding to the atom identifying the selection being requested,
+the selection target that is being requested, and the value set with
+@code{gui-set-selection}.  The value which it returns is either a cons
+of a symbol specifying the data type and a number, symbol, or a vector
+of numbers or symbols, or its cdr by itself.
+
+  If the value is the special symbol @code{NULL}, the data type is set
+to @code{NULL}, and no data is returned to the requestor.
+
+  If the value is a string, it must be a unibyte string; should no
+data type be explicitly specified, the data is transferred to the
+requestor with the type @code{STRING}.
+
+  If the value is a symbol, its ``atom'' is retrieved, and it is
+transferred to the requestor as a 32-bit value---if no data type was
+specified, its type is @code{ATOM}.
+
+  If the value is a number between @code{-32769} and @code{32768}, it
+is transferred to the requestor as a 16 bit value---if no data type
+was specified, its type is @code{INTEGER}.
+
+  If the value is any other number, it is returned as a 32 bit value.
+Even if the number returned is unsigned, the requestor will treat
+words of type @code{INTEGER} as signed.  To return an unsigned value,
+specify the type @code{CARDINAL} instead.
+
+  If the value is a vector of symbols or numbers, it is returned as a
+list of multiple atoms or numbers.  The data type returned by default
+is determined by that of its first element.
+@end defvar
+
+  By default, Emacs is configured with selection converters for the
+following selection targets:
+
+@table @code
+@item TEXT
+This selection converter returns selection data as:
+
+@itemize @bullet
+@item
+A string of type @code{C_STRING}, if the selection contents contain no
+multibyte characters, or contains 8-bit characters with all 8 bits
+set.
+
+@item
+A string of type @code{STRING}, if the selection contents can be
+represented as ISO-Latin-1 text.
+
+@item
+A string of type @code{COMPOUND_TEXT}, if the selection contents can
+be encoded in the X Consortium's Compound Text Encoding, and
+@code{selection-coding-system} or @code{next-selection-coding-system}
+is set to a coding system whose @code{:mime-charset} property is
+@code{x-ctext}.
+
+@item
+A string of type @code{UTF8_STRING} otherwise.
+@end itemize
+
+@item COMPOUND_TEXT
+This selection converter returns selection data as a string of type
+@code{COMPOUND_TEXT}.
+
+@item STRING
+This selection converter returns selection data as a string of type
+@code{STRING}, encoded in ISO-Latin-1 format.
+
+@item UTF8_STRING
+This selection converter returns selection data in UTF-8 format.
+
+@item text/plain
+@item text/plain;charset=utf-8
+@item text/uri-list
+@item text/x-xdnd-username
+@item XmTRANSFER_SUCCESS
+@item XmTRANSFER_FAILURE
+@item FILE
+@item _DT_NETFILE
+These selection converters are used for internal purposes during
+drag-and-drop operations and are not available for selections other
+than @code{XdndSelection}.
+
+@item TARGETS
+This selection converter returns a list of atoms, one for each
+selection target understood by Emacs.
+
+@item MULTIPLE
+This selection converter is implemented in C code and is used to
+implement efficient transfer of selection requests which specify
+multiple selection targets at the same time.
+
+@item LENGTH
+This selection converter returns the length of the selection data, in
+bytes.
+
+@item DELETE
+This selection converter is used for internal purposes during
+drag-and-drop operations.
+
+@item FILE_NAME
+This selection converter returns the file name of the buffer
+containing the selection data.
+
+@item CHARACTER_POSITION
+This selection converter returns the character positions of each end
+of the selection in the buffer containing the selection data.
+
+@item LINE_NUMBER
+@item COLUMN_NUMBER
+This selection converter returns the line and column numbers of each
+end of the selection in the buffer containing the selection data.
+
+@item OWNER_OS
+This selection converter returns the name of the operating system on
+which Emacs is running.
+
+@item HOST_NAME
+This selection converter returns the fully-qualified domain name of
+the machine on which Emacs is running.
+
+@item USER
+This selection converter returns the username of the user account
+under which Emacs is running.
+
+@item CLASS
+@item NAME
+These selection converters return the resource class and name used by
+Emacs.
+
+@item INTEGER
+This selection converter returns an integer value verbatim.
+
+@item SAVE_TARGETS
+@item _EMACS_INTERNAL
+These selection converters are used for internal purposes.
+@end table
+
+  With the exception of @code{INTEGER}, all selection converters
+expect the value given to @code{gui-set-selection} to be one of the
+following:
+
+@itemize @bullet
+@item
+A string.
+
+@item
+A list of the form @w{@code{(@var{beg} @var{end} @var{buf})}}, where
+@var{beg} and @var{end} are two markers or overlays describing the
+bounds of the selection data in the buffer @var{buf}.
+@end itemize
+
+@node Other Selections
+@subsection Other Selections
+
+  Window systems such as MS-Windows, Nextstep, Haiku and Android do
+not provide selections corresponding to the X semantics.  Each window
+system provides its own ad-hoc emulation of selections, none of which
+make use of the ``selection converter'' mechanism described above.  In
+addition, only the @code{PRIMARY}, @code{CLIPBOARD}, and
+@code{SECONDARY} selections are typically supported, alongside the
+@code{XdndSelection} used for drag-and-drop operations.
+
+  GTK itself exposes emulations of X selections to applications, but
+those emulations are of varying completeness.  While Emacs built with
+PGTK will use the same selection interface as Emacs built with X, many
+selection targets will not be useful.
+
+  On MS-Windows, @code{gui-get-selection} accepts a single target,
+@code{STRING}.  The value returned is the selection data decoded
+using @code{selection-coding-system}.
+
+  @code{gui-set-selection} also only accepts strings, encodes them
+in the selection coding system, and saves them to the clipboard.
+
+  On Nextstep, Emacs only supports saving strings to selections.
+However, requests for the following targets are accepted:
+
+@c FIXME: how is the text coding system determined, and do image/* or
+@c application/* return image data or file names?
+@itemize @bullet
+@item text/plain
+@item image/png
+@item text/html
+@item application/pdf
+@item application/rtf
+@item application/rtfd
+@item STRING
+@item text/plain
+@item image/tiff
+@end itemize
+
+  On Haiku, Emacs supports the same selection values as on X.  In
+addition, Emacs fully implements the primary and secondary selections.
+However, instead of taking ownership over the selection data, Emacs
+transfers the selection data to the window server when
+@code{gui-set-selection} is called.  The Haiku window server expects
+selection data to be provided in the form of a ``message'', containing
+associations between data types and selection data.
+
+@defvar haiku-normal-selection-encoders
+List of functions which act as selection encoders.  When
+@code{gui-set-selection} is called, each function in this list is
+successively called with its @var{selection} and @var{value}
+arguments.  If the function returns non-@code{nil}, it should return a
+list of the form @w{@code{(@var{key} @var{type} @var{value})}}, where
+@var{key} is the name of the data type being transferred, @var{type}
+is either a number identifying a data type (in which case @var{value}
+should be a unibyte string that is directly transferred to the window
+server), or a symbol identifying both a data type and how @var{value}
+should be interpreted.
+@end defvar
+
+  Here are the meaningful values of @var{type}, and what they will
+cause Emacs to interpret @var{value} as:
+
+@table @code
+@item string
+A unibyte string.  The string is NULL-terminated after being placed in
+the message.
+
+@item ref
+A file name.  The file is looked up and file system information
+identifying the file is placed in the message.
+
+@item short
+A 16-bit integer value.
+
+@item long
+A 32-bit integer value.
+
+@item llong
+A 64-bit integer value.
+
+@item byte
+@item char
+An unsigned byte between 0 and 255.
+
+@item size_t
+A number between 0 and 1 minus two to the power of the word size of
+the computer Emacs is running on.
+
+@item ssize_t
+A number which fits in the C type @code{ssize_t}.
+
+@item point
+A cons of two floats, specifying a coordinate on-screen.
+
+@item float
+@item double
+A single or double-precision floating point number in an unspecified
+format.
+
+@item (haiku-numeric-enum MIME)
+A unibyte string containing data in a certain MIME type.
+@end table
+
+  Under Haiku, @code{gui-get-selection} accepts either the targets
+@code{TARGETS} and @code{TIMESTAMP}, where the former returns a vector
+containing supported data types (much like on X), and the latter
+returns the number of times the selection has been set, the targets
+@code{STRING} and @code{UTF8_STRING}, which return text in ISO-Latin-1
+and UTF-8 format, or a MIME type, in which the data is returned
+undecoded as a unibyte string.
+
+  Under Android, @code{gui-get-selection} is restricted to returning
+UTF-8 string data of the type @code{STRING}, or image and application
+data associated with a MIME type.  @code{gui-set-selection} will only
+set string data, as on MS-Windows.
+
 @node Yanking Media
 @section Yanking Media
 
@@ -4461,20 +5024,12 @@ really supports that color.  When using X, you can ask 
for any defined
 color on any kind of display, and you will get some result---typically,
 the closest it can do.  To determine whether a frame can really display
 a certain color, use @code{color-supported-p} (see below).
-
-@findex x-color-defined-p
-This function used to be called @code{x-color-defined-p},
-and that name is still supported as an alias.
 @end defun
 
 @defun defined-colors &optional frame
 This function returns a list of the color names that are defined
 and supported on frame @var{frame} (default, the selected frame).
 If @var{frame} does not support colors, the value is @code{nil}.
-
-@findex x-defined-colors
-This function used to be called @code{x-defined-colors},
-and that name is still supported as an alias.
 @end defun
 
 @defun color-supported-p color &optional frame background-p
@@ -4526,10 +5081,6 @@ The color values are returned for @var{frame}'s display. 
 If
 @var{frame} is omitted or @code{nil}, the information is returned for
 the selected frame's display.  If the frame cannot display colors, the
 value is @code{nil}.
-
-@findex x-color-values
-This function used to be called @code{x-color-values},
-and that name is still supported as an alias.
 @end defun
 
 @defun color-name-to-rgb color &optional frame
@@ -4714,10 +5265,7 @@ This function returns @code{t} if @var{display} has a 
mouse available,
 @end defun
 
 @defun display-color-p &optional display
-@findex x-display-color-p
 This function returns @code{t} if the screen is a color screen.
-It used to be called @code{x-display-color-p}, and that name
-is still supported as an alias.
 @end defun
 
 @defun display-grayscale-p &optional display
diff --git a/doc/lispref/keymaps.texi b/doc/lispref/keymaps.texi
index 24b7738caff..99bc10677cd 100644
--- a/doc/lispref/keymaps.texi
+++ b/doc/lispref/keymaps.texi
@@ -2044,6 +2044,15 @@ to turn the character that follows into a Hyper 
character:
 @end group
 @end example
 
+@cindex accessing events within a key translation function
+@vindex current-key-remap-sequence
+A key translation function might want to adjust its behavior based on
+parameters to events within a key sequence containing non-key events
+(@pxref{Input Events}.)  This information is available from the
+variable @code{current-key-remap-sequence}, which is bound to the key
+sub-sequence being translated around calls to key translation
+functions.
+
 @subsection Interaction with normal keymaps
 
 The end of a key sequence is detected when that key sequence either is bound
@@ -2588,6 +2597,12 @@ function should return the binding to use instead.
 Emacs can call this function at any time that it does redisplay or
 operates on menu data structures, so you should write it so it can
 safely be called at any time.
+
+@item :wrap @var{wrap-p}
+If @var{wrap-p} is non-nil inside a tool bar, the menu item is not
+displayed, but instead causes subsequent items to be displayed on a
+new line.  This is not supported when Emacs uses the GTK+ or Nextstep
+toolkits.
 @end table
 
 @node Menu Separators
@@ -3094,6 +3109,16 @@ specifies the local map to make the definition in.  The 
argument
 @code{tool-bar-add-item-from-menu}.
 @end defun
 
+@vindex secondary-tool-bar-map
+In addition to the tool bar items defined in @code{tool-bar-map},
+Emacs also supports displaying an additional row of ``secondary'' tool
+bar items specified in the keymap @code{secondary-tool-bar-map}.
+These items are normally displayed below those defined within
+@code{tool-bar-map} if the tool bar is positioned at the top of its
+frame, but are displayed above them if the tool bar is positioned at
+the bottom (@pxref{Layout Parameters}.)  They are not displayed if the
+tool bar is positioned at the left or right of a frame.
+
 @defvar auto-resize-tool-bars
 If this variable is non-@code{nil}, the tool bar automatically resizes to
 show all defined tool bar items---but not larger than a quarter of the
diff --git a/doc/lispref/minibuf.texi b/doc/lispref/minibuf.texi
index 31b020db57c..8ff5c14055e 100644
--- a/doc/lispref/minibuf.texi
+++ b/doc/lispref/minibuf.texi
@@ -1537,7 +1537,8 @@ that it uses the predicate @code{custom-variable-p} 
instead of
 @code{commandp}.
 @end defun
 
-@deffn Command read-color &optional prompt convert allow-empty display
+@deffn Command read-color &optional prompt convert allow-empty @
+  display foreground face
 This function reads a string that is a color specification, either the
 color's name or an RGB hex value such as @code{#RRRGGGBBB}.  It
 prompts with @var{prompt} (default: @code{"Color (name or #RGB triplet):"})
@@ -1557,6 +1558,13 @@ non-@code{nil} and the user enters null input.
 
 Interactively, or when @var{display} is non-@code{nil}, the return
 value is also displayed in the echo area.
+
+The optional arguments @var{foreground} and @var{face} control the
+appearence of the completion candidates in the @file{*Completions*}
+buffer.  The candidates are displayed in the specified @var{face} but
+with different colors: if @var{foreground} is non-@code{nil}, the
+foreground color is changed to be the color of the candidate,
+otherwise the background is changed to the candidate's color.
 @end deffn
 
   See also the functions @code{read-coding-system} and
diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index ebedfe82087..be8624e0fde 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -972,6 +972,9 @@ Hewlett-Packard HPUX operating system.
 @item nacl
 Google Native Client (@acronym{NaCl}) sandboxing system.
 
+@item android
+The Open Handset Alliance's Android operating system.
+
 @item ms-dos
 Microsoft's DOS@.  Emacs compiled with DJGPP for MS-DOS binds
 @code{system-type} to @code{ms-dos} even when you run it on MS-Windows.
diff --git a/doc/lispref/processes.texi b/doc/lispref/processes.texi
index 43c794104b8..adab9eb0d0c 100644
--- a/doc/lispref/processes.texi
+++ b/doc/lispref/processes.texi
@@ -185,6 +185,24 @@ respective remote host.  In case of a local 
@code{default-directory},
 the function returns just the value of the variable @code{exec-path}.
 @end defun
 
+@cindex programs distributed with Emacs, starting
+@vindex ctags-program-name
+@vindex etags-program-name
+@vindex hexl-program-name
+@vindex emacsclient-program-name
+@vindex movemail-program-name
+@vindex ebrowse-program-manem
+  When starting a program that is part of the Emacs distribution,
+you must take into account that the program may have been renamed in
+order to comply with executable naming restrictions present on the
+system.
+
+  Instead of starting @command{ctags}, for example, you should specify
+the value of @code{ctags-program-name} instead.  Likewise, instead of
+starting @command{movemail}, you must start
+@code{movemail-program-name}, and the same goes for @command{etags},
+@command{hexl}, @command{emacsclient}, and @command{ebrowse}.
+
 @node Shell Arguments
 @section Shell Arguments
 @cindex arguments for shell commands
diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi
index 9b9268ae4ea..7c7f304d55d 100644
--- a/doc/misc/eshell.texi
+++ b/doc/misc/eshell.texi
@@ -1715,7 +1715,7 @@ Treating the value as a file name, gets the directory 
name (the
 
 @item t
 Treating the value as a file name, gets the base name (the ``tail'').
-For example, @samp{foo/bar/baz.el(:h)} expands to @samp{baz.el}.
+For example, @samp{foo/bar/baz.el(:t)} expands to @samp{baz.el}.
 
 @item e
 Treating the value as a file name, gets the final extension of the
diff --git a/doc/misc/org.org b/doc/misc/org.org
index d8bbcb4d0c5..a4ce53cc6cb 100644
--- a/doc/misc/org.org
+++ b/doc/misc/org.org
@@ -3619,7 +3619,7 @@ replacement text.  Here is an example:
 
 #+begin_src emacs-lisp
 (setq org-link-abbrev-alist
-      '(("bugzilla"        . "http://10.1.2.9/bugzilla/show_bug.cgi?id=";)
+      '(("bugzilla"        . "https://10.1.2.9/bugzilla/show_bug.cgi?id=";)
         ("Nu Html Checker" . "https://validator.w3.org/nu/?doc=%h";)
         ("duckduckgo"      . "https://duckduckgo.com/?q=%s";)
         ("omap"            . 
"https://nominatim.openstreetmap.org/search?q=%s&polygon=1";)
@@ -20693,8 +20693,8 @@ adding ~:rewrites~ rules like this:
 
 #+texinfo: @noindent
 Since =example.com/$= is used as a regular expression, it maps
-=http://example.com/=, =https://example.com=,
-=http://www.example.com/= and similar to
+=https://example.com/=, =https://example.com=,
+=https://www.example.com/= and similar to
 =/home/user/example/index.php=.
 
 The ~:rewrites~ rules are searched as a last resort if and only if no
diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi
index e518330c9b0..291d6600af5 100644
--- a/doc/misc/tramp.texi
+++ b/doc/misc/tramp.texi
@@ -375,7 +375,7 @@ From behind a proxy:
 
 @example
 @group
-$ git config --global http.proxy http://user:pwd@@proxy.server.com:8080
+$ git config --global http.proxy https://user:pwd@@proxy.server.com:8080
 $ git clone https://git.savannah.gnu.org/r/tramp.git
 @end group
 @end example
diff --git a/doc/misc/url.texi b/doc/misc/url.texi
index 2f5f994b474..b5a6cb0e6a1 100644
--- a/doc/misc/url.texi
+++ b/doc/misc/url.texi
@@ -896,8 +896,8 @@ Creates a cache file name from @var{url} using MD5 hashing.
 This is creates entries with very few cache collisions and is fast.
 @cindex MD5
 @smallexample
-(url-cache-create-filename-using-md5 "http://www.example.com/foo/bar";)
-  @result{} 
"/home/fx/.url/cache/fx/http/com/example/www/b8a35774ad20db71c7c3409a5410e74f"
+(url-cache-create-filename-using-md5 "https://www.example.com/foo/bar";)
+  @result{} 
"/home/fx/.url/cache/fx/https/com/example/www/b8a35774ad20db71c7c3409a5410e74f"
 @end smallexample
 @end defun
 
@@ -906,8 +906,8 @@ Creates a cache file name from @var{url} more obviously 
connected to
 @var{url} than for @code{url-cache-create-filename-using-md5}, but
 more likely to conflict with other files.
 @smallexample
-(url-cache-create-filename-human-readable "http://www.example.com/foo/bar";)
-  @result{} "/home/fx/.url/cache/fx/http/com/example/www/foo/bar"
+(url-cache-create-filename-human-readable "https://www.example.com/foo/bar";)
+  @result{} "/home/fx/.url/cache/fx/https/com/example/www/foo/bar"
 @end smallexample
 @end defun
 
@@ -1159,7 +1159,7 @@ opened by the URL library.
 @c linked with the resolver libraries, it will not be able to get to any
 @c machines off the local network.  This is characterized by being able
 @c to reach someplace with a raw ip number, but not its hostname
-@c (@url{http://129.79.254.191/} works, but
+@c (@url{https://129.79.254.191/} works, but
 @c @url{https://www.cs.indiana.edu/} doesn't).  This used to happen on
 @c SunOS4 and Ultrix, but is now probably now rare.  If Emacs can't be
 @c rebuilt linked against the resolver library, it can use the external
diff --git a/etc/DEBUG b/etc/DEBUG
index c4f0852abb3..1cc575598bf 100644
--- a/etc/DEBUG
+++ b/etc/DEBUG
@@ -1099,6 +1099,39 @@ Please refer to the LLDB reference on the web for more 
information
 about LLDB.  If you already know GDB, you will also find a mapping
 from GDB commands to corresponding LLDB commands there.
 
+** Debugging Emacs on Android.
+
+Attaching GDB to Emacs running inside the Android application setup
+requires a special script found in the java/ directory, and a suitable
+GDB server binary to be present on the Android device, which is
+present on the free versions of Android.  Connecting to the device
+also requires the `adb' (Android Debug Bridge) utility, and telling
+the Android system to resume the Emacs process after startup requires
+the Java debugger (jdb).
+
+If all three of those tools are present, simply run (from the Emacs
+source directory):
+
+  ../java/debug.sh -- [any extra arguments you wish to pass to gdb]
+
+After which, upon waiting a while, the GDB prompt will show up.
+
+If Emacs crashes and "JNI ERROR" shows up in the Android system log,
+then placing a breakpoint on:
+
+  break art::JavaVMExt::JniAbort
+
+will let you find the source of the crash.
+
+If there is no `gdbserver' binary present on the device, then you can
+specify one to upload, like so:
+
+  ../java/debug.sh --gdbserver /path/to/gdbserver
+
+In addition, when Emacs runs as a 64-bit process on a system
+supporting both 64 and 32-bit binaries, you must specify the path to a
+64-bit gdbserver binary.
+
 
 This file is part of GNU Emacs.
 
diff --git a/etc/HISTORY b/etc/HISTORY
index 70f8669cb29..f6df3e6fe60 100644
--- a/etc/HISTORY
+++ b/etc/HISTORY
@@ -228,7 +228,8 @@ GNU Emacs 28.1 (2022-04-04) emacs-28.1
 
 GNU Emacs 28.2 (2022-09-12) emacs-28.2
 
-GNU Emacs 28.3 (2023-02-17) emacs-28.3 (was not actually released)
+GNU Emacs 28.3 (2023-02-17) emacs-28.3-rc1
+Was not actually released.
 
 GNU Emacs 29.1 (2023-07-30) emacs-29.1
 
diff --git a/etc/MACHINES b/etc/MACHINES
index 8c6f3f48ce7..751d59932c5 100644
--- a/etc/MACHINES
+++ b/etc/MACHINES
@@ -131,6 +131,18 @@ 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.2 onwards, on
+  Linux kernel 2.26.29 or later.
+
+  Android 2.2 has only been tested on ARM.  mips64 has not been
+  tested, but builds.  With these exceptions, Emacs is known to run on
+  all supported versions of Android on all supported machines: arm,
+  armv7, arm64, x86, x86_64, and mips.
+
+  See the file java/INSTALL for detailed installation instructions.
+
 
 * Obsolete platforms
 
diff --git a/etc/NEWS b/etc/NEWS
index 7b521f3e6fe..6cdeeacc158 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -24,6 +24,12 @@ applies, and please also update docstrings as needed.
 
 * Installation Changes in Emacs 30.1
 
+** Emacs has been ported to the Android operating system.
+This requires Emacs to be compiled on another computer.  The Android
+NDK, SDK, and a suitable Java compiler must also be installed.
+
+See the file 'java/INSTALL' for more details.
+
 ---
 ** Emacs now defaults to ossaudio library for sound on NetBSD and OpenBSD.
 Previously configure used ALSA libraries if installed on the
@@ -62,6 +68,12 @@ compositing manager, Emacs will now redisplay such a frame 
even though
 'frame-visible-p' returns nil or 'icon' for it.  This can happen, for
 example, as part of preview for iconified frames.
 
+---
+** New user option 'menu-bar-close-window'.
+When non-nil, selecting Close from the File menu or clicking Close in
+the tool bar will result in the current window being closed, if
+possible.
+
 +++
 ** 'write-region-inhibit-fsync' now defaults to t in interactive mode,
 as it has in batch mode since Emacs 24.
@@ -102,6 +114,11 @@ plus, minus, check-mark, start, etc.
 The 'tool-bar-position' frame parameter can be set to 'bottom' on all
 window systems other than Nextstep.
 
++++
+** New global minor mode 'modifier-bar-mode'.
+When this minor mode is enabled, buttons representing modifier keys
+are displayed along the tool bar.
+
 ---
 ** New user option 'uniquify-dirname-transform'.
 This can be used to customize how buffer names are uniquified, by
@@ -128,6 +145,13 @@ right-aligned to is controlled by the new user option
 
 * Editing Changes in Emacs 30.1
 
++++
+** Emacs now has better support for touchscreen devices.
+Many touch screen gestures are now implemented and translated into
+mouse or gesture events, and support for tapping tool bar buttons and
+opening menus has been written.  Countless packages, such as Dired and
+Custom have been adjusted to better understand touch screen input.
+
 ---
 ** On X, Emacs now supports input methods which perform "string conversion".
 This means an input method can now ask Emacs to delete text
@@ -156,6 +180,25 @@ whereas if the mouse pointer is in the left half of a 
glyph, point
 will be put in front the buffer position corresponding to that glyph.
 By default this is disabled.
 
+** Internationalization
+
+---
+*** Users in CJK locales can control width of some non-CJK characters.
+Some characters are considered by Unicode as "ambiguous" with respect
+to their display width: either "full-width" (i.e. taking 2 columns on
+display) or "narrow" (taking 1 column).  The actual width depends on
+the fonts used for these characters by Emacs or (for text-mode frames)
+by the terminal emulator.  Traditionally, font sets in CJK locales
+were set up so as to display these characters as full-width, and thus
+Emacs modified the char-width table in those locales to follow suit.
+Lately, the tendency is to display these characters as narrow.  The
+new user option 'cjk-ambiguous-chars-are-wide' allows users to control
+whether Emacs considers these characters as full-width (the default)
+or narrow (if the variable is customized to the nil value).
+
+This setting affects the results of 'string-width' and similar
+functions in CJK locales.
+
 
 * Changes in Specialized Modes and Packages in Emacs 30.1
 
@@ -398,6 +441,12 @@ this new face when DocView displays documents, customize 
this face to
 restore the colors you were used to, or to get colors more to your
 liking.
 
+---
+*** DocView buffers now display a new tool bar.
+This tool bar contains options for searching and navigating within the
+document, replacing the incompatible items for incremental search and
+editing within the default tool bar displayed in the past.
+
 ** Shortdoc
 
 +++
@@ -682,10 +731,42 @@ Instead, the Lisp reader approximates an infinity with 
the nearest
 finite value, and a NaN with some other non-numeric object that
 provokes an error if used numerically.
 
++++
+** X color support compatibility aliases are now marked obsolete.
+The compatibility aliases 'x-defined-colors', 'x-color-defined-p',
+'x-color-values', and 'x-display-color-p' are now obsolete.
+
 
 * Lisp Changes in Emacs 30.1
 
+** 'defadvice' is marked as obsolete.
+See (info "(elisp)Porting Old Advice") for help converting them
+to use `advice-add` or `define-advice instead.
+
 +++
+** New variable 'current-key-remap-sequence'.
+It is bound to the key sequence that caused a call to a function bound
+within `function-key-map' or `input-decode-map' around those calls.
+
++++
+** New variables describing the names of built in programs.
+The new variables 'ctags-program-name', 'ebrowse-program-name',
+'etags-program-name', 'hexl-program-name', 'emacsclient-program-name'
+and 'movemail-program-name' should be used instead of "ctags",
+"ebrowse", "etags", "hexl", and "emacsclient", when starting one of
+these built in programs in a subprocess.
+
++++
+** 'x-popup-menu' now understands touch screen events.
+When a 'touchscreen-begin' or 'touchscreen-end' event is passed as the
+POSITION argument, it will behave as if that event was a mouse event.
+
++++
+** New functions for handling touch screen events.
+The new functions 'touch-screen-track-tap' and
+'touch-screen-track-drag' handle tracking common touch screen gestures
+from within a command.
+
 ** New user option 'safe-local-variable-directories'.
 This user option names directories in which Emacs will treat all
 directory-local variables as safe.
@@ -698,6 +779,11 @@ with Emacs.
 
 ** New variable 'inhibit-auto-fill' to temporarily prevent auto-fill.
 
++++
+** New variable 'secondary-tool-bar-map'.
+If non-nil, this variable contains a keymap of menu items that are
+displayed along tool bar items inside 'tool-bar-map'.
+
 ** Functions and variables to transpose sexps
 
 +++
diff --git a/etc/PROBLEMS b/etc/PROBLEMS
index 9bdfe2329ba..6dc56dd9e81 100644
--- a/etc/PROBLEMS
+++ b/etc/PROBLEMS
@@ -3361,6 +3361,56 @@ Compose key to stop working.
 On X Windows, users should not use Emacs configured with PGTK, since
 this and many other problems do not exist on the regular X builds.
 
+* Runtime problems specific to Android
+
+** Text displayed in the default monospace font looks horrible.
+
+Droid Sans Mono (the default Monospace font which comes with Android)
+comes with instruction code designed for Microsoft's proprietary
+TrueType font scaler.  When this code is executed by Emacs to instruct
+a glyph containing more than one component, it tries to address
+"reference points" which are set to the values of two extra "phantom
+points" in the glyph, that are a proprietary extension of the MS font
+scaler.
+
+Emacs does not support these extensions, and as a result characters
+such as
+
+  ĥ
+
+display incorrectly, with the right most edge of the `h' component
+stretched very far out to the right, on some low density displays.
+
+The solution is to replace the MS-specific hinting code in Droid Sans
+Mono with automatically generated code from the FreeType project's
+"ttfautohint" program.  First, extract
+'/system/fonts/DroidSansMono.ttf' from your device:
+
+  $ adb pull /system/fonts/DroidSansMono.ttf
+  /system/fonts/DroidSansMono.ttf: 1 file pulled, 0 skipped.
+  23.1 MB/s (90208 bytes in 0.004s)
+
+install the "ttfautohint" program:
+
+  http://freetype.org/ttfautohint/
+
+generate a font file with new hinting instructions:
+
+  $ ttfautohint DroidSansMono.ttf > DroidSansMono.ttf.rpl
+
+and upload them to your device, either back to /system/fonts (which is
+allowed by free versions of Android, such as Replicant):
+
+  $ adb root
+  $ adb remount
+  $ adb push DroidSansMono.ttf.rpl /system/fonts/DroidSansMono.ttf
+
+or to the user fonts directory described in the "Android Fonts" node
+of the Emacs manual.  You may want to perform this procedure even if
+you are not seeing problems with character display, as the
+automatically generated instructions result in superior display
+results that are easier to read.
+
 * Build-time problems
 
 ** Configuration
diff --git a/etc/images/alt.pbm b/etc/images/alt.pbm
new file mode 100644
index 00000000000..7d12a48b552
Binary files /dev/null and b/etc/images/alt.pbm differ
diff --git a/etc/images/ctrl.pbm b/etc/images/ctrl.pbm
new file mode 100644
index 00000000000..c3ff817dc7a
Binary files /dev/null and b/etc/images/ctrl.pbm differ
diff --git a/etc/images/hyper.pbm b/etc/images/hyper.pbm
new file mode 100644
index 00000000000..fdb79c2f3a9
Binary files /dev/null and b/etc/images/hyper.pbm differ
diff --git a/etc/images/last-page.pbm b/etc/images/last-page.pbm
new file mode 100644
index 00000000000..d25ce022183
Binary files /dev/null and b/etc/images/last-page.pbm differ
diff --git a/etc/images/last-page.xpm b/etc/images/last-page.xpm
new file mode 100644
index 00000000000..5704143aa2e
--- /dev/null
+++ b/etc/images/last-page.xpm
@@ -0,0 +1,122 @@
+/* XPM */
+static char *last_page[] = {
+/* columns rows colors chars-per-pixel */
+"24 24 92 1 ",
+"  c None",
+". c black",
+"X c gray15",
+"o c #2F4050",
+"O c #344353",
+"+ c #3B4F63",
+"@ c #384F66",
+"# c #3A5067",
+"$ c #3C5064",
+"% c #3C5065",
+"& c #3E5166",
+"* c #3F5266",
+"= c #3A5168",
+"- c #3B5269",
+"; c #3D526A",
+": c #3E546A",
+"> c #3F556B",
+", c #3E5975",
+"< c #3F5A76",
+"1 c #464646",
+"2 c #494949",
+"3 c #405367",
+"4 c #405468",
+"5 c #40566C",
+"6 c #41576D",
+"7 c #42586E",
+"8 c #44586F",
+"9 c #45596F",
+"0 c #465B70",
+"q c #415B77",
+"w c #425C78",
+"e c #435E79",
+"r c #445F7A",
+"t c #46607B",
+"y c #47617B",
+"u c #47617C",
+"i c #48627D",
+"p c #49637D",
+"a c #4B647E",
+"s c #4C647F",
+"d c #4C657F",
+"f c gray38",
+"g c #6A6A6A",
+"h c #616A73",
+"j c #68727D",
+"k c #7C7C7C",
+"l c #4E6780",
+"z c #4F6881",
+"x c #506982",
+"c c #526A83",
+"v c #556D85",
+"b c #5B7289",
+"n c #7D8185",
+"m c #77838F",
+"M c #868788",
+"N c #888888",
+"B c #8B8B8B",
+"V c #8F9296",
+"C c #8F9396",
+"Z c #8F9397",
+"A c #909397",
+"S c #959595",
+"D c #91969C",
+"F c #91979C",
+"G c #92979C",
+"H c #92979D",
+"J c #9C9FA1",
+"K c #9D9FA2",
+"L c #A2A3A4",
+"P c #A6A6A6",
+"I c #ACACAC",
+"U c gray68",
+"Y c #B0B0B0",
+"T c #B2B2B2",
+"R c gray71",
+"E c #B6B6B6",
+"W c gray75",
+"Q c #C5C5C5",
+"! c gray79",
+"~ c gray80",
+"^ c LightGray",
+"/ c #D6D6D6",
+"( c #D8D8D8",
+") c #DADADA",
+"_ c #DEDEDE",
+"` c gray89",
+"' c #E5E5E5",
+"] c #E6E6E6",
+"[ c #EEEEEE",
+"{ c #F2F2F2",
+"} c #F6F6F6",
+"| c white",
+/* pixels */
+"                        ",
+"                        ",
+"   ........   ........  ",
+"  .vU/_][`!N2gNT~)]{|b. ",
+"  .@T/_][}||PUTW~)]{|t. ",
+"  .#T/_][}`|PUTW~)]{|t. ",
+"  .-T/_][).|PUTW~)]{|t. ",
+"  .-T/_]^..|PUTW~)]{|t. ",
+"  .;T/_~...|PUTW~)]{|u. ",
+"  .>T/Q.....X1fkSR]{|i. ",
+"  .>T/_~...|PUTW~)]{|i. ",
+"  .5T/_]^..|PUTW~)]{|a. ",
+"  .5T/_][).|PUTW~)]{|d. ",
+"  .7T/_][}`|PUTW~)]{|d. ",
+"  .8T/_][}||PUTW~)]{|l. ",
+"  .8R/_][}||PUTW~)]{|z. ",
+"  .0MAZZZZZKPLHHHHHKmc. ",
+"  .O43&&&&+hnjtrwwq<<c. ",
+"  ..........o.........  ",
+"           ...          ",
+"                        ",
+"                        ",
+"                        ",
+"                        "
+};
diff --git a/etc/images/meta.pbm b/etc/images/meta.pbm
new file mode 100644
index 00000000000..4d4c55c85c7
Binary files /dev/null and b/etc/images/meta.pbm differ
diff --git a/etc/images/shift.pbm b/etc/images/shift.pbm
new file mode 100644
index 00000000000..fbb1f4abe06
Binary files /dev/null and b/etc/images/shift.pbm differ
diff --git a/etc/images/super.pbm b/etc/images/super.pbm
new file mode 100644
index 00000000000..aa126755f99
Binary files /dev/null and b/etc/images/super.pbm differ
diff --git a/exec/Makefile.in b/exec/Makefile.in
new file mode 100644
index 00000000000..00e59771337
--- /dev/null
+++ b/exec/Makefile.in
@@ -0,0 +1,140 @@
+### @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/>.
+
+# Configure build directory information.
+
+     srcdir = @srcdir@
+      VPATH = @srcdir@
+   builddir = @builddir@
+
+# Set up compilation tools.
+
+        CC = @CC@
+        AS = @AS@
+        LD = @LD@
+        M4 = @M4@
+       CPP = @CPP@
+    ASFLAGS = @ASFLAGS@
+    ARFLAGS = @ARFLAGS@
+     CFLAGS = @CFLAGS@
+   CPPFLAGS = @CPPFLAGS@
+    LDFLAGS = @LDFLAGS@
+LOADERFLAGS = @LOADERFLAGS@
+FIND_DELETE = @FIND_DELETE@
+
+# Set up object files.
+
+     LOADER = @exec_loader@
+       OBJS = @OBJS@
+   LOADOBJS = $(patsubst %.s,%.o,$(LOADER))
+
+# Set up automatic dependency tracking.
+
+AUTO_DEPEND = @AUTO_DEPEND@
+DEPDIR = deps
+ifeq ($(AUTO_DEPEND),yes)
+DEPFLAGS = -MMD -MF $(DEPDIR)/$*.d -MP
+-include $(OBJS:%.o=$(DEPDIR)/%.d)
+-include $(DEPDIR)/test.d
+-include $(DEPDIR)/exec1.d
+else
+DEPFLAGS =
+include $(srcdir)/deps.mk
+endif
+
+# Set up the appropriate targets.
+
+all: libexec.a loader
+
+# Set up automatic Makefile regeneration.
+
+$(srcdir)/configure: $(srcdir)/configure.ac
+       cd $(srcdir) && autoreconf
+
+config.status: $(srcdir)/configure
+       if [ -x config.status ]; then   \
+         ./config.status --recheck;    \
+       else                            \
+         $(srcdir)/configure;          \
+       fi
+
+Makefile: config.status Makefile.in
+       MAKE="$(MAKE)" ./config.status
+
+# Set up rules to build targets.
+
+.SUFFIXES: .c .s
+.c.o:
+       $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEPFLAGS) -I. -I$(srcdir) $< -o $@
+.s.o:
+       $(M4) $< > $(notdir $<).s
+       $(AS) $(ASFLAGS) $(notdir $<).s -o $@
+
+# Set up dependencies for config-mips.m4.
+
+config-mips.m4: config-mips.m4.in
+       cd $(srcdir) && ./config.status $@
+$(LOADOBJS): config-mips.m4
+
+# Set up rules to build libexec.a.
+
+libexec.a: $(OBJS)
+       $(AR) cru $(ARFLAGS) $@ $^
+
+# And loader.
+
+loader: $(LOADOBJS)
+       $(LD) -o $@ $(LOADERFLAGS) $(LOADOBJS)
+
+# And test.
+
+test: test.o libexec.a
+       $(CC) $(LDFLAGS) $< libexec.a -o $@
+
+# And exec1.
+
+exec1: exec1.o libexec.a
+       $(CC) $(LDFLAGS) $< libexec.a -o $@
+
+# Set up targets for cleaning.
+
+.PHONY: clean distclean maintainer-clean extraclean bootstrap-clean
+clean:
+       rm -f *.o *.a loader test exec1 *.s.s
+ifeq ($(AUTO_DEPEND),yes)
+       rm -rf deps/*.d
+endif
+
+distclean: clean
+       rm -f Makefile config.status config.h config-mips.m4
+
+maintainer-clean: distclean
+
+### This doesn't actually appear in the coding standards, but Karl
+### says GCC supports it, and that's where the configuration part of
+### the coding standards seem to come from.  It's like distclean, but
+### it deletes backup and autosave files too.
+
+extraclean: maintainer-clean
+       -rm -f config-tmp-* $(srcdir)/aclocal.m4 $(srcdir)/configure \
+         $(srcdir)/src/config.in
+       -[ "$(srcdir)" = "." ] || \
+         find $(srcdir) '(' -name '*~' -o -name '#*' ')' $(FIND_DELETE)
+       -find . '(' -name '*~' -o -name '#*' ')' $(FIND_DELETE)
+bootstrap-clean: extraclean
diff --git a/exec/README b/exec/README
new file mode 100644
index 00000000000..f7eb21cfc84
--- /dev/null
+++ b/exec/README
@@ -0,0 +1,3 @@
+This directory holds the source code to a library used to replace the
+`execve' and `execveat' system calls, used by the Android port of
+Emacs to start executables without intervention from the system.
diff --git a/exec/config-mips.m4.in b/exec/config-mips.m4.in
new file mode 100644
index 00000000000..72632765bd0
--- /dev/null
+++ b/exec/config-mips.m4.in
@@ -0,0 +1,42 @@
+dnl  Assembler templates for MIPS computers.
+dnl
+dnl Copyright (C) 2023 Free Software Foundation, Inc.
+dnl
+dnl  This file is part of GNU Emacs.
+dnl
+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
+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
+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/>.
+
+define(`SYSCALL_open', `ifelse(`@MIPS_N32@',`yes',`6002',`4005')')
+define(`SYSCALL_close', `ifelse(`@MIPS_N32@',`yes',`6003',`4006')')
+define(`SYSCALL_mmap', `ifelse(`@MIPS_N32@',`yes',`6009',`4090')')
+define(`SYSCALL_nanosleep', `ifelse(`@MIPS_N32@',`yes',`6034',`4166')')
+define(`SYSCALL_exit', `ifelse(`@MIPS_N32@',`yes',`6058',`4001')')
+define(`SYSCALL_prctl', `ifelse(`@MIPS_N32@',`yes',`6153',`4192')')
+
+define(`SYSCALL', `ifelse(`@MIPS_N32@',`yes',` move    $a4, $1
+       move    $a5, $2
+       move    $a6, $3
+       move    $a7, $4',`      addi    $sp, -32
+       sw      $1, 16($sp)
+       sw      $2, 20($sp)
+       sw      $3, 24($sp)
+       sw      $4, 28($sp)')')
+
+define(`RESTORE', `ifelse(`@MIPS_N32@',`yes',` nop',`  addi    $sp, 32')')
+
+dnl For mips64.  Some assemblers don't want to assemble `daddi'.
+define(`DADDI2', `ifelse(`@DADDI_BROKEN@',`yes',`      li      $at, $2
+dadd            $1, $1, $at',` daddi   $1, $2')')
+define(`DADDI3', `ifelse(`@DADDI_BROKEN@',`yes',`      li      $at, $3
+dadd            $1, $2, $at',` daddi   $1, $2, $3')')
diff --git a/exec/config.guess b/exec/config.guess
new file mode 100755
index 00000000000..c7f17e8fb97
--- /dev/null
+++ b/exec/config.guess
@@ -0,0 +1,1768 @@
+#!/usr/bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright 1992-2022 Free Software Foundation, Inc.
+
+# shellcheck disable=SC2006,SC2268 # see below for rationale
+
+timestamp='2022-05-25'
+
+# This file 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.
+#
+# This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
+#
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
+#
+# Please send patches to <config-patches@gnu.org>.
+
+
+# The "shellcheck disable" line above the timestamp inhibits complaints
+# about features and limitations of the classic Bourne shell that were
+# superseded or lifted in POSIX.  However, this script identifies a wide
+# variety of pre-POSIX systems that do not have POSIX shells at all, and
+# even some reasonably current systems (Solaris 10 as case-in-point) still
+# have a pre-POSIX /bin/sh.
+
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Options:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright 1992-2022 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+# Just in case it came from the environment.
+GUESS=
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+tmp=
+# shellcheck disable=SC2172
+trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15
+
+set_cc_for_build() {
+    # prevent multiple calls if $tmp is already set
+    test "$tmp" && return 0
+    : "${TMPDIR=/tmp}"
+    # shellcheck disable=SC2039,SC3028
+    { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n 
"$tmp" && test -d "$tmp" ; } ||
+       { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir 
"$tmp" 2>/dev/null) ; } ||
+       { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo 
"Warning: creating insecure temp directory" >&2 ; } ||
+       { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 
1 ; }
+    dummy=$tmp/dummy
+    case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
+       ,,)    echo "int x;" > "$dummy.c"
+              for driver in cc gcc c89 c99 ; do
+                  if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; 
then
+                      CC_FOR_BUILD=$driver
+                      break
+                  fi
+              done
+              if test x"$CC_FOR_BUILD" = x ; then
+                  CC_FOR_BUILD=no_compiler_found
+              fi
+              ;;
+       ,,*)   CC_FOR_BUILD=$CC ;;
+       ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+    esac
+}
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if test -f /.attbin/uname ; then
+       PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+case $UNAME_SYSTEM in
+Linux|GNU|GNU/*)
+       LIBC=unknown
+
+       set_cc_for_build
+       cat <<-EOF > "$dummy.c"
+       #include <features.h>
+       #if defined(__UCLIBC__)
+       LIBC=uclibc
+       #elif defined(__dietlibc__)
+       LIBC=dietlibc
+       #elif defined(__GLIBC__)
+       LIBC=gnu
+       #else
+       #include <stdarg.h>
+       /* First heuristic to detect musl libc.  */
+       #ifdef __DEFINED_va_list
+       LIBC=musl
+       #endif
+       #endif
+       EOF
+       cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | 
sed 's, ,,g'`
+       eval "$cc_set_libc"
+
+       # Second heuristic to detect musl libc.
+       if [ "$LIBC" = unknown ] &&
+          command -v ldd >/dev/null &&
+          ldd --version 2>&1 | grep -q ^musl; then
+               LIBC=musl
+       fi
+
+       # If the system lacks a compiler, then just pick glibc.
+       # We could probably try harder.
+       if [ "$LIBC" = unknown ]; then
+               LIBC=gnu
+       fi
+       ;;
+esac
+
+# Note: order is significant - the case branches are not exclusive.
+
+case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in
+    *:NetBSD:*:*)
+       # NetBSD (nbsd) targets should (where applicable) match one or
+       # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+       # *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+       # switched to ELF, *-*-netbsd* would select the old
+       # object file format.  This provides both forward
+       # compatibility and a consistent mechanism for selecting the
+       # object file format.
+       #
+       # Note: NetBSD doesn't particularly care about the vendor
+       # portion of the name.  We always set it to "unknown".
+       UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+           /sbin/sysctl -n hw.machine_arch 2>/dev/null || \
+           /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \
+           echo unknown)`
+       case $UNAME_MACHINE_ARCH in
+           aarch64eb) machine=aarch64_be-unknown ;;
+           armeb) machine=armeb-unknown ;;
+           arm*) machine=arm-unknown ;;
+           sh3el) machine=shl-unknown ;;
+           sh3eb) machine=sh-unknown ;;
+           sh5el) machine=sh5le-unknown ;;
+           earmv*)
+               arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 
's,^e\(armv[0-9]\).*$,\1,'`
+               endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 
's,^.*\(eb\)$,\1,p'`
+               machine=${arch}${endian}-unknown
+               ;;
+           *) machine=$UNAME_MACHINE_ARCH-unknown ;;
+       esac
+       # The Operating System including object format, if it has switched
+       # to ELF recently (or will in the future) and ABI.
+       case $UNAME_MACHINE_ARCH in
+           earm*)
+               os=netbsdelf
+               ;;
+           arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+               set_cc_for_build
+               if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+                       | grep -q __ELF__
+               then
+                   # Once all utilities can be ECOFF (netbsdecoff) or a.out 
(netbsdaout).
+                   # Return netbsd for either.  FIX?
+                   os=netbsd
+               else
+                   os=netbsdelf
+               fi
+               ;;
+           *)
+               os=netbsd
+               ;;
+       esac
+       # Determine ABI tags.
+       case $UNAME_MACHINE_ARCH in
+           earm*)
+               expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+               abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
+               ;;
+       esac
+       # The OS release
+       # Debian GNU/NetBSD machines have a different userland, and
+       # thus, need a distinct triplet. However, they do not need
+       # kernel version information, so it can be replaced with a
+       # suitable tag, in the style of linux-gnu.
+       case $UNAME_VERSION in
+           Debian*)
+               release='-gnu'
+               ;;
+           *)
+               release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. 
-f1,2`
+               ;;
+       esac
+       # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+       # contains redundant information, the shorter form:
+       # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+       GUESS=$machine-${os}${release}${abi-}
+       ;;
+    *:Bitrig:*:*)
+       UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+       GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE
+       ;;
+    *:OpenBSD:*:*)
+       UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+       GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE
+       ;;
+    *:SecBSD:*:*)
+       UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'`
+       GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE
+       ;;
+    *:LibertyBSD:*:*)
+       UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
+       GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE
+       ;;
+    *:MidnightBSD:*:*)
+       GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE
+       ;;
+    *:ekkoBSD:*:*)
+       GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE
+       ;;
+    *:SolidBSD:*:*)
+       GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE
+       ;;
+    *:OS108:*:*)
+       GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE
+       ;;
+    macppc:MirBSD:*:*)
+       GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE
+       ;;
+    *:MirBSD:*:*)
+       GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE
+       ;;
+    *:Sortix:*:*)
+       GUESS=$UNAME_MACHINE-unknown-sortix
+       ;;
+    *:Twizzler:*:*)
+       GUESS=$UNAME_MACHINE-unknown-twizzler
+       ;;
+    *:Redox:*:*)
+       GUESS=$UNAME_MACHINE-unknown-redox
+       ;;
+    mips:OSF1:*.*)
+       GUESS=mips-dec-osf1
+       ;;
+    alpha:OSF1:*:*)
+       # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+       trap '' 0
+       case $UNAME_RELEASE in
+       *4.0)
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+               ;;
+       *5.*)
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+               ;;
+       esac
+       # According to Compaq, /usr/sbin/psrinfo has been available on
+       # OSF/1 and Tru64 systems produced since 1995.  I hope that
+       # covers most systems running today.  This code pipes the CPU
+       # types through head -n 1, so we only detect the type of CPU 0.
+       ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) 
processor.*$/\1/p' | head -n 1`
+       case $ALPHA_CPU_TYPE in
+           "EV4 (21064)")
+               UNAME_MACHINE=alpha ;;
+           "EV4.5 (21064)")
+               UNAME_MACHINE=alpha ;;
+           "LCA4 (21066/21068)")
+               UNAME_MACHINE=alpha ;;
+           "EV5 (21164)")
+               UNAME_MACHINE=alphaev5 ;;
+           "EV5.6 (21164A)")
+               UNAME_MACHINE=alphaev56 ;;
+           "EV5.6 (21164PC)")
+               UNAME_MACHINE=alphapca56 ;;
+           "EV5.7 (21164PC)")
+               UNAME_MACHINE=alphapca57 ;;
+           "EV6 (21264)")
+               UNAME_MACHINE=alphaev6 ;;
+           "EV6.7 (21264A)")
+               UNAME_MACHINE=alphaev67 ;;
+           "EV6.8CB (21264C)")
+               UNAME_MACHINE=alphaev68 ;;
+           "EV6.8AL (21264B)")
+               UNAME_MACHINE=alphaev68 ;;
+           "EV6.8CX (21264D)")
+               UNAME_MACHINE=alphaev68 ;;
+           "EV6.9A (21264/EV69A)")
+               UNAME_MACHINE=alphaev69 ;;
+           "EV7 (21364)")
+               UNAME_MACHINE=alphaev7 ;;
+           "EV7.9 (21364A)")
+               UNAME_MACHINE=alphaev79 ;;
+       esac
+       # A Pn.n version is a patched version.
+       # A Vn.n version is a released version.
+       # A Tn.n version is a released field test version.
+       # A Xn.n version is an unreleased experimental baselevel.
+       # 1.2 uses "1.2" for uname -r.
+       OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr 
ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+       GUESS=$UNAME_MACHINE-dec-osf$OSF_REL
+       ;;
+    Amiga*:UNIX_System_V:4.0:*)
+       GUESS=m68k-unknown-sysv4
+       ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+       GUESS=$UNAME_MACHINE-unknown-amigaos
+       ;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+       GUESS=$UNAME_MACHINE-unknown-morphos
+       ;;
+    *:OS/390:*:*)
+       GUESS=i370-ibm-openedition
+       ;;
+    *:z/VM:*:*)
+       GUESS=s390-ibm-zvmoe
+       ;;
+    *:OS400:*:*)
+       GUESS=powerpc-ibm-os400
+       ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+       GUESS=arm-acorn-riscix$UNAME_RELEASE
+       ;;
+    arm*:riscos:*:*|arm*:RISCOS:*:*)
+       GUESS=arm-unknown-riscos
+       ;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+       GUESS=hppa1.1-hitachi-hiuxmpp
+       ;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+       # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+       case `(/bin/universe) 2>/dev/null` in
+           att) GUESS=pyramid-pyramid-sysv3 ;;
+           *)   GUESS=pyramid-pyramid-bsd   ;;
+       esac
+       ;;
+    NILE*:*:*:dcosx)
+       GUESS=pyramid-pyramid-svr4
+       ;;
+    DRS?6000:unix:4.0:6*)
+       GUESS=sparc-icl-nx6
+       ;;
+    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+       case `/usr/bin/uname -p` in
+           sparc) GUESS=sparc-icl-nx7 ;;
+       esac
+       ;;
+    s390x:SunOS:*:*)
+       SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+       GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL
+       ;;
+    sun4H:SunOS:5.*:*)
+       SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+       GUESS=sparc-hal-solaris2$SUN_REL
+       ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+       SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+       GUESS=sparc-sun-solaris2$SUN_REL
+       ;;
+    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+       GUESS=i386-pc-auroraux$UNAME_RELEASE
+       ;;
+    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+       set_cc_for_build
+       SUN_ARCH=i386
+       # If there is a compiler, see if it is configured for 64-bit objects.
+       # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+       # This test works for both compilers.
+       if test "$CC_FOR_BUILD" != no_compiler_found; then
+           if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+               (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \
+               grep IS_64BIT_ARCH >/dev/null
+           then
+               SUN_ARCH=x86_64
+           fi
+       fi
+       SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+       GUESS=$SUN_ARCH-pc-solaris2$SUN_REL
+       ;;
+    sun4*:SunOS:6*:*)
+       # According to config.sub, this is the proper way to canonicalize
+       # SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+       # it's likely to be more like Solaris than SunOS4.
+       SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+       GUESS=sparc-sun-solaris3$SUN_REL
+       ;;
+    sun4*:SunOS:*:*)
+       case `/usr/bin/arch -k` in
+           Series*|S4*)
+               UNAME_RELEASE=`uname -v`
+               ;;
+       esac
+       # Japanese Language versions have a version number like `4.1.3-JL'.
+       SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'`
+       GUESS=sparc-sun-sunos$SUN_REL
+       ;;
+    sun3*:SunOS:*:*)
+       GUESS=m68k-sun-sunos$UNAME_RELEASE
+       ;;
+    sun*:*:4.2BSD:*)
+       UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 
2>/dev/null`
+       test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
+       case `/bin/arch` in
+           sun3)
+               GUESS=m68k-sun-sunos$UNAME_RELEASE
+               ;;
+           sun4)
+               GUESS=sparc-sun-sunos$UNAME_RELEASE
+               ;;
+       esac
+       ;;
+    aushp:SunOS:*:*)
+       GUESS=sparc-auspex-sunos$UNAME_RELEASE
+       ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+       GUESS=m68k-atari-mint$UNAME_RELEASE
+       ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+       GUESS=m68k-atari-mint$UNAME_RELEASE
+       ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+       GUESS=m68k-atari-mint$UNAME_RELEASE
+       ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+       GUESS=m68k-milan-mint$UNAME_RELEASE
+       ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+       GUESS=m68k-hades-mint$UNAME_RELEASE
+       ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+       GUESS=m68k-unknown-mint$UNAME_RELEASE
+       ;;
+    m68k:machten:*:*)
+       GUESS=m68k-apple-machten$UNAME_RELEASE
+       ;;
+    powerpc:machten:*:*)
+       GUESS=powerpc-apple-machten$UNAME_RELEASE
+       ;;
+    RISC*:Mach:*:*)
+       GUESS=mips-dec-mach_bsd4.3
+       ;;
+    RISC*:ULTRIX:*:*)
+       GUESS=mips-dec-ultrix$UNAME_RELEASE
+       ;;
+    VAX*:ULTRIX*:*:*)
+       GUESS=vax-dec-ultrix$UNAME_RELEASE
+       ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+       GUESS=clipper-intergraph-clix$UNAME_RELEASE
+       ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+       set_cc_for_build
+       sed 's/^        //' << EOF > "$dummy.c"
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+       int main (int argc, char *argv[]) {
+#else
+       int main (argc, argv) int argc; char *argv[]; {
+#endif
+       #if defined (host_mips) && defined (MIPSEB)
+       #if defined (SYSTYPE_SYSV)
+         printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_SVR4)
+         printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+         printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
+       #endif
+       #endif
+         exit (-1);
+       }
+EOF
+       $CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
+         dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+         SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
+           { echo "$SYSTEM_NAME"; exit; }
+       GUESS=mips-mips-riscos$UNAME_RELEASE
+       ;;
+    Motorola:PowerMAX_OS:*:*)
+       GUESS=powerpc-motorola-powermax
+       ;;
+    Motorola:*:4.3:PL8-*)
+       GUESS=powerpc-harris-powermax
+       ;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+       GUESS=powerpc-harris-powermax
+       ;;
+    Night_Hawk:Power_UNIX:*:*)
+       GUESS=powerpc-harris-powerunix
+       ;;
+    m88k:CX/UX:7*:*)
+       GUESS=m88k-harris-cxux7
+       ;;
+    m88k:*:4*:R4*)
+       GUESS=m88k-motorola-sysv4
+       ;;
+    m88k:*:3*:R3*)
+       GUESS=m88k-motorola-sysv3
+       ;;
+    AViiON:dgux:*:*)
+       # DG/UX returns AViiON for all architectures
+       UNAME_PROCESSOR=`/usr/bin/uname -p`
+       if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = 
mc88110
+       then
+           if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \
+              test "$TARGET_BINARY_INTERFACE"x = x
+           then
+               GUESS=m88k-dg-dgux$UNAME_RELEASE
+           else
+               GUESS=m88k-dg-dguxbcs$UNAME_RELEASE
+           fi
+       else
+           GUESS=i586-dg-dgux$UNAME_RELEASE
+       fi
+       ;;
+    M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
+       GUESS=m88k-dolphin-sysv3
+       ;;
+    M88*:*:R3*:*)
+       # Delta 88k system running SVR3
+       GUESS=m88k-motorola-sysv3
+       ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+       GUESS=m88k-tektronix-sysv3
+       ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+       GUESS=m68k-tektronix-bsd
+       ;;
+    *:IRIX*:*:*)
+       IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'`
+       GUESS=mips-sgi-irix$IRIX_REL
+       ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+       GUESS=romp-ibm-aix    # uname -m gives an 8 hex-code CPU id
+       ;;                    # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+       GUESS=i386-ibm-aix
+       ;;
+    ia64:AIX:*:*)
+       if test -x /usr/bin/oslevel ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
+       fi
+       GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV
+       ;;
+    *:AIX:2:3)
+       if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+               set_cc_for_build
+               sed 's/^                //' << EOF > "$dummy.c"
+               #include <sys/systemcfg.h>
+
+               main()
+                       {
+                       if (!__power_pc())
+                               exit(1);
+                       puts("powerpc-ibm-aix3.2.5");
+                       exit(0);
+                       }
+EOF
+               if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && 
SYSTEM_NAME=`"$dummy"`
+               then
+                       GUESS=$SYSTEM_NAME
+               else
+                       GUESS=rs6000-ibm-aix3.2.5
+               fi
+       elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+               GUESS=rs6000-ibm-aix3.2.4
+       else
+               GUESS=rs6000-ibm-aix3.2
+       fi
+       ;;
+    *:AIX:*:[4567])
+       IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk 
'{ print $1 }'`
+       if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; 
then
+               IBM_ARCH=rs6000
+       else
+               IBM_ARCH=powerpc
+       fi
+       if test -x /usr/bin/lslpp ; then
+               IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \
+                          awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
+       else
+               IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
+       fi
+       GUESS=$IBM_ARCH-ibm-aix$IBM_REV
+       ;;
+    *:AIX:*:*)
+       GUESS=rs6000-ibm-aix
+       ;;
+    ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
+       GUESS=romp-ibm-bsd4.4
+       ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+       GUESS=romp-ibm-bsd$UNAME_RELEASE    # 4.3 with uname added to
+       ;;                                  # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+       GUESS=rs6000-bull-bosx
+       ;;
+    DPX/2?00:B.O.S.:*:*)
+       GUESS=m68k-bull-sysv3
+       ;;
+    9000/[34]??:4.3bsd:1.*:*)
+       GUESS=m68k-hp-bsd
+       ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+       GUESS=m68k-hp-bsd4.4
+       ;;
+    9000/[34678]??:HP-UX:*:*)
+       HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
+       case $UNAME_MACHINE in
+           9000/31?)            HP_ARCH=m68000 ;;
+           9000/[34]??)         HP_ARCH=m68k ;;
+           9000/[678][0-9][0-9])
+               if test -x /usr/bin/getconf; then
+                   sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+                   sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+                   case $sc_cpu_version in
+                     523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
+                     528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
+                     532)                      # CPU_PA_RISC2_0
+                       case $sc_kernel_bits in
+                         32) HP_ARCH=hppa2.0n ;;
+                         64) HP_ARCH=hppa2.0w ;;
+                         '') HP_ARCH=hppa2.0 ;;   # HP-UX 10.20
+                       esac ;;
+                   esac
+               fi
+               if test "$HP_ARCH" = ""; then
+                   set_cc_for_build
+                   sed 's/^            //' << EOF > "$dummy.c"
+
+               #define _HPUX_SOURCE
+               #include <stdlib.h>
+               #include <unistd.h>
+
+               int main ()
+               {
+               #if defined(_SC_KERNEL_BITS)
+                   long bits = sysconf(_SC_KERNEL_BITS);
+               #endif
+                   long cpu  = sysconf (_SC_CPU_VERSION);
+
+                   switch (cpu)
+                       {
+                       case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+                       case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+                       case CPU_PA_RISC2_0:
+               #if defined(_SC_KERNEL_BITS)
+                           switch (bits)
+                               {
+                               case 64: puts ("hppa2.0w"); break;
+                               case 32: puts ("hppa2.0n"); break;
+                               default: puts ("hppa2.0"); break;
+                               } break;
+               #else  /* !defined(_SC_KERNEL_BITS) */
+                           puts ("hppa2.0"); break;
+               #endif
+                       default: puts ("hppa1.0"); break;
+                       }
+                   exit (0);
+               }
+EOF
+                   (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 
2>/dev/null) && HP_ARCH=`"$dummy"`
+                   test -z "$HP_ARCH" && HP_ARCH=hppa
+               fi ;;
+       esac
+       if test "$HP_ARCH" = hppa2.0w
+       then
+           set_cc_for_build
+
+           # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+           # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
+           # generating 64-bit code.  GNU and HP use different nomenclature:
+           #
+           # $ CC_FOR_BUILD=cc ./config.guess
+           # => hppa2.0w-hp-hpux11.23
+           # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+           # => hppa64-hp-hpux11.23
+
+           if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
+               grep -q __LP64__
+           then
+               HP_ARCH=hppa2.0w
+           else
+               HP_ARCH=hppa64
+           fi
+       fi
+       GUESS=$HP_ARCH-hp-hpux$HPUX_REV
+       ;;
+    ia64:HP-UX:*:*)
+       HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
+       GUESS=ia64-hp-hpux$HPUX_REV
+       ;;
+    3050*:HI-UX:*:*)
+       set_cc_for_build
+       sed 's/^        //' << EOF > "$dummy.c"
+       #include <unistd.h>
+       int
+       main ()
+       {
+         long cpu = sysconf (_SC_CPU_VERSION);
+         /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+            true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+            results, however.  */
+         if (CPU_IS_PA_RISC (cpu))
+           {
+             switch (cpu)
+               {
+                 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+                 default: puts ("hppa-hitachi-hiuxwe2"); break;
+               }
+           }
+         else if (CPU_IS_HP_MC68K (cpu))
+           puts ("m68k-hitachi-hiuxwe2");
+         else puts ("unknown-hitachi-hiuxwe2");
+         exit (0);
+       }
+EOF
+       $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
+               { echo "$SYSTEM_NAME"; exit; }
+       GUESS=unknown-hitachi-hiuxwe2
+       ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
+       GUESS=hppa1.1-hp-bsd
+       ;;
+    9000/8??:4.3bsd:*:*)
+       GUESS=hppa1.0-hp-bsd
+       ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+       GUESS=hppa1.0-hp-mpeix
+       ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
+       GUESS=hppa1.1-hp-osf
+       ;;
+    hp8??:OSF1:*:*)
+       GUESS=hppa1.0-hp-osf
+       ;;
+    i*86:OSF1:*:*)
+       if test -x /usr/sbin/sysversion ; then
+           GUESS=$UNAME_MACHINE-unknown-osf1mk
+       else
+           GUESS=$UNAME_MACHINE-unknown-osf1
+       fi
+       ;;
+    parisc*:Lites*:*:*)
+       GUESS=hppa1.1-hp-lites
+       ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+       GUESS=c1-convex-bsd
+       ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+       exit ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+       GUESS=c34-convex-bsd
+       ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+       GUESS=c38-convex-bsd
+       ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+       GUESS=c4-convex-bsd
+       ;;
+    CRAY*Y-MP:*:*:*)
+       CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+       GUESS=ymp-cray-unicos$CRAY_REL
+       ;;
+    CRAY*[A-Z]90:*:*:*)
+       echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
+       | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+             -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+             -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*TS:*:*:*)
+       CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+       GUESS=t90-cray-unicos$CRAY_REL
+       ;;
+    CRAY*T3E:*:*:*)
+       CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+       GUESS=alphaev5-cray-unicosmk$CRAY_REL
+       ;;
+    CRAY*SV1:*:*:*)
+       CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+       GUESS=sv1-cray-unicos$CRAY_REL
+       ;;
+    *:UNICOS/mp:*:*)
+       CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+       GUESS=craynv-cray-unicosmp$CRAY_REL
+       ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+       FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ 
abcdefghijklmnopqrstuvwxyz`
+       FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ 
abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+       FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
+       GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
+       ;;
+    5000:UNIX_System_V:4.*:*)
+       FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ 
abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+       FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ 
abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
+       GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
+       ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+       GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE
+       ;;
+    sparc*:BSD/OS:*:*)
+       GUESS=sparc-unknown-bsdi$UNAME_RELEASE
+       ;;
+    *:BSD/OS:*:*)
+       GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE
+       ;;
+    arm:FreeBSD:*:*)
+       UNAME_PROCESSOR=`uname -p`
+       set_cc_for_build
+       if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+           | grep -q __ARM_PCS_VFP
+       then
+           FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+           GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi
+       else
+           FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+           GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf
+       fi
+       ;;
+    *:FreeBSD:*:*)
+       UNAME_PROCESSOR=`/usr/bin/uname -p`
+       case $UNAME_PROCESSOR in
+           amd64)
+               UNAME_PROCESSOR=x86_64 ;;
+           i386)
+               UNAME_PROCESSOR=i586 ;;
+       esac
+       FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+       GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL
+       ;;
+    i*:CYGWIN*:*)
+       GUESS=$UNAME_MACHINE-pc-cygwin
+       ;;
+    *:MINGW64*:*)
+       GUESS=$UNAME_MACHINE-pc-mingw64
+       ;;
+    *:MINGW*:*)
+       GUESS=$UNAME_MACHINE-pc-mingw32
+       ;;
+    *:MSYS*:*)
+       GUESS=$UNAME_MACHINE-pc-msys
+       ;;
+    i*:PW*:*)
+       GUESS=$UNAME_MACHINE-pc-pw32
+       ;;
+    *:SerenityOS:*:*)
+        GUESS=$UNAME_MACHINE-pc-serenity
+        ;;
+    *:Interix*:*)
+       case $UNAME_MACHINE in
+           x86)
+               GUESS=i586-pc-interix$UNAME_RELEASE
+               ;;
+           authenticamd | genuineintel | EM64T)
+               GUESS=x86_64-unknown-interix$UNAME_RELEASE
+               ;;
+           IA64)
+               GUESS=ia64-unknown-interix$UNAME_RELEASE
+               ;;
+       esac ;;
+    i*:UWIN*:*)
+       GUESS=$UNAME_MACHINE-pc-uwin
+       ;;
+    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+       GUESS=x86_64-pc-cygwin
+       ;;
+    prep*:SunOS:5.*:*)
+       SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+       GUESS=powerpcle-unknown-solaris2$SUN_REL
+       ;;
+    *:GNU:*:*)
+       # the GNU system
+       GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'`
+       GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'`
+       GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL
+       ;;
+    *:GNU/*:*:*)
+       # other systems with GNU libc and userland
+       GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" 
"[:lower:]"`
+       GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+       GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC
+       ;;
+    *:Minix:*:*)
+       GUESS=$UNAME_MACHINE-unknown-minix
+       ;;
+    aarch64:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    aarch64_be:Linux:*:*)
+       UNAME_MACHINE=aarch64_be
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    alpha:Linux:*:*)
+       case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 
2>/dev/null` in
+         EV5)   UNAME_MACHINE=alphaev5 ;;
+         EV56)  UNAME_MACHINE=alphaev56 ;;
+         PCA56) UNAME_MACHINE=alphapca56 ;;
+         PCA57) UNAME_MACHINE=alphapca56 ;;
+         EV6)   UNAME_MACHINE=alphaev6 ;;
+         EV67)  UNAME_MACHINE=alphaev67 ;;
+         EV68*) UNAME_MACHINE=alphaev68 ;;
+       esac
+       objdump --private-headers /bin/sh | grep -q ld.so.1
+       if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    arm*:Linux:*:*)
+       set_cc_for_build
+       if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+           | grep -q __ARM_EABI__
+       then
+           GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       else
+           if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+               | grep -q __ARM_PCS_VFP
+           then
+               GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi
+           else
+               GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf
+           fi
+       fi
+       ;;
+    avr32*:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    cris:Linux:*:*)
+       GUESS=$UNAME_MACHINE-axis-linux-$LIBC
+       ;;
+    crisv32:Linux:*:*)
+       GUESS=$UNAME_MACHINE-axis-linux-$LIBC
+       ;;
+    e2k:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    frv:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    hexagon:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    i*86:Linux:*:*)
+       GUESS=$UNAME_MACHINE-pc-linux-$LIBC
+       ;;
+    ia64:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    k1om:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    m32r*:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    m68*:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    mips:Linux:*:* | mips64:Linux:*:*)
+       set_cc_for_build
+       IS_GLIBC=0
+       test x"${LIBC}" = xgnu && IS_GLIBC=1
+       sed 's/^        //' << EOF > "$dummy.c"
+       #undef CPU
+       #undef mips
+       #undef mipsel
+       #undef mips64
+       #undef mips64el
+       #if ${IS_GLIBC} && defined(_ABI64)
+       LIBCABI=gnuabi64
+       #else
+       #if ${IS_GLIBC} && defined(_ABIN32)
+       LIBCABI=gnuabin32
+       #else
+       LIBCABI=${LIBC}
+       #endif
+       #endif
+
+       #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && 
__mips_isa_rev>=6
+       CPU=mipsisa64r6
+       #else
+       #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && 
__mips_isa_rev>=6
+       CPU=mipsisa32r6
+       #else
+       #if defined(__mips64)
+       CPU=mips64
+       #else
+       CPU=mips
+       #endif
+       #endif
+       #endif
+
+       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || 
defined(MIPSEL)
+       MIPS_ENDIAN=el
+       #else
+       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || 
defined(MIPSEB)
+       MIPS_ENDIAN=
+       #else
+       MIPS_ENDIAN=
+       #endif
+       #endif
+EOF
+       cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep 
'^CPU\|^MIPS_ENDIAN\|^LIBCABI'`
+       eval "$cc_set_vars"
+       test "x$CPU" != x && { echo 
"$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; }
+       ;;
+    mips64el:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    openrisc*:Linux:*:*)
+       GUESS=or1k-unknown-linux-$LIBC
+       ;;
+    or32:Linux:*:* | or1k*:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    padre:Linux:*:*)
+       GUESS=sparc-unknown-linux-$LIBC
+       ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+       GUESS=hppa64-unknown-linux-$LIBC
+       ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+       # Look for CPU level
+       case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+         PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;;
+         PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;;
+         *)    GUESS=hppa-unknown-linux-$LIBC ;;
+       esac
+       ;;
+    ppc64:Linux:*:*)
+       GUESS=powerpc64-unknown-linux-$LIBC
+       ;;
+    ppc:Linux:*:*)
+       GUESS=powerpc-unknown-linux-$LIBC
+       ;;
+    ppc64le:Linux:*:*)
+       GUESS=powerpc64le-unknown-linux-$LIBC
+       ;;
+    ppcle:Linux:*:*)
+       GUESS=powerpcle-unknown-linux-$LIBC
+       ;;
+    riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | 
riscv64be:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+       GUESS=$UNAME_MACHINE-ibm-linux-$LIBC
+       ;;
+    sh64*:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    sh*:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    tile*:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    vax:Linux:*:*)
+       GUESS=$UNAME_MACHINE-dec-linux-$LIBC
+       ;;
+    x86_64:Linux:*:*)
+       set_cc_for_build
+       CPU=$UNAME_MACHINE
+       LIBCABI=$LIBC
+       if test "$CC_FOR_BUILD" != no_compiler_found; then
+           ABI=64
+           sed 's/^        //' << EOF > "$dummy.c"
+           #ifdef __i386__
+           ABI=x86
+           #else
+           #ifdef __ILP32__
+           ABI=x32
+           #endif
+           #endif
+EOF
+           cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | 
sed 's, ,,g'`
+           eval "$cc_set_abi"
+           case $ABI in
+               x86) CPU=i686 ;;
+               x32) LIBCABI=${LIBC}x32 ;;
+           esac
+       fi
+       GUESS=$CPU-pc-linux-$LIBCABI
+       ;;
+    xtensa*:Linux:*:*)
+       GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+       ;;
+    i*86:DYNIX/ptx:4*:*)
+       # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+       # earlier versions are messed up and put the nodename in both
+       # sysname and nodename.
+       GUESS=i386-sequent-sysv4
+       ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+       # Unixware is an offshoot of SVR4, but it has its own version
+       # number series starting with 2...
+       # I am not positive that other SVR4 systems won't match this,
+       # I just have to hope.  -- rms.
+       # Use sysv4.2uw... so that sysv4* matches it.
+       GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION
+       ;;
+    i*86:OS/2:*:*)
+       # If we were able to find `uname', then EMX Unix compatibility
+       # is probably installed.
+       GUESS=$UNAME_MACHINE-pc-os2-emx
+       ;;
+    i*86:XTS-300:*:STOP)
+       GUESS=$UNAME_MACHINE-unknown-stop
+       ;;
+    i*86:atheos:*:*)
+       GUESS=$UNAME_MACHINE-unknown-atheos
+       ;;
+    i*86:syllable:*:*)
+       GUESS=$UNAME_MACHINE-pc-syllable
+       ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+       GUESS=i386-unknown-lynxos$UNAME_RELEASE
+       ;;
+    i*86:*DOS:*:*)
+       GUESS=$UNAME_MACHINE-pc-msdosdjgpp
+       ;;
+    i*86:*:4.*:*)
+       UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
+       if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+               GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL
+       else
+               GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL
+       fi
+       ;;
+    i*86:*:5:[678]*)
+       # UnixWare 7.x, OpenUNIX and OpenServer 6.
+       case `/bin/uname -X | grep "^Machine"` in
+           *486*)           UNAME_MACHINE=i486 ;;
+           *Pentium)        UNAME_MACHINE=i586 ;;
+           *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+       esac
+       
GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+       ;;
+    i*86:*:3.2:*)
+       if test -f /usr/options/cb.name; then
+               UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+               GUESS=$UNAME_MACHINE-pc-isc$UNAME_REL
+       elif /bin/uname -X 2>/dev/null >/dev/null ; then
+               UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+               (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+               (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+                       && UNAME_MACHINE=i586
+               (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL
+       else
+               GUESS=$UNAME_MACHINE-pc-sysv32
+       fi
+       ;;
+    pc:*:*:*)
+       # Left here for compatibility:
+       # uname -m prints for DJGPP always 'pc', but it prints nothing about
+       # the processor, so we play safe by assuming i586.
+       # Note: whatever this is, it MUST be the same as what config.sub
+       # prints for the "djgpp" host, or else GDB configure will decide that
+       # this is a cross-build.
+       GUESS=i586-pc-msdosdjgpp
+       ;;
+    Intel:Mach:3*:*)
+       GUESS=i386-pc-mach3
+       ;;
+    paragon:*:*:*)
+       GUESS=i860-intel-osf1
+       ;;
+    i860:*:4.*:*) # i860-SVR4
+       if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+         GUESS=i860-stardent-sysv$UNAME_RELEASE    # Stardent Vistra i860-SVR4
+       else # Add other i860-SVR4 vendors below as they are discovered.
+         GUESS=i860-unknown-sysv$UNAME_RELEASE     # Unknown i860-SVR4
+       fi
+       ;;
+    mini*:CTIX:SYS*5:*)
+       # "miniframe"
+       GUESS=m68010-convergent-sysv
+       ;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+       GUESS=m68k-convergent-sysv
+       ;;
+    M680?0:D-NIX:5.3:*)
+       GUESS=m68k-diab-dnix
+       ;;
+    M68*:*:R3V[5678]*:*)
+       test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 
3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | 
SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+       OS_REL=''
+       test -r /etc/.relid \
+       && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+         && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+         && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+         && { echo i486-ncr-sysv4; exit; } ;;
+    NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+       OS_REL='.3'
+       test -r /etc/.relid \
+           && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < 
/etc/.relid`
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+           && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+           && { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
+       /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+           && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+       GUESS=m68k-unknown-lynxos$UNAME_RELEASE
+       ;;
+    mc68030:UNIX_System_V:4.*:*)
+       GUESS=m68k-atari-sysv4
+       ;;
+    TSUNAMI:LynxOS:2.*:*)
+       GUESS=sparc-unknown-lynxos$UNAME_RELEASE
+       ;;
+    rs6000:LynxOS:2.*:*)
+       GUESS=rs6000-unknown-lynxos$UNAME_RELEASE
+       ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+       GUESS=powerpc-unknown-lynxos$UNAME_RELEASE
+       ;;
+    SM[BE]S:UNIX_SV:*:*)
+       GUESS=mips-dde-sysv$UNAME_RELEASE
+       ;;
+    RM*:ReliantUNIX-*:*:*)
+       GUESS=mips-sni-sysv4
+       ;;
+    RM*:SINIX-*:*:*)
+       GUESS=mips-sni-sysv4
+       ;;
+    *:SINIX-*:*:*)
+       if uname -p 2>/dev/null >/dev/null ; then
+               UNAME_MACHINE=`(uname -p) 2>/dev/null`
+               GUESS=$UNAME_MACHINE-sni-sysv4
+       else
+               GUESS=ns32k-sni-sysv
+       fi
+       ;;
+    PENTIUM:*:4.0*:*)  # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                       # says <Richard.M.Bartel@ccMail.Census.GOV>
+       GUESS=i586-unisys-sysv4
+       ;;
+    *:UNIX_System_V:4*:FTX*)
+       # From Gerald Hewes <hewes@openmarket.com>.
+       # How about differentiating between stratus architectures? -djm
+       GUESS=hppa1.1-stratus-sysv4
+       ;;
+    *:*:*:FTX*)
+       # From seanf@swdc.stratus.com.
+       GUESS=i860-stratus-sysv4
+       ;;
+    i*86:VOS:*:*)
+       # From Paul.Green@stratus.com.
+       GUESS=$UNAME_MACHINE-stratus-vos
+       ;;
+    *:VOS:*:*)
+       # From Paul.Green@stratus.com.
+       GUESS=hppa1.1-stratus-vos
+       ;;
+    mc68*:A/UX:*:*)
+       GUESS=m68k-apple-aux$UNAME_RELEASE
+       ;;
+    news*:NEWS-OS:6*:*)
+       GUESS=mips-sony-newsos6
+       ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+       if test -d /usr/nec; then
+               GUESS=mips-nec-sysv$UNAME_RELEASE
+       else
+               GUESS=mips-unknown-sysv$UNAME_RELEASE
+       fi
+       ;;
+    BeBox:BeOS:*:*)    # BeOS running on hardware made by Be, PPC only.
+       GUESS=powerpc-be-beos
+       ;;
+    BeMac:BeOS:*:*)    # BeOS running on Mac or Mac clone, PPC only.
+       GUESS=powerpc-apple-beos
+       ;;
+    BePC:BeOS:*:*)     # BeOS running on Intel PC compatible.
+       GUESS=i586-pc-beos
+       ;;
+    BePC:Haiku:*:*)    # Haiku running on Intel PC compatible.
+       GUESS=i586-pc-haiku
+       ;;
+    ppc:Haiku:*:*)     # Haiku running on Apple PowerPC
+       GUESS=powerpc-apple-haiku
+       ;;
+    *:Haiku:*:*)       # Haiku modern gcc (not bound by BeOS compat)
+       GUESS=$UNAME_MACHINE-unknown-haiku
+       ;;
+    SX-4:SUPER-UX:*:*)
+       GUESS=sx4-nec-superux$UNAME_RELEASE
+       ;;
+    SX-5:SUPER-UX:*:*)
+       GUESS=sx5-nec-superux$UNAME_RELEASE
+       ;;
+    SX-6:SUPER-UX:*:*)
+       GUESS=sx6-nec-superux$UNAME_RELEASE
+       ;;
+    SX-7:SUPER-UX:*:*)
+       GUESS=sx7-nec-superux$UNAME_RELEASE
+       ;;
+    SX-8:SUPER-UX:*:*)
+       GUESS=sx8-nec-superux$UNAME_RELEASE
+       ;;
+    SX-8R:SUPER-UX:*:*)
+       GUESS=sx8r-nec-superux$UNAME_RELEASE
+       ;;
+    SX-ACE:SUPER-UX:*:*)
+       GUESS=sxace-nec-superux$UNAME_RELEASE
+       ;;
+    Power*:Rhapsody:*:*)
+       GUESS=powerpc-apple-rhapsody$UNAME_RELEASE
+       ;;
+    *:Rhapsody:*:*)
+       GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE
+       ;;
+    arm64:Darwin:*:*)
+       GUESS=aarch64-apple-darwin$UNAME_RELEASE
+       ;;
+    *:Darwin:*:*)
+       UNAME_PROCESSOR=`uname -p`
+       case $UNAME_PROCESSOR in
+           unknown) UNAME_PROCESSOR=powerpc ;;
+       esac
+       if command -v xcode-select > /dev/null 2> /dev/null && \
+               ! xcode-select --print-path > /dev/null 2> /dev/null ; then
+           # Avoid executing cc if there is no toolchain installed as
+           # cc will be a stub that puts up a graphical alert
+           # prompting the user to install developer tools.
+           CC_FOR_BUILD=no_compiler_found
+       else
+           set_cc_for_build
+       fi
+       if test "$CC_FOR_BUILD" != no_compiler_found; then
+           if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+                  (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+                  grep IS_64BIT_ARCH >/dev/null
+           then
+               case $UNAME_PROCESSOR in
+                   i386) UNAME_PROCESSOR=x86_64 ;;
+                   powerpc) UNAME_PROCESSOR=powerpc64 ;;
+               esac
+           fi
+           # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
+           if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
+                  (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+                  grep IS_PPC >/dev/null
+           then
+               UNAME_PROCESSOR=powerpc
+           fi
+       elif test "$UNAME_PROCESSOR" = i386 ; then
+           # uname -m returns i386 or x86_64
+           UNAME_PROCESSOR=$UNAME_MACHINE
+       fi
+       GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE
+       ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+       UNAME_PROCESSOR=`uname -p`
+       if test "$UNAME_PROCESSOR" = x86; then
+               UNAME_PROCESSOR=i386
+               UNAME_MACHINE=pc
+       fi
+       GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE
+       ;;
+    *:QNX:*:4*)
+       GUESS=i386-pc-qnx
+       ;;
+    NEO-*:NONSTOP_KERNEL:*:*)
+       GUESS=neo-tandem-nsk$UNAME_RELEASE
+       ;;
+    NSE-*:NONSTOP_KERNEL:*:*)
+       GUESS=nse-tandem-nsk$UNAME_RELEASE
+       ;;
+    NSR-*:NONSTOP_KERNEL:*:*)
+       GUESS=nsr-tandem-nsk$UNAME_RELEASE
+       ;;
+    NSV-*:NONSTOP_KERNEL:*:*)
+       GUESS=nsv-tandem-nsk$UNAME_RELEASE
+       ;;
+    NSX-*:NONSTOP_KERNEL:*:*)
+       GUESS=nsx-tandem-nsk$UNAME_RELEASE
+       ;;
+    *:NonStop-UX:*:*)
+       GUESS=mips-compaq-nonstopux
+       ;;
+    BS2000:POSIX*:*:*)
+       GUESS=bs2000-siemens-sysv
+       ;;
+    DS/*:UNIX_System_V:*:*)
+       GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE
+       ;;
+    *:Plan9:*:*)
+       # "uname -m" is not consistent, so use $cputype instead. 386
+       # is converted to i386 for consistency with other x86
+       # operating systems.
+       if test "${cputype-}" = 386; then
+           UNAME_MACHINE=i386
+       elif test "x${cputype-}" != x; then
+           UNAME_MACHINE=$cputype
+       fi
+       GUESS=$UNAME_MACHINE-unknown-plan9
+       ;;
+    *:TOPS-10:*:*)
+       GUESS=pdp10-unknown-tops10
+       ;;
+    *:TENEX:*:*)
+       GUESS=pdp10-unknown-tenex
+       ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+       GUESS=pdp10-dec-tops20
+       ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+       GUESS=pdp10-xkl-tops20
+       ;;
+    *:TOPS-20:*:*)
+       GUESS=pdp10-unknown-tops20
+       ;;
+    *:ITS:*:*)
+       GUESS=pdp10-unknown-its
+       ;;
+    SEI:*:*:SEIUX)
+       GUESS=mips-sei-seiux$UNAME_RELEASE
+       ;;
+    *:DragonFly:*:*)
+       DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+       GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL
+       ;;
+    *:*VMS:*:*)
+       UNAME_MACHINE=`(uname -p) 2>/dev/null`
+       case $UNAME_MACHINE in
+           A*) GUESS=alpha-dec-vms ;;
+           I*) GUESS=ia64-dec-vms ;;
+           V*) GUESS=vax-dec-vms ;;
+       esac ;;
+    *:XENIX:*:SysV)
+       GUESS=i386-pc-xenix
+       ;;
+    i*86:skyos:*:*)
+       SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`
+       GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL
+       ;;
+    i*86:rdos:*:*)
+       GUESS=$UNAME_MACHINE-pc-rdos
+       ;;
+    i*86:Fiwix:*:*)
+       GUESS=$UNAME_MACHINE-pc-fiwix
+       ;;
+    *:AROS:*:*)
+       GUESS=$UNAME_MACHINE-unknown-aros
+       ;;
+    x86_64:VMkernel:*:*)
+       GUESS=$UNAME_MACHINE-unknown-esx
+       ;;
+    amd64:Isilon\ OneFS:*:*)
+       GUESS=x86_64-unknown-onefs
+       ;;
+    *:Unleashed:*:*)
+       GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
+       ;;
+esac
+
+# Do we have a guess based on uname results?
+if test "x$GUESS" != x; then
+    echo "$GUESS"
+    exit
+fi
+
+# No uname command or uname output not recognized.
+set_cc_for_build
+cat > "$dummy.c" <<EOF
+#ifdef _SEQUENT_
+#include <sys/types.h>
+#include <sys/utsname.h>
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || 
defined(__ultrix__)
+#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || 
defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#include <signal.h>
+#if defined(_SIZE_T_) || defined(SIGLOST)
+#include <sys/utsname.h>
+#endif
+#endif
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+  "4"
+#else
+  ""
+#endif
+  ); exit (0);
+#endif
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+  struct utsname un;
+
+  uname(&un);
+  if (strncmp(un.version, "V2", 2) == 0) {
+    printf ("i386-sequent-ptx2\n"); exit (0);
+  }
+  if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+    printf ("i386-sequent-ptx1\n"); exit (0);
+  }
+  printf ("i386-sequent-ptx\n"); exit (0);
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+#include <sys/param.h>
+#if defined (BSD)
+#if BSD == 43
+  printf ("vax-dec-bsd4.3\n"); exit (0);
+#else
+#if BSD == 199006
+  printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#else
+  printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#endif
+#else
+  printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#else
+#if defined(_SIZE_T_) || defined(SIGLOST)
+  struct utsname un;
+  uname (&un);
+  printf ("vax-dec-ultrix%s\n", un.release); exit (0);
+#else
+  printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || 
defined(__ultrix__)
+#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || 
defined(__MIPS__)
+#if defined(_SIZE_T_) || defined(SIGLOST)
+  struct utsname *un;
+  uname (&un);
+  printf ("mips-dec-ultrix%s\n", un.release); exit (0);
+#else
+  printf ("mips-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` &&
+       { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; }
+
+echo "$0: unable to guess system type" >&2
+
+case $UNAME_MACHINE:$UNAME_SYSTEM in
+    mips:Linux | mips64:Linux)
+       # If we got here on MIPS GNU/Linux, output extra information.
+       cat >&2 <<EOF
+
+NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
+the system type. Please install a C compiler and try again.
+EOF
+       ;;
+esac
+
+cat >&2 <<EOF
+
+This script (version $timestamp), has failed to recognize the
+operating system you are using. If your script is old, overwrite *all*
+copies of config.guess and config.sub with the latest versions from:
+
+  https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
+and
+  https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
+EOF
+
+our_year=`echo $timestamp | sed 's,-.*,,'`
+thisyear=`date +%Y`
+# shellcheck disable=SC2003
+script_age=`expr "$thisyear" - "$our_year"`
+if test "$script_age" -lt 3 ; then
+   cat >&2 <<EOF
+
+If $0 has already been updated, send the following data and any
+information you think might be pertinent to config-patches@gnu.org to
+provide the necessary information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = "$UNAME_MACHINE"
+UNAME_RELEASE = "$UNAME_RELEASE"
+UNAME_SYSTEM  = "$UNAME_SYSTEM"
+UNAME_VERSION = "$UNAME_VERSION"
+EOF
+fi
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/exec/config.h.in b/exec/config.h.in
new file mode 100644
index 00000000000..3e04af37f79
--- /dev/null
+++ b/exec/config.h.in
@@ -0,0 +1,358 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* 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/>. */
+
+/* Define to number of reserved bytes past the stack frame. */
+#undef ABI_RED_ZONE
+
+/* Define if building universal (internal helper macro) */
+#undef AC_APPLE_UNIVERSAL_BUILD
+
+/* Define to number of the `clone3' system call. */
+#undef CLONE3_SYSCALL
+
+/* Define to number of the `clone' system call. */
+#undef CLONE_SYSCALL
+
+/* Virtual address for loading PIC executables */
+#undef EXECUTABLE_BASE
+
+/* Define to 1 if the system utilizes 64-bit ELF. */
+#undef EXEC_64
+
+/* Define to number of the `exec' system call. */
+#undef EXEC_SYSCALL
+
+/* Define to 1 if you have the declaration of `stpcpy', and to 0 if you don't.
+   */
+#undef HAVE_DECL_STPCPY
+
+/* Define to 1 if you have the declaration of `stpncpy', and to 0 if you
+   don't. */
+#undef HAVE_DECL_STPNCPY
+
+/* Define to 1 if you have the `getpagesize' function. */
+#undef HAVE_GETPAGESIZE
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <minix/config.h> header file. */
+#undef HAVE_MINIX_CONFIG_H
+
+/* Define to 1 if process_vm_readv is available. */
+#undef HAVE_PROCESS_VM
+
+/* Define to 1 if `si_syscall' is a member of `siginfo_t'. */
+#undef HAVE_SIGINFO_T_SI_SYSCALL
+
+/* Define to 1 if stdbool.h conforms to C99. */
+#undef HAVE_STDBOOL_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `stpcpy' function. */
+#undef HAVE_STPCPY
+
+/* Define to 1 if you have the `stpncpy' function. */
+#undef HAVE_STPNCPY
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#undef HAVE_SYS_UIO_H
+
+/* Define to 1 if the system has the type `uintptr_t'. */
+#undef HAVE_UINTPTR_T
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#undef HAVE_WCHAR_H
+
+/* Define to 1 if the system has the type `_Bool'. */
+#undef HAVE__BOOL
+
+/* Virtual address for loading PIC interpreters */
+#undef INTERPRETER_BASE
+
+/* Define to 1 if MIPS NABI calling convention is being used. */
+#undef MIPS_NABI
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to number of the `readlinkat' system call. */
+#undef READLINKAT_SYSCALL
+
+/* Define to number of the `readlink' system call. */
+#undef READLINK_SYSCALL
+
+/* Define to 1 if the library is used within a signal handler. */
+#undef REENTRANT
+
+/* Define to 1 if the stack grows downwards. */
+#undef STACK_GROWS_DOWNWARDS
+
+/* Define to register holding the stack pointer. */
+#undef STACK_POINTER
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+   required in a freestanding environment). This macro is provided for
+   backward compatibility; new code need not use it. */
+#undef STDC_HEADERS
+
+/* Define to register holding arg1 to system calls. */
+#undef SYSCALL_ARG1_REG
+
+/* Define to register holding arg2 to system calls. */
+#undef SYSCALL_ARG2_REG
+
+/* Define to register holding arg3 to system calls. */
+#undef SYSCALL_ARG3_REG
+
+/* Define to register holding arg0 to system calls. */
+#undef SYSCALL_ARG_REG
+
+/* Define to header holding system call numbers. */
+#undef SYSCALL_HEADER
+
+/* Define to register holding the system call number. */
+#undef SYSCALL_NUM_REG
+
+/* Define to register holding value of system calls. */
+#undef SYSCALL_RET_REG
+
+/* Define to header holding USER_REGS_STRUCT. */
+#undef USER_HEADER
+
+/* Define to structure holding user registers. */
+#undef USER_REGS_STRUCT
+
+/* Define to word type used by tracees. */
+#undef USER_WORD
+
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+/* Enable general extensions on macOS.  */
+#ifndef _DARWIN_C_SOURCE
+# undef _DARWIN_C_SOURCE
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* Enable X/Open compliant socket functions that do not require linking
+   with -lxnet on HP-UX 11.11.  */
+#ifndef _HPUX_ALT_XOPEN_SOCKET_API
+# undef _HPUX_ALT_XOPEN_SOCKET_API
+#endif
+/* Identify the host operating system as Minix.
+   This macro does not affect the system headers' behavior.
+   A future release of Autoconf may stop defining this macro.  */
+#ifndef _MINIX
+# undef _MINIX
+#endif
+/* Enable general extensions on NetBSD.
+   Enable NetBSD compatibility extensions on Minix.  */
+#ifndef _NETBSD_SOURCE
+# undef _NETBSD_SOURCE
+#endif
+/* Enable OpenBSD compatibility extensions on NetBSD.
+   Oddly enough, this does nothing on OpenBSD.  */
+#ifndef _OPENBSD_SOURCE
+# undef _OPENBSD_SOURCE
+#endif
+/* Define to 1 if needed for POSIX-compatible behavior.  */
+#ifndef _POSIX_SOURCE
+# undef _POSIX_SOURCE
+#endif
+/* Define to 2 if needed for POSIX-compatible behavior.  */
+#ifndef _POSIX_1_SOURCE
+# undef _POSIX_1_SOURCE
+#endif
+/* Enable POSIX-compatible threading on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-5:2014.  */
+#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
+# undef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-1:2014.  */
+#ifndef __STDC_WANT_IEC_60559_BFP_EXT__
+# undef __STDC_WANT_IEC_60559_BFP_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-2:2015.  */
+#ifndef __STDC_WANT_IEC_60559_DFP_EXT__
+# undef __STDC_WANT_IEC_60559_DFP_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-4:2015.  */
+#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__
+# undef __STDC_WANT_IEC_60559_FUNCS_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-3:2015.  */
+#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
+# undef __STDC_WANT_IEC_60559_TYPES_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TR 24731-2:2010.  */
+#ifndef __STDC_WANT_LIB_EXT2__
+# undef __STDC_WANT_LIB_EXT2__
+#endif
+/* Enable extensions specified by ISO/IEC 24747:2009.  */
+#ifndef __STDC_WANT_MATH_SPEC_FUNCS__
+# undef __STDC_WANT_MATH_SPEC_FUNCS__
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+/* Enable X/Open extensions.  Define to 500 only if necessary
+   to make mbstate_t available.  */
+#ifndef _XOPEN_SOURCE
+# undef _XOPEN_SOURCE
+#endif
+
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+   significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+#  define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+#  undef WORDS_BIGENDIAN
+# endif
+#endif
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+#undef _UINT32_T
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+#undef _UINT64_T
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+#undef _UINT8_T
+
+/* Define as a signed integer type capable of holding a process identifier. */
+#undef pid_t
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef ssize_t
+
+/* Define to the type of an unsigned integer type of width exactly 16 bits if
+   such a type exists and the standard includes do not define it. */
+#undef uint16_t
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+   such a type exists and the standard includes do not define it. */
+#undef uint32_t
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+   such a type exists and the standard includes do not define it. */
+#undef uint64_t
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+   such a type exists and the standard includes do not define it. */
+#undef uint8_t
+
+/* Define to the type of an unsigned integer type wide enough to hold a
+   pointer, if such a type exists, and if the system does not define it. */
+#undef uintptr_t
+
+
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# ifndef HAVE__BOOL
+#  ifdef __cplusplus
+typedef bool _Bool;
+#  else
+#   define _Bool signed char
+#  endif
+# endif
+# define bool _Bool
+# define false 0
+# define true 1
+# define __bool_true_false_are_defined 1
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif /* HAVE_SYS_PARAM_H */
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif /* MAX */
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif /* MIN */
+
diff --git a/exec/config.sub b/exec/config.sub
new file mode 100755
index 00000000000..b41da55df45
--- /dev/null
+++ b/exec/config.sub
@@ -0,0 +1,1890 @@
+#!/usr/bin/sh
+# Configuration validation subroutine script.
+#   Copyright 1992-2022 Free Software Foundation, Inc.
+
+# shellcheck disable=SC2006,SC2268 # see below for rationale
+
+timestamp='2022-01-03'
+
+# This file 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.
+#
+# This program 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 this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#      CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#      CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+# The "shellcheck disable" line above the timestamp inhibits complaints
+# about features and limitations of the classic Bourne shell that were
+# superseded or lifted in POSIX.  However, this script identifies a wide
+# variety of pre-POSIX systems that do not have POSIX shells at all, and
+# even some reasonably current systems (Solaris 10 as case-in-point) still
+# have a pre-POSIX /bin/sh.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
+
+Canonicalize a configuration name.
+
+Options:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright 1992-2022 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo "$1"
+       exit ;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Split fields of configuration type
+# shellcheck disable=SC2162
+saved_IFS=$IFS
+IFS="-" read field1 field2 field3 field4 <<EOF
+$1
+EOF
+IFS=$saved_IFS
+
+# Separate into logical components for further validation
+case $1 in
+       *-*-*-*-*)
+               echo Invalid configuration \`"$1"\': more than four components 
>&2
+               exit 1
+               ;;
+       *-*-*-*)
+               basic_machine=$field1-$field2
+               basic_os=$field3-$field4
+               ;;
+       *-*-*)
+               # Ambiguous whether COMPANY is present, or skipped and 
KERNEL-OS is two
+               # parts
+               maybe_os=$field2-$field3
+               case $maybe_os in
+                       nto-qnx* | linux-* | uclinux-uclibc* \
+                       | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | 
netbsd*-gnu* \
+                       | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \
+                       | storm-chaos* | os2-emx* | rtmk-nova*)
+                               basic_machine=$field1
+                               basic_os=$maybe_os
+                               ;;
+                       android-linux)
+                               basic_machine=$field1-unknown
+                               basic_os=linux-android
+                               ;;
+                       *)
+                               basic_machine=$field1-$field2
+                               basic_os=$field3
+                               ;;
+               esac
+               ;;
+       *-*)
+               # A lone config we happen to match not fitting any pattern
+               case $field1-$field2 in
+                       decstation-3100)
+                               basic_machine=mips-dec
+                               basic_os=
+                               ;;
+                       *-*)
+                               # Second component is usually, but not always 
the OS
+                               case $field2 in
+                                       # Prevent following clause from 
handling this valid os
+                                       sun*os*)
+                                               basic_machine=$field1
+                                               basic_os=$field2
+                                               ;;
+                                       zephyr*)
+                                               basic_machine=$field1-unknown
+                                               basic_os=$field2
+                                               ;;
+                                       # Manufacturers
+                                       dec* | mips* | sequent* | encore* | 
pc533* | sgi* | sony* \
+                                       | att* | 7300* | 3300* | delta* | 
motorola* | sun[234]* \
+                                       | unicom* | ibm* | next | hp | isi* | 
apollo | altos* \
+                                       | convergent* | ncr* | news | 32* | 
3600* | 3100* \
+                                       | hitachi* | c[123]* | convex* | sun | 
crds | omron* | dg \
+                                       | ultra | tti* | harris | dolphin | 
highlevel | gould \
+                                       | cbm | ns | masscomp | apple | axis | 
knuth | cray \
+                                       | microblaze* | sim | cisco \
+                                       | oki | wec | wrs | winbond)
+                                               basic_machine=$field1-$field2
+                                               basic_os=
+                                               ;;
+                                       *)
+                                               basic_machine=$field1
+                                               basic_os=$field2
+                                               ;;
+                               esac
+                       ;;
+               esac
+               ;;
+       *)
+               # Convert single-component short-hands not valid as part of
+               # multi-component configurations.
+               case $field1 in
+                       386bsd)
+                               basic_machine=i386-pc
+                               basic_os=bsd
+                               ;;
+                       a29khif)
+                               basic_machine=a29k-amd
+                               basic_os=udi
+                               ;;
+                       adobe68k)
+                               basic_machine=m68010-adobe
+                               basic_os=scout
+                               ;;
+                       alliant)
+                               basic_machine=fx80-alliant
+                               basic_os=
+                               ;;
+                       altos | altos3068)
+                               basic_machine=m68k-altos
+                               basic_os=
+                               ;;
+                       am29k)
+                               basic_machine=a29k-none
+                               basic_os=bsd
+                               ;;
+                       amdahl)
+                               basic_machine=580-amdahl
+                               basic_os=sysv
+                               ;;
+                       amiga)
+                               basic_machine=m68k-unknown
+                               basic_os=
+                               ;;
+                       amigaos | amigados)
+                               basic_machine=m68k-unknown
+                               basic_os=amigaos
+                               ;;
+                       amigaunix | amix)
+                               basic_machine=m68k-unknown
+                               basic_os=sysv4
+                               ;;
+                       apollo68)
+                               basic_machine=m68k-apollo
+                               basic_os=sysv
+                               ;;
+                       apollo68bsd)
+                               basic_machine=m68k-apollo
+                               basic_os=bsd
+                               ;;
+                       aros)
+                               basic_machine=i386-pc
+                               basic_os=aros
+                               ;;
+                       aux)
+                               basic_machine=m68k-apple
+                               basic_os=aux
+                               ;;
+                       balance)
+                               basic_machine=ns32k-sequent
+                               basic_os=dynix
+                               ;;
+                       blackfin)
+                               basic_machine=bfin-unknown
+                               basic_os=linux
+                               ;;
+                       cegcc)
+                               basic_machine=arm-unknown
+                               basic_os=cegcc
+                               ;;
+                       convex-c1)
+                               basic_machine=c1-convex
+                               basic_os=bsd
+                               ;;
+                       convex-c2)
+                               basic_machine=c2-convex
+                               basic_os=bsd
+                               ;;
+                       convex-c32)
+                               basic_machine=c32-convex
+                               basic_os=bsd
+                               ;;
+                       convex-c34)
+                               basic_machine=c34-convex
+                               basic_os=bsd
+                               ;;
+                       convex-c38)
+                               basic_machine=c38-convex
+                               basic_os=bsd
+                               ;;
+                       cray)
+                               basic_machine=j90-cray
+                               basic_os=unicos
+                               ;;
+                       crds | unos)
+                               basic_machine=m68k-crds
+                               basic_os=
+                               ;;
+                       da30)
+                               basic_machine=m68k-da30
+                               basic_os=
+                               ;;
+                       decstation | pmax | pmin | dec3100 | decstatn)
+                               basic_machine=mips-dec
+                               basic_os=
+                               ;;
+                       delta88)
+                               basic_machine=m88k-motorola
+                               basic_os=sysv3
+                               ;;
+                       dicos)
+                               basic_machine=i686-pc
+                               basic_os=dicos
+                               ;;
+                       djgpp)
+                               basic_machine=i586-pc
+                               basic_os=msdosdjgpp
+                               ;;
+                       ebmon29k)
+                               basic_machine=a29k-amd
+                               basic_os=ebmon
+                               ;;
+                       es1800 | OSE68k | ose68k | ose | OSE)
+                               basic_machine=m68k-ericsson
+                               basic_os=ose
+                               ;;
+                       gmicro)
+                               basic_machine=tron-gmicro
+                               basic_os=sysv
+                               ;;
+                       go32)
+                               basic_machine=i386-pc
+                               basic_os=go32
+                               ;;
+                       h8300hms)
+                               basic_machine=h8300-hitachi
+                               basic_os=hms
+                               ;;
+                       h8300xray)
+                               basic_machine=h8300-hitachi
+                               basic_os=xray
+                               ;;
+                       h8500hms)
+                               basic_machine=h8500-hitachi
+                               basic_os=hms
+                               ;;
+                       harris)
+                               basic_machine=m88k-harris
+                               basic_os=sysv3
+                               ;;
+                       hp300 | hp300hpux)
+                               basic_machine=m68k-hp
+                               basic_os=hpux
+                               ;;
+                       hp300bsd)
+                               basic_machine=m68k-hp
+                               basic_os=bsd
+                               ;;
+                       hppaosf)
+                               basic_machine=hppa1.1-hp
+                               basic_os=osf
+                               ;;
+                       hppro)
+                               basic_machine=hppa1.1-hp
+                               basic_os=proelf
+                               ;;
+                       i386mach)
+                               basic_machine=i386-mach
+                               basic_os=mach
+                               ;;
+                       isi68 | isi)
+                               basic_machine=m68k-isi
+                               basic_os=sysv
+                               ;;
+                       m68knommu)
+                               basic_machine=m68k-unknown
+                               basic_os=linux
+                               ;;
+                       magnum | m3230)
+                               basic_machine=mips-mips
+                               basic_os=sysv
+                               ;;
+                       merlin)
+                               basic_machine=ns32k-utek
+                               basic_os=sysv
+                               ;;
+                       mingw64)
+                               basic_machine=x86_64-pc
+                               basic_os=mingw64
+                               ;;
+                       mingw32)
+                               basic_machine=i686-pc
+                               basic_os=mingw32
+                               ;;
+                       mingw32ce)
+                               basic_machine=arm-unknown
+                               basic_os=mingw32ce
+                               ;;
+                       monitor)
+                               basic_machine=m68k-rom68k
+                               basic_os=coff
+                               ;;
+                       morphos)
+                               basic_machine=powerpc-unknown
+                               basic_os=morphos
+                               ;;
+                       moxiebox)
+                               basic_machine=moxie-unknown
+                               basic_os=moxiebox
+                               ;;
+                       msdos)
+                               basic_machine=i386-pc
+                               basic_os=msdos
+                               ;;
+                       msys)
+                               basic_machine=i686-pc
+                               basic_os=msys
+                               ;;
+                       mvs)
+                               basic_machine=i370-ibm
+                               basic_os=mvs
+                               ;;
+                       nacl)
+                               basic_machine=le32-unknown
+                               basic_os=nacl
+                               ;;
+                       ncr3000)
+                               basic_machine=i486-ncr
+                               basic_os=sysv4
+                               ;;
+                       netbsd386)
+                               basic_machine=i386-pc
+                               basic_os=netbsd
+                               ;;
+                       netwinder)
+                               basic_machine=armv4l-rebel
+                               basic_os=linux
+                               ;;
+                       news | news700 | news800 | news900)
+                               basic_machine=m68k-sony
+                               basic_os=newsos
+                               ;;
+                       news1000)
+                               basic_machine=m68030-sony
+                               basic_os=newsos
+                               ;;
+                       necv70)
+                               basic_machine=v70-nec
+                               basic_os=sysv
+                               ;;
+                       nh3000)
+                               basic_machine=m68k-harris
+                               basic_os=cxux
+                               ;;
+                       nh[45]000)
+                               basic_machine=m88k-harris
+                               basic_os=cxux
+                               ;;
+                       nindy960)
+                               basic_machine=i960-intel
+                               basic_os=nindy
+                               ;;
+                       mon960)
+                               basic_machine=i960-intel
+                               basic_os=mon960
+                               ;;
+                       nonstopux)
+                               basic_machine=mips-compaq
+                               basic_os=nonstopux
+                               ;;
+                       os400)
+                               basic_machine=powerpc-ibm
+                               basic_os=os400
+                               ;;
+                       OSE68000 | ose68000)
+                               basic_machine=m68000-ericsson
+                               basic_os=ose
+                               ;;
+                       os68k)
+                               basic_machine=m68k-none
+                               basic_os=os68k
+                               ;;
+                       paragon)
+                               basic_machine=i860-intel
+                               basic_os=osf
+                               ;;
+                       parisc)
+                               basic_machine=hppa-unknown
+                               basic_os=linux
+                               ;;
+                       psp)
+                               basic_machine=mipsallegrexel-sony
+                               basic_os=psp
+                               ;;
+                       pw32)
+                               basic_machine=i586-unknown
+                               basic_os=pw32
+                               ;;
+                       rdos | rdos64)
+                               basic_machine=x86_64-pc
+                               basic_os=rdos
+                               ;;
+                       rdos32)
+                               basic_machine=i386-pc
+                               basic_os=rdos
+                               ;;
+                       rom68k)
+                               basic_machine=m68k-rom68k
+                               basic_os=coff
+                               ;;
+                       sa29200)
+                               basic_machine=a29k-amd
+                               basic_os=udi
+                               ;;
+                       sei)
+                               basic_machine=mips-sei
+                               basic_os=seiux
+                               ;;
+                       sequent)
+                               basic_machine=i386-sequent
+                               basic_os=
+                               ;;
+                       sps7)
+                               basic_machine=m68k-bull
+                               basic_os=sysv2
+                               ;;
+                       st2000)
+                               basic_machine=m68k-tandem
+                               basic_os=
+                               ;;
+                       stratus)
+                               basic_machine=i860-stratus
+                               basic_os=sysv4
+                               ;;
+                       sun2)
+                               basic_machine=m68000-sun
+                               basic_os=
+                               ;;
+                       sun2os3)
+                               basic_machine=m68000-sun
+                               basic_os=sunos3
+                               ;;
+                       sun2os4)
+                               basic_machine=m68000-sun
+                               basic_os=sunos4
+                               ;;
+                       sun3)
+                               basic_machine=m68k-sun
+                               basic_os=
+                               ;;
+                       sun3os3)
+                               basic_machine=m68k-sun
+                               basic_os=sunos3
+                               ;;
+                       sun3os4)
+                               basic_machine=m68k-sun
+                               basic_os=sunos4
+                               ;;
+                       sun4)
+                               basic_machine=sparc-sun
+                               basic_os=
+                               ;;
+                       sun4os3)
+                               basic_machine=sparc-sun
+                               basic_os=sunos3
+                               ;;
+                       sun4os4)
+                               basic_machine=sparc-sun
+                               basic_os=sunos4
+                               ;;
+                       sun4sol2)
+                               basic_machine=sparc-sun
+                               basic_os=solaris2
+                               ;;
+                       sun386 | sun386i | roadrunner)
+                               basic_machine=i386-sun
+                               basic_os=
+                               ;;
+                       sv1)
+                               basic_machine=sv1-cray
+                               basic_os=unicos
+                               ;;
+                       symmetry)
+                               basic_machine=i386-sequent
+                               basic_os=dynix
+                               ;;
+                       t3e)
+                               basic_machine=alphaev5-cray
+                               basic_os=unicos
+                               ;;
+                       t90)
+                               basic_machine=t90-cray
+                               basic_os=unicos
+                               ;;
+                       toad1)
+                               basic_machine=pdp10-xkl
+                               basic_os=tops20
+                               ;;
+                       tpf)
+                               basic_machine=s390x-ibm
+                               basic_os=tpf
+                               ;;
+                       udi29k)
+                               basic_machine=a29k-amd
+                               basic_os=udi
+                               ;;
+                       ultra3)
+                               basic_machine=a29k-nyu
+                               basic_os=sym1
+                               ;;
+                       v810 | necv810)
+                               basic_machine=v810-nec
+                               basic_os=none
+                               ;;
+                       vaxv)
+                               basic_machine=vax-dec
+                               basic_os=sysv
+                               ;;
+                       vms)
+                               basic_machine=vax-dec
+                               basic_os=vms
+                               ;;
+                       vsta)
+                               basic_machine=i386-pc
+                               basic_os=vsta
+                               ;;
+                       vxworks960)
+                               basic_machine=i960-wrs
+                               basic_os=vxworks
+                               ;;
+                       vxworks68)
+                               basic_machine=m68k-wrs
+                               basic_os=vxworks
+                               ;;
+                       vxworks29k)
+                               basic_machine=a29k-wrs
+                               basic_os=vxworks
+                               ;;
+                       xbox)
+                               basic_machine=i686-pc
+                               basic_os=mingw32
+                               ;;
+                       ymp)
+                               basic_machine=ymp-cray
+                               basic_os=unicos
+                               ;;
+                       *)
+                               basic_machine=$1
+                               basic_os=
+                               ;;
+               esac
+               ;;
+esac
+
+# Decode 1-component or ad-hoc basic machines
+case $basic_machine in
+       # Here we handle the default manufacturer of certain CPU types.  It is 
in
+       # some cases the only manufacturer, in others, it is the most popular.
+       w89k)
+               cpu=hppa1.1
+               vendor=winbond
+               ;;
+       op50n)
+               cpu=hppa1.1
+               vendor=oki
+               ;;
+       op60c)
+               cpu=hppa1.1
+               vendor=oki
+               ;;
+       ibm*)
+               cpu=i370
+               vendor=ibm
+               ;;
+       orion105)
+               cpu=clipper
+               vendor=highlevel
+               ;;
+       mac | mpw | mac-mpw)
+               cpu=m68k
+               vendor=apple
+               ;;
+       pmac | pmac-mpw)
+               cpu=powerpc
+               vendor=apple
+               ;;
+
+       # Recognize the various machine names and aliases which stand
+       # for a CPU type and a company and sometimes even an OS.
+       3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+               cpu=m68000
+               vendor=att
+               ;;
+       3b*)
+               cpu=we32k
+               vendor=att
+               ;;
+       bluegene*)
+               cpu=powerpc
+               vendor=ibm
+               basic_os=cnk
+               ;;
+       decsystem10* | dec10*)
+               cpu=pdp10
+               vendor=dec
+               basic_os=tops10
+               ;;
+       decsystem20* | dec20*)
+               cpu=pdp10
+               vendor=dec
+               basic_os=tops20
+               ;;
+       delta | 3300 | motorola-3300 | motorola-delta \
+             | 3300-motorola | delta-motorola)
+               cpu=m68k
+               vendor=motorola
+               ;;
+       dpx2*)
+               cpu=m68k
+               vendor=bull
+               basic_os=sysv3
+               ;;
+       encore | umax | mmax)
+               cpu=ns32k
+               vendor=encore
+               ;;
+       elxsi)
+               cpu=elxsi
+               vendor=elxsi
+               basic_os=${basic_os:-bsd}
+               ;;
+       fx2800)
+               cpu=i860
+               vendor=alliant
+               ;;
+       genix)
+               cpu=ns32k
+               vendor=ns
+               ;;
+       h3050r* | hiux*)
+               cpu=hppa1.1
+               vendor=hitachi
+               basic_os=hiuxwe2
+               ;;
+       hp3k9[0-9][0-9] | hp9[0-9][0-9])
+               cpu=hppa1.0
+               vendor=hp
+               ;;
+       hp9k2[0-9][0-9] | hp9k31[0-9])
+               cpu=m68000
+               vendor=hp
+               ;;
+       hp9k3[2-9][0-9])
+               cpu=m68k
+               vendor=hp
+               ;;
+       hp9k6[0-9][0-9] | hp6[0-9][0-9])
+               cpu=hppa1.0
+               vendor=hp
+               ;;
+       hp9k7[0-79][0-9] | hp7[0-79][0-9])
+               cpu=hppa1.1
+               vendor=hp
+               ;;
+       hp9k78[0-9] | hp78[0-9])
+               # FIXME: really hppa2.0-hp
+               cpu=hppa1.1
+               vendor=hp
+               ;;
+       hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | 
hp9k893 | hp893)
+               # FIXME: really hppa2.0-hp
+               cpu=hppa1.1
+               vendor=hp
+               ;;
+       hp9k8[0-9][13679] | hp8[0-9][13679])
+               cpu=hppa1.1
+               vendor=hp
+               ;;
+       hp9k8[0-9][0-9] | hp8[0-9][0-9])
+               cpu=hppa1.0
+               vendor=hp
+               ;;
+       i*86v32)
+               cpu=`echo "$1" | sed -e 's/86.*/86/'`
+               vendor=pc
+               basic_os=sysv32
+               ;;
+       i*86v4*)
+               cpu=`echo "$1" | sed -e 's/86.*/86/'`
+               vendor=pc
+               basic_os=sysv4
+               ;;
+       i*86v)
+               cpu=`echo "$1" | sed -e 's/86.*/86/'`
+               vendor=pc
+               basic_os=sysv
+               ;;
+       i*86sol2)
+               cpu=`echo "$1" | sed -e 's/86.*/86/'`
+               vendor=pc
+               basic_os=solaris2
+               ;;
+       j90 | j90-cray)
+               cpu=j90
+               vendor=cray
+               basic_os=${basic_os:-unicos}
+               ;;
+       iris | iris4d)
+               cpu=mips
+               vendor=sgi
+               case $basic_os in
+                   irix*)
+                       ;;
+                   *)
+                       basic_os=irix4
+                       ;;
+               esac
+               ;;
+       miniframe)
+               cpu=m68000
+               vendor=convergent
+               ;;
+       *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
+               cpu=m68k
+               vendor=atari
+               basic_os=mint
+               ;;
+       news-3600 | risc-news)
+               cpu=mips
+               vendor=sony
+               basic_os=newsos
+               ;;
+       next | m*-next)
+               cpu=m68k
+               vendor=next
+               case $basic_os in
+                   openstep*)
+                       ;;
+                   nextstep*)
+                       ;;
+                   ns2*)
+                     basic_os=nextstep2
+                       ;;
+                   *)
+                     basic_os=nextstep3
+                       ;;
+               esac
+               ;;
+       np1)
+               cpu=np1
+               vendor=gould
+               ;;
+       op50n-* | op60c-*)
+               cpu=hppa1.1
+               vendor=oki
+               basic_os=proelf
+               ;;
+       pa-hitachi)
+               cpu=hppa1.1
+               vendor=hitachi
+               basic_os=hiuxwe2
+               ;;
+       pbd)
+               cpu=sparc
+               vendor=tti
+               ;;
+       pbb)
+               cpu=m68k
+               vendor=tti
+               ;;
+       pc532)
+               cpu=ns32k
+               vendor=pc532
+               ;;
+       pn)
+               cpu=pn
+               vendor=gould
+               ;;
+       power)
+               cpu=power
+               vendor=ibm
+               ;;
+       ps2)
+               cpu=i386
+               vendor=ibm
+               ;;
+       rm[46]00)
+               cpu=mips
+               vendor=siemens
+               ;;
+       rtpc | rtpc-*)
+               cpu=romp
+               vendor=ibm
+               ;;
+       sde)
+               cpu=mipsisa32
+               vendor=sde
+               basic_os=${basic_os:-elf}
+               ;;
+       simso-wrs)
+               cpu=sparclite
+               vendor=wrs
+               basic_os=vxworks
+               ;;
+       tower | tower-32)
+               cpu=m68k
+               vendor=ncr
+               ;;
+       vpp*|vx|vx-*)
+               cpu=f301
+               vendor=fujitsu
+               ;;
+       w65)
+               cpu=w65
+               vendor=wdc
+               ;;
+       w89k-*)
+               cpu=hppa1.1
+               vendor=winbond
+               basic_os=proelf
+               ;;
+       none)
+               cpu=none
+               vendor=none
+               ;;
+       leon|leon[3-9])
+               cpu=sparc
+               vendor=$basic_machine
+               ;;
+       leon-*|leon[3-9]-*)
+               cpu=sparc
+               vendor=`echo "$basic_machine" | sed 's/-.*//'`
+               ;;
+
+       *-*)
+               # shellcheck disable=SC2162
+               saved_IFS=$IFS
+               IFS="-" read cpu vendor <<EOF
+$basic_machine
+EOF
+               IFS=$saved_IFS
+               ;;
+       # We use `pc' rather than `unknown'
+       # because (1) that's what they normally are, and
+       # (2) the word "unknown" tends to confuse beginning users.
+       i*86 | x86_64)
+               cpu=$basic_machine
+               vendor=pc
+               ;;
+       # These rules are duplicated from below for sake of the special case 
above;
+       # i.e. things that normalized to x86 arches should also default to "pc"
+       pc98)
+               cpu=i386
+               vendor=pc
+               ;;
+       x64 | amd64)
+               cpu=x86_64
+               vendor=pc
+               ;;
+       # Recognize the basic CPU types without company name.
+       *)
+               cpu=$basic_machine
+               vendor=unknown
+               ;;
+esac
+
+unset -v basic_machine
+
+# Decode basic machines in the full and proper CPU-Company form.
+case $cpu-$vendor in
+       # Here we handle the default manufacturer of certain CPU types in 
canonical form. It is in
+       # some cases the only manufacturer, in others, it is the most popular.
+       craynv-unknown)
+               vendor=cray
+               basic_os=${basic_os:-unicosmp}
+               ;;
+       c90-unknown | c90-cray)
+               vendor=cray
+               basic_os=${Basic_os:-unicos}
+               ;;
+       fx80-unknown)
+               vendor=alliant
+               ;;
+       romp-unknown)
+               vendor=ibm
+               ;;
+       mmix-unknown)
+               vendor=knuth
+               ;;
+       microblaze-unknown | microblazeel-unknown)
+               vendor=xilinx
+               ;;
+       rs6000-unknown)
+               vendor=ibm
+               ;;
+       vax-unknown)
+               vendor=dec
+               ;;
+       pdp11-unknown)
+               vendor=dec
+               ;;
+       we32k-unknown)
+               vendor=att
+               ;;
+       cydra-unknown)
+               vendor=cydrome
+               ;;
+       i370-ibm*)
+               vendor=ibm
+               ;;
+       orion-unknown)
+               vendor=highlevel
+               ;;
+       xps-unknown | xps100-unknown)
+               cpu=xps100
+               vendor=honeywell
+               ;;
+
+       # Here we normalize CPU types with a missing or matching vendor
+       armh-unknown | armh-alt)
+               cpu=armv7l
+               vendor=alt
+               basic_os=${basic_os:-linux-gnueabihf}
+               ;;
+       dpx20-unknown | dpx20-bull)
+               cpu=rs6000
+               vendor=bull
+               basic_os=${basic_os:-bosx}
+               ;;
+
+       # Here we normalize CPU types irrespective of the vendor
+       amd64-*)
+               cpu=x86_64
+               ;;
+       blackfin-*)
+               cpu=bfin
+               basic_os=linux
+               ;;
+       c54x-*)
+               cpu=tic54x
+               ;;
+       c55x-*)
+               cpu=tic55x
+               ;;
+       c6x-*)
+               cpu=tic6x
+               ;;
+       e500v[12]-*)
+               cpu=powerpc
+               basic_os=${basic_os}"spe"
+               ;;
+       mips3*-*)
+               cpu=mips64
+               ;;
+       ms1-*)
+               cpu=mt
+               ;;
+       m68knommu-*)
+               cpu=m68k
+               basic_os=linux
+               ;;
+       m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
+               cpu=s12z
+               ;;
+       openrisc-*)
+               cpu=or32
+               ;;
+       parisc-*)
+               cpu=hppa
+               basic_os=linux
+               ;;
+       pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+               cpu=i586
+               ;;
+       pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*)
+               cpu=i686
+               ;;
+       pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+               cpu=i686
+               ;;
+       pentium4-*)
+               cpu=i786
+               ;;
+       pc98-*)
+               cpu=i386
+               ;;
+       ppc-* | ppcbe-*)
+               cpu=powerpc
+               ;;
+       ppcle-* | powerpclittle-*)
+               cpu=powerpcle
+               ;;
+       ppc64-*)
+               cpu=powerpc64
+               ;;
+       ppc64le-* | powerpc64little-*)
+               cpu=powerpc64le
+               ;;
+       sb1-*)
+               cpu=mipsisa64sb1
+               ;;
+       sb1el-*)
+               cpu=mipsisa64sb1el
+               ;;
+       sh5e[lb]-*)
+               cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'`
+               ;;
+       spur-*)
+               cpu=spur
+               ;;
+       strongarm-* | thumb-*)
+               cpu=arm
+               ;;
+       tx39-*)
+               cpu=mipstx39
+               ;;
+       tx39el-*)
+               cpu=mipstx39el
+               ;;
+       x64-*)
+               cpu=x86_64
+               ;;
+       xscale-* | xscalee[bl]-*)
+               cpu=`echo "$cpu" | sed 's/^xscale/arm/'`
+               ;;
+       arm64-* | aarch64le-*)
+               cpu=aarch64
+               ;;
+
+       # Recognize the canonical CPU Types that limit and/or modify the
+       # company names they are paired with.
+       cr16-*)
+               basic_os=${basic_os:-elf}
+               ;;
+       crisv32-* | etraxfs*-*)
+               cpu=crisv32
+               vendor=axis
+               ;;
+       cris-* | etrax*-*)
+               cpu=cris
+               vendor=axis
+               ;;
+       crx-*)
+               basic_os=${basic_os:-elf}
+               ;;
+       neo-tandem)
+               cpu=neo
+               vendor=tandem
+               ;;
+       nse-tandem)
+               cpu=nse
+               vendor=tandem
+               ;;
+       nsr-tandem)
+               cpu=nsr
+               vendor=tandem
+               ;;
+       nsv-tandem)
+               cpu=nsv
+               vendor=tandem
+               ;;
+       nsx-tandem)
+               cpu=nsx
+               vendor=tandem
+               ;;
+       mipsallegrexel-sony)
+               cpu=mipsallegrexel
+               vendor=sony
+               ;;
+       tile*-*)
+               basic_os=${basic_os:-linux-gnu}
+               ;;
+
+       *)
+               # Recognize the canonical CPU types that are allowed with any
+               # company name.
+               case $cpu in
+                       1750a | 580 \
+                       | a29k \
+                       | aarch64 | aarch64_be \
+                       | abacus \
+                       | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \
+                       | alpha64 | alpha64ev[4-8] | alpha64ev56 | 
alpha64ev6[78] \
+                       | alphapca5[67] | alpha64pca5[67] \
+                       | am33_2.0 \
+                       | amdgcn \
+                       | arc | arceb | arc32 | arc64 \
+                       | arm | arm[lb]e | arme[lb] | armv* \
+                       | avr | avr32 \
+                       | asmjs \
+                       | ba \
+                       | be32 | be64 \
+                       | bfin | bpf | bs2000 \
+                       | c[123]* | c30 | [cjt]90 | c4x \
+                       | c8051 | clipper | craynv | csky | cydra \
+                       | d10v | d30v | dlx | dsp16xx \
+                       | e2k | elxsi | epiphany \
+                       | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \
+                       | h8300 | h8500 \
+                       | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+                       | hexagon \
+                       | i370 | i*86 | i860 | i960 | ia16 | ia64 \
+                       | ip2k | iq2000 \
+                       | k1om \
+                       | le32 | le64 \
+                       | lm32 \
+                       | loongarch32 | loongarch64 | loongarchx32 \
+                       | m32c | m32r | m32rle \
+                       | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | 
m68k \
+                       | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
+                       | m88110 | m88k | maxq | mb | mcore | mep | metag \
+                       | microblaze | microblazeel \
+                       | mips | mipsbe | mipseb | mipsel | mipsle \
+                       | mips16 \
+                       | mips64 | mips64eb | mips64el \
+                       | mips64octeon | mips64octeonel \
+                       | mips64orion | mips64orionel \
+                       | mips64r5900 | mips64r5900el \
+                       | mips64vr | mips64vrel \
+                       | mips64vr4100 | mips64vr4100el \
+                       | mips64vr4300 | mips64vr4300el \
+                       | mips64vr5000 | mips64vr5000el \
+                       | mips64vr5900 | mips64vr5900el \
+                       | mipsisa32 | mipsisa32el \
+                       | mipsisa32r2 | mipsisa32r2el \
+                       | mipsisa32r3 | mipsisa32r3el \
+                       | mipsisa32r5 | mipsisa32r5el \
+                       | mipsisa32r6 | mipsisa32r6el \
+                       | mipsisa64 | mipsisa64el \
+                       | mipsisa64r2 | mipsisa64r2el \
+                       | mipsisa64r3 | mipsisa64r3el \
+                       | mipsisa64r5 | mipsisa64r5el \
+                       | mipsisa64r6 | mipsisa64r6el \
+                       | mipsisa64sb1 | mipsisa64sb1el \
+                       | mipsisa64sr71k | mipsisa64sr71kel \
+                       | mipsr5900 | mipsr5900el \
+                       | mipstx39 | mipstx39el \
+                       | mmix \
+                       | mn10200 | mn10300 \
+                       | moxie \
+                       | mt \
+                       | msp430 \
+                       | nds32 | nds32le | nds32be \
+                       | nfp \
+                       | nios | nios2 | nios2eb | nios2el \
+                       | none | np1 | ns16k | ns32k | nvptx \
+                       | open8 \
+                       | or1k* \
+                       | or32 \
+                       | orion \
+                       | picochip \
+                       | pdp10 | pdp11 | pj | pjl | pn | power \
+                       | powerpc | powerpc64 | powerpc64le | powerpcle | 
powerpcspe \
+                       | pru \
+                       | pyramid \
+                       | riscv | riscv32 | riscv32be | riscv64 | riscv64be \
+                       | rl78 | romp | rs6000 | rx \
+                       | s390 | s390x \
+                       | score \
+                       | sh | shl \
+                       | sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] 
| sh[lb]e \
+                       | sh[1234]e[lb] |  sh[12345][lb]e | sh[23]ele | sh64 | 
sh64le \
+                       | sparc | sparc64 | sparc64b | sparc64v | sparc86x | 
sparclet \
+                       | sparclite \
+                       | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \
+                       | spu \
+                       | tahoe \
+                       | thumbv7* \
+                       | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \
+                       | tron \
+                       | ubicom32 \
+                       | v70 | v850 | v850e | v850e1 | v850es | v850e2 | 
v850e2v3 \
+                       | vax \
+                       | visium \
+                       | w65 \
+                       | wasm32 | wasm64 \
+                       | we32k \
+                       | x86 | x86_64 | xc16x | xgate | xps100 \
+                       | xstormy16 | xtensa* \
+                       | ymp \
+                       | z8k | z80)
+                               ;;
+
+                       *)
+                               echo Invalid configuration \`"$1"\': machine 
\`"$cpu-$vendor"\' not recognized 1>&2
+                               exit 1
+                               ;;
+               esac
+               ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $vendor in
+       digital*)
+               vendor=dec
+               ;;
+       commodore*)
+               vendor=cbm
+               ;;
+       *)
+               ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if test x$basic_os != x
+then
+
+# First recognize some ad-hoc cases, or perhaps split kernel-os, or else just
+# set os.
+case $basic_os in
+       gnu/linux*)
+               kernel=linux
+               os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'`
+               ;;
+       os2-emx)
+               kernel=os2
+               os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'`
+               ;;
+       nto-qnx*)
+               kernel=nto
+               os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'`
+               ;;
+       *-*)
+               # shellcheck disable=SC2162
+               saved_IFS=$IFS
+               IFS="-" read kernel os <<EOF
+$basic_os
+EOF
+               IFS=$saved_IFS
+               ;;
+       # Default OS when just kernel was specified
+       nto*)
+               kernel=nto
+               os=`echo "$basic_os" | sed -e 's|nto|qnx|'`
+               ;;
+       linux*)
+               kernel=linux
+               os=`echo "$basic_os" | sed -e 's|linux|gnu|'`
+               ;;
+       *)
+               kernel=
+               os=$basic_os
+               ;;
+esac
+
+# Now, normalize the OS (knowing we just have one component, it's not a kernel,
+# etc.)
+case $os in
+       # First match some system type aliases that might get confused
+       # with valid system types.
+       # solaris* is a basic system type, with this one exception.
+       auroraux)
+               os=auroraux
+               ;;
+       bluegene*)
+               os=cnk
+               ;;
+       solaris1 | solaris1.*)
+               os=`echo "$os" | sed -e 's|solaris1|sunos4|'`
+               ;;
+       solaris)
+               os=solaris2
+               ;;
+       unixware*)
+               os=sysv4.2uw
+               ;;
+       # es1800 is here to avoid being matched by es* (a different OS)
+       es1800*)
+               os=ose
+               ;;
+       # Some version numbers need modification
+       chorusos*)
+               os=chorusos
+               ;;
+       isc)
+               os=isc2.2
+               ;;
+       sco6)
+               os=sco5v6
+               ;;
+       sco5)
+               os=sco3.2v5
+               ;;
+       sco4)
+               os=sco3.2v4
+               ;;
+       sco3.2.[4-9]*)
+               os=`echo "$os" | sed -e 's/sco3.2./sco3.2v/'`
+               ;;
+       sco*v* | scout)
+               # Don't match below
+               ;;
+       sco*)
+               os=sco3.2v2
+               ;;
+       psos*)
+               os=psos
+               ;;
+       qnx*)
+               os=qnx
+               ;;
+       hiux*)
+               os=hiuxwe2
+               ;;
+       lynx*178)
+               os=lynxos178
+               ;;
+       lynx*5)
+               os=lynxos5
+               ;;
+       lynxos*)
+               # don't get caught up in next wildcard
+               ;;
+       lynx*)
+               os=lynxos
+               ;;
+       mac[0-9]*)
+               os=`echo "$os" | sed -e 's|mac|macos|'`
+               ;;
+       opened*)
+               os=openedition
+               ;;
+       os400*)
+               os=os400
+               ;;
+       sunos5*)
+               os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
+               ;;
+       sunos6*)
+               os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
+               ;;
+       wince*)
+               os=wince
+               ;;
+       utek*)
+               os=bsd
+               ;;
+       dynix*)
+               os=bsd
+               ;;
+       acis*)
+               os=aos
+               ;;
+       atheos*)
+               os=atheos
+               ;;
+       syllable*)
+               os=syllable
+               ;;
+       386bsd)
+               os=bsd
+               ;;
+       ctix* | uts*)
+               os=sysv
+               ;;
+       nova*)
+               os=rtmk-nova
+               ;;
+       ns2)
+               os=nextstep2
+               ;;
+       # Preserve the version number of sinix5.
+       sinix5.*)
+               os=`echo "$os" | sed -e 's|sinix|sysv|'`
+               ;;
+       sinix*)
+               os=sysv4
+               ;;
+       tpf*)
+               os=tpf
+               ;;
+       triton*)
+               os=sysv3
+               ;;
+       oss*)
+               os=sysv3
+               ;;
+       svr4*)
+               os=sysv4
+               ;;
+       svr3)
+               os=sysv3
+               ;;
+       sysvr4)
+               os=sysv4
+               ;;
+       ose*)
+               os=ose
+               ;;
+       *mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+               os=mint
+               ;;
+       dicos*)
+               os=dicos
+               ;;
+       pikeos*)
+               # Until real need of OS specific support for
+               # particular features comes up, bare metal
+               # configurations are quite functional.
+               case $cpu in
+                   arm*)
+                       os=eabi
+                       ;;
+                   *)
+                       os=elf
+                       ;;
+               esac
+               ;;
+       *)
+               # No normalization, but not necessarily accepted, that comes 
below.
+               ;;
+esac
+
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+kernel=
+case $cpu-$vendor in
+       score-*)
+               os=elf
+               ;;
+       spu-*)
+               os=elf
+               ;;
+       *-acorn)
+               os=riscix1.2
+               ;;
+       arm*-rebel)
+               kernel=linux
+               os=gnu
+               ;;
+       arm*-semi)
+               os=aout
+               ;;
+       c4x-* | tic4x-*)
+               os=coff
+               ;;
+       c8051-*)
+               os=elf
+               ;;
+       clipper-intergraph)
+               os=clix
+               ;;
+       hexagon-*)
+               os=elf
+               ;;
+       tic54x-*)
+               os=coff
+               ;;
+       tic55x-*)
+               os=coff
+               ;;
+       tic6x-*)
+               os=coff
+               ;;
+       # This must come before the *-dec entry.
+       pdp10-*)
+               os=tops20
+               ;;
+       pdp11-*)
+               os=none
+               ;;
+       *-dec | vax-*)
+               os=ultrix4.2
+               ;;
+       m68*-apollo)
+               os=domain
+               ;;
+       i386-sun)
+               os=sunos4.0.2
+               ;;
+       m68000-sun)
+               os=sunos3
+               ;;
+       m68*-cisco)
+               os=aout
+               ;;
+       mep-*)
+               os=elf
+               ;;
+       mips*-cisco)
+               os=elf
+               ;;
+       mips*-*)
+               os=elf
+               ;;
+       or32-*)
+               os=coff
+               ;;
+       *-tti)  # must be before sparc entry or we get the wrong os.
+               os=sysv3
+               ;;
+       sparc-* | *-sun)
+               os=sunos4.1.1
+               ;;
+       pru-*)
+               os=elf
+               ;;
+       *-be)
+               os=beos
+               ;;
+       *-ibm)
+               os=aix
+               ;;
+       *-knuth)
+               os=mmixware
+               ;;
+       *-wec)
+               os=proelf
+               ;;
+       *-winbond)
+               os=proelf
+               ;;
+       *-oki)
+               os=proelf
+               ;;
+       *-hp)
+               os=hpux
+               ;;
+       *-hitachi)
+               os=hiux
+               ;;
+       i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+               os=sysv
+               ;;
+       *-cbm)
+               os=amigaos
+               ;;
+       *-dg)
+               os=dgux
+               ;;
+       *-dolphin)
+               os=sysv3
+               ;;
+       m68k-ccur)
+               os=rtu
+               ;;
+       m88k-omron*)
+               os=luna
+               ;;
+       *-next)
+               os=nextstep
+               ;;
+       *-sequent)
+               os=ptx
+               ;;
+       *-crds)
+               os=unos
+               ;;
+       *-ns)
+               os=genix
+               ;;
+       i370-*)
+               os=mvs
+               ;;
+       *-gould)
+               os=sysv
+               ;;
+       *-highlevel)
+               os=bsd
+               ;;
+       *-encore)
+               os=bsd
+               ;;
+       *-sgi)
+               os=irix
+               ;;
+       *-siemens)
+               os=sysv4
+               ;;
+       *-masscomp)
+               os=rtu
+               ;;
+       f30[01]-fujitsu | f700-fujitsu)
+               os=uxpv
+               ;;
+       *-rom68k)
+               os=coff
+               ;;
+       *-*bug)
+               os=coff
+               ;;
+       *-apple)
+               os=macos
+               ;;
+       *-atari*)
+               os=mint
+               ;;
+       *-wrs)
+               os=vxworks
+               ;;
+       *)
+               os=none
+               ;;
+esac
+
+fi
+
+# Now, validate our (potentially fixed-up) OS.
+case $os in
+       # Sometimes we do "kernel-libc", so those need to count as OSes.
+       musl* | newlib* | relibc* | uclibc*)
+               ;;
+       # Likewise for "kernel-abi"
+       eabi* | gnueabi*)
+               ;;
+       # VxWorks passes extra cpu info in the 4th filed.
+       simlinux | simwindows | spe)
+               ;;
+       # Now accept the basic system types.
+       # The portable systems comes first.
+       # Each alternative MUST end in a * to match a version number.
+       gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
+            | *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \
+            | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
+            | sym* |  plan9* | psp* | sim* | xray* | os68k* | v88r* \
+            | hiux* | abug | nacl* | netware* | windows* \
+            | os9* | macos* | osx* | ios* \
+            | mpw* | magic* | mmixware* | mon960* | lnews* \
+            | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
+            | aos* | aros* | cloudabi* | sortix* | twizzler* \
+            | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
+            | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
+            | mirbsd* | netbsd* | dicos* | openedition* | ose* \
+            | bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \
+            | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \
+            | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
+            | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
+            | udi* | lites* | ieee* | go32* | aux* | hcos* \
+            | chorusrdb* | cegcc* | glidix* | serenity* \
+            | cygwin* | msys* | pe* | moss* | proelf* | rtems* \
+            | midipix* | mingw32* | mingw64* | mint* \
+            | uxpv* | beos* | mpeix* | udk* | moxiebox* \
+            | interix* | uwin* | mks* | rhapsody* | darwin* \
+            | openstep* | oskit* | conix* | pw32* | nonstopux* \
+            | storm-chaos* | tops10* | tenex* | tops20* | its* \
+            | os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \
+            | scout* | superux* | sysv* | rtmk* | tpf* | windiss* \
+            | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \
+            | skyos* | haiku* | rdos* | toppers* | drops* | es* \
+            | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
+            | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
+            | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \
+            | fiwix* )
+               ;;
+       # This one is extra strict with allowed versions
+       sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
+               # Don't forget version if it is 3.2v4 or newer.
+               ;;
+       none)
+               ;;
+       *)
+               echo Invalid configuration \`"$1"\': OS \`"$os"\' not 
recognized 1>&2
+               exit 1
+               ;;
+esac
+
+# As a final step for OS-related things, validate the OS-kernel combination
+# (given a valid OS), if there is a kernel.
+case $kernel-$os in
+       linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \
+                  | linux-musl* | linux-relibc* | linux-uclibc* )
+               ;;
+       uclinux-uclibc* )
+               ;;
+       -dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* )
+               # These are just libc implementations, not actual OSes, and thus
+               # require a kernel.
+               echo "Invalid configuration \`$1': libc \`$os' needs explicit 
kernel." 1>&2
+               exit 1
+               ;;
+       kfreebsd*-gnu* | kopensolaris*-gnu*)
+               ;;
+       vxworks-simlinux | vxworks-simwindows | vxworks-spe)
+               ;;
+       nto-qnx*)
+               ;;
+       os2-emx)
+               ;;
+       *-eabi* | *-gnueabi*)
+               ;;
+       -*)
+               # Blank kernel with real OS is always fine.
+               ;;
+       *-*)
+               echo "Invalid configuration \`$1': Kernel \`$kernel' not known 
to work with OS \`$os'." 1>&2
+               exit 1
+               ;;
+esac
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+case $vendor in
+       unknown)
+               case $cpu-$os in
+                       *-riscix*)
+                               vendor=acorn
+                               ;;
+                       *-sunos*)
+                               vendor=sun
+                               ;;
+                       *-cnk* | *-aix*)
+                               vendor=ibm
+                               ;;
+                       *-beos*)
+                               vendor=be
+                               ;;
+                       *-hpux*)
+                               vendor=hp
+                               ;;
+                       *-mpeix*)
+                               vendor=hp
+                               ;;
+                       *-hiux*)
+                               vendor=hitachi
+                               ;;
+                       *-unos*)
+                               vendor=crds
+                               ;;
+                       *-dgux*)
+                               vendor=dg
+                               ;;
+                       *-luna*)
+                               vendor=omron
+                               ;;
+                       *-genix*)
+                               vendor=ns
+                               ;;
+                       *-clix*)
+                               vendor=intergraph
+                               ;;
+                       *-mvs* | *-opened*)
+                               vendor=ibm
+                               ;;
+                       *-os400*)
+                               vendor=ibm
+                               ;;
+                       s390-* | s390x-*)
+                               vendor=ibm
+                               ;;
+                       *-ptx*)
+                               vendor=sequent
+                               ;;
+                       *-tpf*)
+                               vendor=ibm
+                               ;;
+                       *-vxsim* | *-vxworks* | *-windiss*)
+                               vendor=wrs
+                               ;;
+                       *-aux*)
+                               vendor=apple
+                               ;;
+                       *-hms*)
+                               vendor=hitachi
+                               ;;
+                       *-mpw* | *-macos*)
+                               vendor=apple
+                               ;;
+                       *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*)
+                               vendor=atari
+                               ;;
+                       *-vos*)
+                               vendor=stratus
+                               ;;
+               esac
+               ;;
+esac
+
+echo "$cpu-$vendor-${kernel:+$kernel-}$os"
+exit
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/exec/configure.ac b/exec/configure.ac
new file mode 100644
index 00000000000..e78d8ebea90
--- /dev/null
+++ b/exec/configure.ac
@@ -0,0 +1,537 @@
+dnl  Autoconf script for GNU Emacs's exec library.
+dnl To rebuild the 'configure' script from this, execute the command
+dnl    autoconf
+dnl in the directory containing this script.
+dnl If you changed any AC_DEFINES, also run autoheader.
+dnl
+dnl Copyright (C) 2023 Free Software Foundation, Inc.
+dnl
+dnl  This file is part of GNU Emacs.
+dnl
+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
+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
+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/>.
+
+AC_PREREQ([2.65])
+AC_INIT([libexec], [30.0.50], [bug-gnu-emacs@gnu.org], [],
+  [https://www.gnu.org/software/emacs/])
+
+AH_TOP([/* 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/>. */])
+
+AC_ARG_WITH([reentrancy],
+  [AS_HELP_STRING([--with-reentrancy],
+    [Generate library which can be used within a signal handler.])],
+  [AC_DEFINE([REENTRANT], [1])])
+
+AC_USE_SYSTEM_EXTENSIONS
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_INSTALL
+
+AC_TYPE_UINT8_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
+AC_TYPE_UINTPTR_T
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_TYPE_PID_T
+
+AC_HEADER_STDBOOL
+AC_CHECK_FUNCS([getpagesize stpcpy stpncpy])
+AC_CHECK_DECLS([stpcpy, stpncpy])
+AC_CHECK_FUNC([process_vm_readv],
+  [AC_CHECK_FUNC([process_vm_writev],
+    [AC_CHECK_DECL([process_vm_readv],
+      [AC_DEFINE([HAVE_PROCESS_VM], [1],
+        [Define to 1 if process_vm_readv is available.])],
+      [], [[
+#include <sys/uio.h>
+      ]])])])
+AC_CHECK_HEADERS([sys/param.h sys/uio.h])
+AC_CHECK_MEMBERS([siginfo_t.si_syscall], [], [],
+  [[
+#include <signal.h>
+  ]])
+
+AH_BOTTOM([
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# ifndef HAVE__BOOL
+#  ifdef __cplusplus
+typedef bool _Bool;
+#  else
+#   define _Bool signed char
+#  endif
+# endif
+# define bool _Bool
+# define false 0
+# define true 1
+# define __bool_true_false_are_defined 1
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif /* HAVE_SYS_PARAM_H */
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif /* MAX */
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif /* MIN */
+])
+
+AC_C_BIGENDIAN
+
+AH_TEMPLATE([SYSCALL_HEADER], [Define to header holding system call numbers.])
+AH_TEMPLATE([USER_HEADER], [Define to header holding USER_REGS_STRUCT.])
+AH_TEMPLATE([USER_REGS_STRUCT], [Define to structure holding user registers.])
+AH_TEMPLATE([SYSCALL_NUM_REG], [Define to register holding the system call 
number.])
+AH_TEMPLATE([SYSCALL_ARG_REG], [Define to register holding arg0 to system 
calls.])
+AH_TEMPLATE([SYSCALL_ARG1_REG], [Define to register holding arg1 to system 
calls.])
+AH_TEMPLATE([SYSCALL_ARG2_REG], [Define to register holding arg2 to system 
calls.])
+AH_TEMPLATE([SYSCALL_ARG3_REG], [Define to register holding arg3 to system 
calls.])
+AH_TEMPLATE([SYSCALL_RET_REG], [Define to register holding value of system 
calls.])
+AH_TEMPLATE([STACK_POINTER], [Define to register holding the stack pointer.])
+AH_TEMPLATE([EXEC_SYSCALL], [Define to number of the `exec' system call.])
+AH_TEMPLATE([USER_WORD], [Define to word type used by tracees.])
+AH_TEMPLATE([EXEC_64], [Define to 1 if the system utilizes 64-bit ELF.])
+AH_TEMPLATE([STACK_GROWS_DOWNWARDS], [Define to 1 if the stack grows 
downwards.])
+AH_TEMPLATE([ABI_RED_ZONE], [Define to number of reserved bytes past the stack 
frame.])
+AH_TEMPLATE([EXECUTABLE_BASE], [Virtual address for loading PIC executables])
+AH_TEMPLATE([INTERPRETER_BASE], [Virtual address for loading PIC interpreters])
+AH_TEMPLATE([CLONE_SYSCALL], [Define to number of the `clone' system call.])
+AH_TEMPLATE([CLONE3_SYSCALL], [Define to number of the `clone3' system call.])
+AH_TEMPLATE([READLINK_SYSCALL], [Define to number of the `readlink' system 
call.])
+AH_TEMPLATE([READLINKAT_SYSCALL], [Define to number of the `readlinkat' system 
call.])
+AH_TEMPLATE([REENTRANT], [Define to 1 if the library is used within a signal 
handler.])
+
+AC_CANONICAL_HOST
+
+# Check whether or not sys/user exists.  If it doesn't, try
+# asm/user.h, and croak if that doesn't exist either.
+AS_CASE([$host], [*mips*], [], [*],
+  [AC_CHECK_HEADER([sys/user.h], [user_h="<sys/user.h>"],
+    [AC_CHECK_HEADER([asm/user.h], [user_h="<asm/user.h>"],
+      [AC_MSG_ERROR([Can not find working user.h])])])])
+
+# Look for required tools.
+
+AC_ARG_VAR([M4], [`m4' preprocessor command.])
+AC_ARG_VAR([AS], [`as' assembler command.])
+AC_ARG_VAR([LD], [`ld' linker command.])
+
+# Check for a working m4.
+AC_CHECK_PROGS([M4], [gm4 m4],
+  [AC_MSG_ERROR([Cannot find m4])])
+
+# Check for a working assembler.
+AC_CHECK_TOOL([AS], [as],
+  [AC_MSG_ERROR([Cannot find a working assembler])])
+
+# And ar.
+AC_CHECK_TOOL([AR], [ar],
+  [AC_MSG_ERROR([Cannot find a working ar])])
+
+# And ld.
+AC_CHECK_TOOL([LD], [ld],
+  [AC_MSG_ERROR([Cannot find a working linker])])
+
+# Now check if ld is a C compiler.
+LDPREFIX=
+AC_CACHE_CHECK([whether ld is a C compiler],
+  [exec_cv_ld_is_cc],
+  [cat <<_ACEOF > conftest.c
+AC_LANG_PROGRAM(,)
+_ACEOF
+   exec_cv_ld_is_cc=yes
+   $LD -c conftest.c -o conftest.$OBJEXT >&AS_MESSAGE_LOG_FD 2>&1 \
+   || exec_cv_ld_is_cc=no
+   rm -f conftest.c conftest.$OBJEXT])
+
+# And if as is a C compiler.
+AC_CACHE_CHECK([whether as is a C compiler],
+  [exec_cv_as_is_cc],
+  [cat <<_ACEOF > conftest.c
+AC_LANG_PROGRAM(,)
+_ACEOF
+   exec_cv_as_is_cc=yes
+   $AS -c conftest.c -o conftest.$OBJEXT >&AS_MESSAGE_LOG_FD 2>&1 \
+   || exec_cv_as_is_cc=no
+   rm -f conftest.c conftest.$OBJEXT])
+
+# If ld is a C compiler, pass `-nostdlib', `-nostartfiles', and
+# `-static'.  Also, set LDPREFIX to -Wl,
+AS_IF([test "x$exec_cv_ld_is_cc" = "xyes"],
+  [LOADERFLAGS="$LOADERFLAGS -nostdlib -nostartfiles -static"
+   LDPREFIX=-Wl,])
+
+# If as is a C compiler, add `-c' to ASFLAGS.
+AS_IF([test "x$exec_cv_as_is_cc" = "xyes"],
+  [ASFLAGS="$ASFLAGS -c"])
+
+AC_DEFUN([exec_CHECK_LINUX_CLONE3],
+[
+AC_CHECK_DECL([__NR_clone3],
+  [AC_DEFINE([CLONE3_SYSCALL], [__NR_clone3])],
+  [], [[
+#include <asm/unistd.h>
+]])
+])
+
+AC_DEFUN([exec_CHECK_MIPS_NABI],
+[
+AC_CACHE_CHECK([whether MIPS NABI calling convention is used],
+  [exec_cv_mips_nabi],
+  [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sgidefs.h>
+]], [[
+#ifndef  __mips64__
+#if _MIPS_SIM == _ABIO32
+OABI in use.
+#endif /* _MIPS_SIM == _ABIO32 */
+#endif /* !__mips64__ */
+]])], [exec_cv_mips_nabi=yes],
+  [exec_cv_mips_nabi=no])])
+
+dnl mips64 systems use N64 calling convention, a variant of nabi
+dnl calling convention.
+AS_IF([test "x$exec_cv_mips_nabi" != "xno"],
+  [AC_DEFINE([MIPS_NABI], [1],
+    [Define to 1 if MIPS NABI calling convention is being used.])],
+   [OBJS="$OBJS mipsfpu.o"])
+])
+
+# Determine the system type and define appropriate macros.
+exec_loader=
+is_mips=
+OBJS="exec.o trace.o"
+DADDI_BROKEN=no
+
+AS_CASE([$host], [x86_64-*linux*],
+  [AC_CHECK_MEMBER([struct user_regs_struct.rdi],
+    [AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
+     AC_DEFINE_UNQUOTED([USER_HEADER], [$user_h])
+     AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct])
+     AC_DEFINE([SYSCALL_NUM_REG], [orig_rax])
+     AC_DEFINE([SYSCALL_RET_REG], [rax])
+     AC_DEFINE([SYSCALL_ARG_REG], [rdi])
+     AC_DEFINE([SYSCALL_ARG1_REG], [rsi])
+     AC_DEFINE([SYSCALL_ARG2_REG], [rdx])
+     AC_DEFINE([SYSCALL_ARG3_REG], [r10])
+     AC_DEFINE([STACK_POINTER], [rsp])
+     AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
+     AC_DEFINE([USER_WORD], [uintptr_t])
+     AC_DEFINE([EXEC_64], [1])
+     AC_DEFINE([ABI_RED_ZONE], [128])
+     AC_DEFINE([EXECUTABLE_BASE], [0x555555554000])
+     AC_DEFINE([INTERPRETER_BASE], [0x600000000000])
+     AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
+     AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+     AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+     AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+     exec_CHECK_LINUX_CLONE3
+     # Make sure the loader doesn't conflict with other position
+     # dependent code.
+     LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x200000000000"
+     exec_loader=loader-x86_64.s],
+    [AC_MSG_ERROR([Missing `rdi' in user_regs_struct])],
+    [[
+#include $user_h
+    ]])], [i[[34567]]86-*linux*],
+  [AC_CHECK_MEMBER([struct user_regs_struct.edi],
+    [AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
+     AC_DEFINE_UNQUOTED([USER_HEADER], [$user_h])
+     AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct])
+     AC_DEFINE([SYSCALL_NUM_REG], [orig_eax])
+     AC_DEFINE([SYSCALL_RET_REG], [eax])
+     AC_DEFINE([SYSCALL_ARG_REG], [ebx])
+     AC_DEFINE([SYSCALL_ARG1_REG], [ecx])
+     AC_DEFINE([SYSCALL_ARG2_REG], [edx])
+     AC_DEFINE([SYSCALL_ARG3_REG], [esi])
+     AC_DEFINE([STACK_POINTER], [esp])
+     AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
+     AC_DEFINE([USER_WORD], [uintptr_t])
+     AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
+     AC_DEFINE([INTERPRETER_BASE], [0xaf000000])
+     AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
+     AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+     AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+     AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+     exec_CHECK_LINUX_CLONE3
+     # Make sure the loader doesn't conflict with other position
+     # dependent code.
+     LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0xa0000000"
+     exec_loader=loader-x86.s],
+    [AC_MSG_ERROR([Missing `edi' in user_regs_struct])],
+    [[
+#include $user_h
+    ]])], [aarch64-*linux*],
+  [AC_CHECK_MEMBER([struct user_regs_struct.sp],
+    [AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
+     AC_DEFINE_UNQUOTED([USER_HEADER], [$user_h])
+     AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct])
+     AC_DEFINE([SYSCALL_NUM_REG], [[regs[8]]])
+     AC_DEFINE([SYSCALL_RET_REG], [[regs[0]]])
+     AC_DEFINE([SYSCALL_ARG_REG], [[regs[0]]])
+     AC_DEFINE([SYSCALL_ARG1_REG], [[regs[1]]])
+     AC_DEFINE([SYSCALL_ARG2_REG], [[regs[2]]])
+     AC_DEFINE([SYSCALL_ARG3_REG], [[regs[3]]])
+     AC_DEFINE([STACK_POINTER], [sp])
+     AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
+     AC_DEFINE([USER_WORD], [uintptr_t])
+     AC_DEFINE([EXEC_64], [1])
+     AC_DEFINE([EXECUTABLE_BASE], [0x3000000000])
+     AC_DEFINE([INTERPRETER_BASE], [0x3f00000000])
+     AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
+     AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+     # Note that aarch64 has no `readlink'.
+     AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+     exec_CHECK_LINUX_CLONE3
+     # Make sure the loader doesn't conflict with other position
+     # dependent code.  ARM places rather significant restrictions on
+     # virtual addresses for a 64 bit architecture.
+     LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x2000000000"
+     exec_loader=loader-aarch64.s],
+    [AC_MSG_ERROR([Missing `sp' in user_regs_struct])],
+    [[
+#include $user_h
+    ]])], [arm*linux*eabi* | armv7*linux*],
+  [AC_CHECK_MEMBER([struct user_regs.uregs],
+    [AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
+     AC_DEFINE_UNQUOTED([USER_HEADER], [$user_h])
+     AC_DEFINE([USER_REGS_STRUCT], [struct user_regs])
+     AC_DEFINE([SYSCALL_NUM_REG], [[uregs[7]]])
+     AC_DEFINE([SYSCALL_RET_REG], [[uregs[0]]])
+     AC_DEFINE([SYSCALL_ARG_REG], [[uregs[0]]])
+     AC_DEFINE([SYSCALL_ARG1_REG], [[uregs[1]]])
+     AC_DEFINE([SYSCALL_ARG2_REG], [[uregs[2]]])
+     AC_DEFINE([SYSCALL_ARG3_REG], [[uregs[3]]])
+     AC_DEFINE([STACK_POINTER], [[uregs[13]]])
+     AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
+     AC_DEFINE([USER_WORD], [uintptr_t])
+     AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
+     AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
+     AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
+     AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+     AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+     AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+     exec_CHECK_LINUX_CLONE3
+     LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000"
+     exec_loader=loader-armeabi.s],
+    [AC_CHECK_MEMBER([struct pt_regs.uregs],
+      [AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
+       AC_DEFINE_UNQUOTED([USER_HEADER], [<asm/ptrace.h>])
+       AC_DEFINE([USER_REGS_STRUCT], [struct pt_regs])
+       AC_DEFINE([SYSCALL_NUM_REG], [[uregs[7]]])
+       AC_DEFINE([SYSCALL_RET_REG], [[uregs[0]]])
+       AC_DEFINE([SYSCALL_ARG_REG], [[uregs[0]]])
+       AC_DEFINE([SYSCALL_ARG1_REG], [[uregs[1]]])
+       AC_DEFINE([SYSCALL_ARG2_REG], [[uregs[2]]])
+       AC_DEFINE([SYSCALL_ARG3_REG], [[uregs[3]]])
+       AC_DEFINE([STACK_POINTER], [[uregs[13]]])
+       AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
+       AC_DEFINE([USER_WORD], [uintptr_t])
+       AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
+       AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
+       AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
+       AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+       AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+       AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+       exec_CHECK_LINUX_CLONE3
+       LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000"
+       exec_loader=loader-armeabi.s],
+      [AC_MSG_ERROR([Missing `uregs' in user_regs_struct or pt_regs])],
+      [[
+#include <asm/ptrace.h>
+      ]])],
+    [[
+#include $user_h
+    ]])], [mipsel*linux*],
+  [AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
+   AC_DEFINE([USER_HEADER], ["mipsel-user.h"])
+   AC_DEFINE([USER_REGS_STRUCT], [struct mipsel_regs])
+   AC_DEFINE([SYSCALL_NUM_REG], [[gregs[2]]]) # v0
+   AC_DEFINE([SYSCALL_RET_REG], [[gregs[4]]]) # a0
+   AC_DEFINE([SYSCALL_ARG_REG], [[gregs[4]]]) # a0
+   AC_DEFINE([SYSCALL_ARG1_REG], [[gregs[5]]]) # a1
+   AC_DEFINE([SYSCALL_ARG2_REG], [[gregs[4]]]) # a2
+   AC_DEFINE([SYSCALL_ARG3_REG], [[gregs[5]]]) # a3
+   AC_DEFINE([STACK_POINTER], [[gregs[29]]]) # sp
+   AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
+   AC_DEFINE([USER_WORD], [uintptr_t])
+   AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
+   AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
+   AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
+   AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+   AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+   AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+   AC_CHECK_DECL([_MIPS_SIM], [exec_CHECK_MIPS_NABI],
+     [AC_MSG_ERROR([_MIPS_SIM could not be determined]),
+     [[
+#include <sgidefs.h>
+]]])
+   exec_CHECK_LINUX_CLONE3
+   LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000"
+   is_mips=yes
+   exec_loader=loader-mipsel.s], [mips64el*linux*],
+  [AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
+   AC_DEFINE([USER_HEADER], ["mipsel-user.h"])
+   AC_DEFINE([USER_REGS_STRUCT], [struct mipsel_regs])
+   AC_DEFINE([SYSCALL_NUM_REG], [[gregs[2]]]) # v0
+   AC_DEFINE([SYSCALL_RET_REG], [[gregs[4]]]) # a0
+   AC_DEFINE([SYSCALL_ARG_REG], [[gregs[4]]]) # a0
+   AC_DEFINE([SYSCALL_ARG1_REG], [[gregs[5]]]) # a1
+   AC_DEFINE([SYSCALL_ARG2_REG], [[gregs[4]]]) # a2
+   AC_DEFINE([SYSCALL_ARG3_REG], [[gregs[5]]]) # a3
+   AC_DEFINE([STACK_POINTER], [[gregs[29]]]) # sp
+   AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
+   AC_DEFINE([USER_WORD], [uintptr_t])
+   AC_DEFINE([EXEC_64], [1])
+   AC_DEFINE([EXECUTABLE_BASE], [0x400000])
+   AC_DEFINE([INTERPRETER_BASE], [0x3f00000000])
+   AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
+   AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+   AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+   AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+   AC_CACHE_CHECK([whether as understands `daddi'],
+     [exec_cv_as_daddi],
+     [exec_cv_as_daddi=no
+      cat <<_ACEOF >conftest.s
+      .section text
+      .global __start
+__start:
+       li      $t0, 0
+       li      $t1, 0
+       daddi   $t0, $t1, 1
+       daddi   $t0, $t1, -1
+       daddi   $t0, -1
+       daddi   $t0, 1
+
+_ACEOF
+      $AS $ASFLAGS conftest.s -o conftest.$OBJEXT \
+       >&AS_MESSAGE_LOG_FD 2>&1                  \
+       && exec_cv_as_daddi=yes
+      rm -f conftest.s conftest.$OBJEXT])
+   AS_IF([test "x$exec_cv_as_daddi" != "xyes"],
+     [DADDI_BROKEN=yes])
+   exec_CHECK_LINUX_CLONE3
+   exec_CHECK_MIPS_NABI
+   LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x3e00000000"
+   is_mips=yes
+   exec_loader=loader-mips64el.s], [*],
+  [AC_MSG_ERROR([Please port libexec to $host])])
+
+AC_SUBST([DADDI_BROKEN])
+
+MIPS_N32=$exec_cv_mips_nabi
+
+AC_ARG_VAR([LOADERFLAGS], [Flags used to link the loader.])
+AC_ARG_VAR([ARFLAGS], [Flags for the archiver.])
+AC_ARG_VAR([ASFLAGS], [Flags for the assembler.])
+
+# Make the assembler optimize for code size.  Don't do this on MIPS,
+# as the assembler code manages branch delays manually.
+
+AC_CACHE_CHECK([whether as understands -O],
+  [exec_cv_as_O],
+  [exec_cv_as_O=no
+   cat <<_ACEOF >conftest.s
+   .section text
+   .global _start
+_start:
+
+_ACEOF
+   $AS $ASFLAGS -O conftest.s -o conftest.$OBJEXT \
+     >&AS_MESSAGE_LOG_FD 2>&1                    \
+     && exec_cv_as_O=yes
+   rm -f conftest.s conftest.$OBJEXT])
+
+AS_IF([test "$exec_cv_as_O" = "yes" \
+       && test "$is_mips" != "yes"],
+  [ASFLAGS="$ASFLAGS -O"])
+
+# Make the assembler generate debug information.
+
+AC_CACHE_CHECK([whether as understands -g],
+  [exec_cv_as_g],
+  [exec_cv_as_g=no
+   cat <<_ACEOF >conftest.s
+   .section text
+   .global _start
+_start:
+
+_ACEOF
+   $AS $ASFLAGS -g conftest.s -o conftest.$OBJEXT \
+     >&AS_MESSAGE_LOG_FD 2>&1                    \
+     && exec_cv_as_g=yes
+   rm -f conftest.s conftest.$OBJEXT])
+AS_IF([test "$exec_cv_as_g" = "yes"], [ASFLAGS="$ASFLAGS -g"])
+
+# Check for the ability to automatically generate dependencies for C
+# source files.
+AUTO_DEPEND=no
+AS_IF([test "x$GCC" = xyes],
+  [AC_CACHE_CHECK([whether gcc understands -MMD -MF],
+     [exec_cv_autodepend],
+     [SAVE_CFLAGS="$CFLAGS"
+      CFLAGS="$CFLAGS -MMD -MF deps.d -MP"
+      AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
+       [exec_cv_autodepend=yes],
+       [exec_cv_autodepend=no])
+     CFLAGS="$SAVE_CFLAGS"
+     test -f deps.d || emacs_cv_autodepend=no
+     rm -rf deps.d])
+   AS_IF([test "x$exec_cv_autodepend" = xyes],
+     [AUTO_DEPEND=yes
+      AS_MKDIR_P([deps])])])
+
+# Now check for some other stuff.
+
+AC_CACHE_CHECK([for 'find' args to delete a file],
+  [exec_cv_find_delete],
+  [AS_IF([touch conftest.tmp && find conftest.tmp -delete 2>/dev/null &&
+      test ! -f conftest.tmp], [exec_cv_find_delete="-delete"],
+   [exec_cv_find_delete="-exec rm -f {} ';'"])])
+FIND_DELETE=$exec_cv_find_delete
+AC_SUBST([FIND_DELETE])
+
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_FILES([Makefile config-mips.m4])
+
+AC_SUBST([AUTO_DEPEND])
+AC_SUBST([LOADERFLAGS])
+AC_SUBST([ARFLAGS])
+AC_SUBST([ASFLAGS])
+AC_SUBST([exec_loader])
+AC_SUBST([MIPS_N32])
+AC_SUBST([OBJS])
+
+AC_OUTPUT
diff --git a/exec/deps.mk b/exec/deps.mk
new file mode 100644
index 00000000000..20fcd2dbc5a
--- /dev/null
+++ b/exec/deps.mk
@@ -0,0 +1,21 @@
+### deps.mk
+
+## 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/>.
+
+exec.o: exec.h config.h
+trace.o: exec.h config.h
diff --git a/exec/exec.c b/exec/exec.c
new file mode 100644
index 00000000000..0d9187cabfa
--- /dev/null
+++ b/exec/exec.c
@@ -0,0 +1,1235 @@
+/* Program execution for Emacs.
+
+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 <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <sys/ptrace.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include "exec.h"
+
+#if defined __mips__ && !defined MIPS_NABI
+#include "mipsfpu.h"
+#endif /* defined __mips__ && !defined MIPS_NABI */
+
+
+
+
+/* Define replacements for required string functions.  */
+
+#if !defined HAVE_STPCPY || !defined HAVE_DECL_STPCPY
+
+/* Copy SRC to DEST, returning the address of the terminating '\0' in
+   DEST.  */
+
+static char *
+rpl_stpcpy (char *dest, const char *src)
+{
+  register char *d;
+  register const char *s;
+
+  d = dest;
+  s = src;
+
+  do
+    *d++ = *s;
+  while (*s++ != '\0');
+
+  return d - 1;
+}
+
+#define stpcpy rpl_stpcpy
+#endif /* !defined HAVE_STPCPY || !defined HAVE_DECL_STPCPY */
+
+#if !defined HAVE_STPNCPY || !defined HAVE_DECL_STPNCPY
+
+/* Copy no more than N bytes of SRC to DST, returning a pointer past
+   the last non-NUL byte written into DST.  */
+
+static char *
+rpl_stpncpy (char *dest, const char *src, size_t n)
+{
+  char c, *s;
+  size_t n4;
+
+  s = dest;
+
+  if (n >= 4)
+    {
+      n4 = n >> 2;
+
+      for (;;)
+       {
+         c = *src++;
+         *dest++ = c;
+         if (c == '\0')
+           break;
+         c = *src++;
+         *dest++ = c;
+         if (c == '\0')
+           break;
+         c = *src++;
+         *dest++ = c;
+         if (c == '\0')
+           break;
+         c = *src++;
+         *dest++ = c;
+         if (c == '\0')
+           break;
+         if (--n4 == 0)
+           goto last_chars;
+       }
+      n -= dest - s;
+      goto zero_fill;
+    }
+
+ last_chars:
+  n &= 3;
+  if (n == 0)
+    return dest;
+
+  for (;;)
+    {
+      c = *src++;
+      --n;
+      *dest++ = c;
+      if (c == '\0')
+       break;
+      if (n == 0)
+       return dest;
+    }
+
+ zero_fill:
+  while (n-- > 0)
+    dest[n] = '\0';
+
+  return dest - 1;
+}
+
+#define stpncpy rpl_stpncpy
+#endif /* !defined HAVE_STPNCPY || !defined HAVE_DECL_STPNCPY */
+
+
+
+/* Executable reading functions.
+   These functions extract information from an executable that is
+   about to be loaded.
+
+   `exec_0' takes the name of the program, determines whether or not
+   its format is correct, and if so, returns the list of actions that
+   the loader should perform.
+
+   The actions include:
+
+     - Making the stack executable, if PT_GNU_STACK.
+     - Mapping PT_LOAD sections into the executable with the correct
+       memory protection.
+     - On MIPS, setting the floating point register size.
+     - Transferring control to the interpreter or executable.  */
+
+
+/* Check whether or not FD starts with a #!, and return the executable
+   to load if it does.  Value is NAME if no interpreter character was
+   found, or the interpreter otherwise.  Value is NULL upon an IO
+   error.
+
+   If an additional command line argument is specified, place it in
+   *EXTRA.  */
+
+static const char *
+check_interpreter (const char *name, int fd, const char **extra)
+{
+  static char buffer[PATH_MAX], *start;
+  char first[2], *end, *ws;
+  ssize_t rc;
+
+  /* Read the first character.  */
+  rc = read (fd, &first, 2);
+
+  if (rc != 2)
+    goto fail;
+
+  if (first[0] != '#' || first[1] != '!')
+    goto nomatch;
+
+  rc = read (fd, buffer, PATH_MAX);
+
+  if (rc < 0)
+    goto fail;
+
+  /* Strip leading whitespace.  */
+  start = buffer;
+  while (*start && ((unsigned char) *start) < 128 && isspace (*start))
+    ++start;
+
+  /* Look for a newline character.  */
+  end = memchr (start, '\n', rc);
+
+  if (!end)
+    goto fail;
+
+  /* The string containing the interpreter is now in start.  NULL
+     terminate it.  */
+  *end = '\0';
+
+  /* Now look for any whitespace characters.  */
+  ws = strchr (start, ' ');
+
+  /* If there's no whitespace, return the entire start.  */
+
+  if (!ws)
+    {
+      if (lseek (fd, 0, SEEK_SET))
+       goto fail;
+
+      return start;
+    }
+
+  /* Otherwise, split the string at the whitespace and return the
+     additional argument.  */
+  *ws = '\0';
+
+  if (lseek (fd, 0, SEEK_SET))
+    goto fail;
+
+  *extra = ws + 1;
+  return start;
+
+ nomatch:
+  /* There's no interpreter.  */
+  if (lseek (fd, 0, SEEK_SET))
+    goto fail;
+
+  return name;
+
+ fail:
+  errno = ENOEXEC;
+  return NULL;
+}
+
+/* Static area used to store data placed on the loader's stack.  */
+static char loader_area[65536];
+
+/* Number of bytes used in that area.  */
+static int loader_area_used;
+
+
+
+/* Structure definitions for commands placed in the loader area.
+   Arrange these so that each member is naturally aligned.  */
+
+struct exec_open_command
+{
+  /* Word identifying the type of this command.  */
+  USER_WORD command;
+
+  /* NULL-terminated file name follows, padded to the size of a user
+     word.  */
+};
+
+struct exec_map_command
+{
+  /* Word identifying the type of this command.  */
+  USER_WORD command;
+
+  /* Where the file will be mapped.  */
+  USER_WORD vm_address;
+
+  /* Offset into the file to map from.  */
+  USER_WORD file_offset;
+
+  /* Memory protection for mprotect.  */
+  USER_WORD protection;
+
+  /* Number of bytes to be mapped.  */
+  USER_WORD length;
+
+  /* Flags for mmap.  */
+  USER_WORD flags;
+
+  /* Number of bytes to clear at the end of this mapping.  */
+  USER_WORD clear;
+};
+
+struct exec_jump_command
+{
+  /* Word identifying the type of this command.  */
+  USER_WORD command;
+
+  /* Address to jump to.  */
+  USER_WORD entry;
+
+  /* The value of AT_ENTRY inside the aux vector.  */
+  USER_WORD at_entry;
+
+  /* The value of AT_PHENT inside the aux vector.  */
+  USER_WORD at_phent;
+
+  /* The value of AT_PHNUM inside the aux vector.  */
+  USER_WORD at_phnum;
+
+  /* The value of AT_PHDR inside the aux vector.  */
+  USER_WORD at_phdr;
+
+  /* The value of AT_BASE inside the aux vector.  */
+  USER_WORD at_base;
+
+#if defined __mips__ && !defined MIPS_NABI
+  /* The FPU mode to apply.  */
+  USER_WORD fpu_mode;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+};
+
+
+
+/* Write a command to open the file NAME to the loader area.
+   If ALTERNATE is true, then use the command code 16 instead
+   of 0.  Value is 1 upon failure, else 0.  */
+
+static int
+write_open_command (const char *name, bool alternate)
+{
+  struct exec_open_command command;
+  size_t size;
+
+  /* First, write the command to open NAME.  This is followed by NAME
+     itself, padded to sizeof (USER_WORD) bytes.  */
+
+  command.command = alternate ? 16 : 0;
+  if (sizeof loader_area - loader_area_used < sizeof command)
+    return 1;
+  memcpy (loader_area + loader_area_used, &command, sizeof command);
+  loader_area_used += sizeof command;
+
+  /* Calculate the length of NAME.  */
+  size = strlen (name) + 1;
+
+  /* Round it up.  */
+  size = ((size + (sizeof (USER_WORD) - 1))
+         & ~(sizeof (USER_WORD) - 1));
+
+  if (sizeof loader_area - loader_area_used < size)
+    return 1;
+
+  /* Now copy name to the loader area, filling the padding with NULL
+     bytes.  */
+  strncpy (loader_area + loader_area_used, name, size);
+
+  /* Increase loader_area_used.  */
+  loader_area_used += size;
+  return 0;
+}
+
+/* Write the commands necessary to map the executable file into memory
+   for the given PT_LOAD program HEADER.  Value is 1 upon failure,
+   else 0.  If USE_ALTERNATE, use the command code 17 instead of
+   1.
+
+   Apply the given OFFSET to virtual addresses that will be mapped.  */
+
+static int
+write_load_command (program_header *header, bool use_alternate,
+                   USER_WORD offset)
+{
+  struct exec_map_command command;
+  struct exec_map_command command1;
+  USER_WORD start, end;
+  bool need_command1;
+  static long pagesize;
+
+  /* First, write the commands necessary to map the specified segment
+     itself.
+
+     This is the area between header->p_vaddr and header->p_filesz,
+     rounded up to the page size.  */
+
+#ifndef PAGE_MASK
+  /* This system doesn't define a fixed page size.  */
+
+#ifdef HAVE_GETPAGESIZE
+  if (!pagesize)
+    pagesize = getpagesize ();
+#else /* HAVE_GETPAGESIZE */
+  if (!pagesize)
+    pagesize = sysconf (_SC_PAGESIZE);
+
+#define PAGE_MASK (~(pagesize - 1))
+#define PAGE_SIZE (pagesize)
+#endif /* HAVE_GETPAGESIZE */
+#endif /* PAGE_MASK */
+
+  start = header->p_vaddr & PAGE_MASK;
+  end = ((header->p_vaddr + header->p_filesz
+         + PAGE_SIZE)
+        & PAGE_MASK);
+
+  command.command = use_alternate ? 17 : 1;
+  command.vm_address = start;
+  command.file_offset = header->p_offset & PAGE_MASK;
+  command.protection = 0;
+  command.length = end - start;
+  command.clear = 0;
+  command.flags = MAP_PRIVATE | MAP_FIXED;
+
+  /* Apply the memory protection specified in the header.  */
+
+  if (header->p_flags & 4) /* PF_R */
+    command.protection |= PROT_READ;
+
+  if (header->p_flags & 2) /* PF_W */
+    command.protection |= PROT_WRITE;
+
+  if (header->p_flags & 1) /* PF_X */
+    command.protection |= PROT_EXEC;
+
+  /* Next, write any command necessary to map pages in the area
+     between p_filesz and p_memsz.  */
+  need_command1 = false;
+
+  if (header->p_memsz > header->p_filesz)
+    {
+      /* If there are bytes after end which need to be initialized, do
+        that now.  */
+      command.clear = end - header->p_vaddr - header->p_filesz;
+      start = end;
+      end = header->p_vaddr + header->p_memsz + PAGE_SIZE;
+      end &= PAGE_MASK;
+
+      if (end > start)
+       {
+         command1.command = 4;
+         command1.vm_address = start;
+         command1.file_offset = 0;
+         command1.length = end - start;
+         command1.clear = 0;
+         command1.protection = command.protection;
+         command1.flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;
+         need_command1 = true;
+       }
+    }
+
+  /* Apply the offset to both commands if necessary.  */
+
+  if (offset)
+    {
+      if (need_command1)
+       command1.vm_address += offset;
+
+      command.vm_address += offset;
+    }
+
+  /* Write both commands.  */
+
+  if (sizeof loader_area - loader_area_used < sizeof command)
+    return 1;
+
+  memcpy (loader_area + loader_area_used, &command,
+         sizeof command);
+  loader_area_used += sizeof command;
+
+  if (!need_command1)
+    return 0;
+
+  if (sizeof loader_area - loader_area_used < sizeof command1)
+    return 1;
+
+  memcpy (loader_area + loader_area_used, &command1,
+         sizeof command1);
+  loader_area_used += sizeof command1;
+
+  return 0;
+}
+
+#if defined __mips__ && !defined MIPS_NABI
+
+/* Static storage used for MIPS ABI flags.  */
+static struct mips_elf_abi_flags exec_abi, interpreter_abi;
+
+/* Static storage for interpreter headers.  */
+static elf_header exec_interpreter_header;
+
+/* Pointer to the ELF header of this executable's interpreter.  */
+static elf_header *interpreter_header;
+
+/* Pointer to any PT_MIPS_ABIFLAGS program header found in the
+   executable itself.  */
+static struct mips_elf_abi_flags *exec_abiflags;
+
+/* Pointer to any PT_MIPS_ABIFLAGS program header found in the
+   executable's ELF interpreter.  */
+static struct mips_elf_abi_flags *interpreter_abiflags;
+
+#endif /* defined __mips__ && !defined MIPS_NABI */
+
+/* Process the specified program HEADER; HEADER is from the ELF
+   interpreter of another executable.  FD is the executable file from
+   which it is being read, NAME is its file name, and ELF_HEADER is
+   its header.
+
+   If ELF_HEADER->e_type is ET_DYN, add the base address for position
+   independent interpreter code to virtual addresses.
+
+   Value is 1 upon failure, else 0.  */
+
+static int
+process_interpreter_1 (const char *name, int fd,
+                      program_header *header,
+                      elf_header *elf_header)
+{
+  int rc;
+#if defined __mips__ && !defined MIPS_NABI
+  ssize_t rc1;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+
+  switch (header->p_type)
+    {
+    default: /* PT_NULL, PT_NOTE, PT_DYNAMIC, PT_INTERP, et cetera */
+      rc = 0;
+      break;
+
+    case 1: /* PT_LOAD */
+      /* This describes a segment in the file that must be loaded.
+        Write the appropriate load command.  */
+
+      if (elf_header->e_type == 3) /* ET_DYN */
+       rc = write_load_command (header, true,
+                                INTERPRETER_BASE);
+      else
+       rc = write_load_command (header, true, 0);
+
+      break;
+
+#if defined __mips__ && !defined MIPS_NABI
+    case 0x70000003: /* PT_MIPS_ABIFLAGS */
+      /* Record this header for later use.  */
+      rc1 = pread (fd, &interpreter_abi, sizeof interpreter_abi,
+                  header->p_offset);
+
+      if (rc1 != sizeof interpreter_abi)
+       return 1;
+
+      interpreter_abiflags = &interpreter_abi;
+      rc = 0;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+    }
+
+  return rc;
+}
+
+/* Read the ELF interpreter specified in the given program header from
+   FD, and append the commands necessary to load it to the load area.
+   Then, return the interpreter entry point in *ENTRY.
+
+   Value is 1 upon failure, else 0.  */
+
+static int
+process_interpreter (int fd, program_header *prog_header,
+                    USER_WORD *entry)
+{
+  char buffer[PATH_MAX + 1];
+  int rc, size, i;
+  elf_header header;
+  program_header program;
+
+  /* Read the interpreter name.  */
+  size = MIN (prog_header->p_filesz, PATH_MAX);
+  rc = pread (fd, buffer, size, prog_header->p_offset);
+  if (rc < size)
+    return 1;
+
+  /* Make sure the name is NULL terminated.  */
+  buffer[size] = '\0';
+
+  /* Check if the file is executable.  This is unfortunately not
+     atomic.  */
+
+  if (access (buffer, X_OK))
+    return 1;
+
+  /* Read the interpreter's header much like exec_0.
+
+     However, use special command codes in `process_program_header' if
+     it is position independent.  That way, the loader knows it should
+     use the open interpreter instead.  */
+
+  fd = open (buffer, O_RDONLY);
+
+  if (fd < 0)
+    return 1;
+
+  rc = read (fd, &header, sizeof header);
+
+  if (rc < sizeof header)
+    goto fail;
+
+#if defined __mips__ && !defined MIPS_NABI
+  /* Record this interpreter's header for later use determining the
+     floating point ABI.  */
+  exec_interpreter_header = header;
+  interpreter_header = &exec_interpreter_header;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+
+  /* Verify that this is indeed an ELF file.  */
+
+  if (header.e_ident[0] != 0x7f
+      || header.e_ident[1] != 'E'
+      || header.e_ident[2] != 'L'
+      || header.e_ident[3] != 'F')
+    goto fail;
+
+  /* Now check that the class is correct.  */
+#ifdef EXEC_64
+  if (header.e_ident[4] != 2)
+    goto fail;
+#else /* !EXEC_64 */
+  if (header.e_ident[4] != 1)
+    goto fail;
+#endif /* EXEC_64 */
+
+  /* And the endianness.  */
+#ifndef WORDS_BIGENDIAN
+  if (header.e_ident[5] != 1)
+    goto fail;
+#else /* WORDS_BIGENDIAN */
+  if (header.e_ident[5] != 2)
+    goto fail;
+#endif /* EXEC_64 */
+
+  /* Check that this is an executable.  */
+  if (header.e_type != 2 && header.e_type != 3)
+    goto fail;
+
+  /* Now check that the ELF program header makes sense.  */
+  if (header.e_phnum > 0xffff
+      || (header.e_phentsize
+         != sizeof (program_header)))
+    goto fail;
+
+  if (write_open_command (buffer, true))
+    goto fail;
+
+  for (i = 0; i < header.e_phnum; ++i)
+    {
+      rc = read (fd, &program, sizeof program);
+      if (rc < sizeof program)
+       goto fail;
+
+      if (process_interpreter_1 (buffer, fd, &program,
+                                &header))
+       goto fail;
+    }
+
+  if (header.e_type == 3) /* ET_DYN */
+    *entry = header.e_entry + INTERPRETER_BASE;
+  else
+    *entry = header.e_entry;
+
+  close (fd);
+  return 0;
+
+ fail:
+  close (fd);
+  return 1;
+}
+
+/* Process the specified program HEADER.  FD is the executable file
+   from which it is being read, NAME is its file name, and ELF_HEADER
+   is its header.
+
+   If ELF_HEADER->e_type is ET_DYN, add the base address for position
+   independent code to virtual addresses.
+
+   If OFFSET is non-NULL, and *OFFSET is -1, write the virtual address
+   of HEADER if it describes a PT_LOAD segment.
+
+   If an interpreter is found, set *ENTRY to its entry point.
+
+   Value is 1 upon failure, else 0.  */
+
+static int
+process_program_header (const char *name, int fd,
+                       program_header *header,
+                       elf_header *elf_header,
+                       USER_WORD *entry,
+                       USER_WORD *offset)
+{
+  int rc;
+#if defined __mips__ && !defined MIPS_NABI
+  ssize_t rc1;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+
+  switch (header->p_type)
+    {
+    default: /* PT_NULL, PT_NOTE, PT_DYNAMIC, et cetera */
+      rc = 0;
+      break;
+
+    case 1: /* PT_LOAD */
+      /* This describes a segment in the file that must be loaded.
+        Write the appropriate load command.  */
+
+      if (elf_header->e_type == 3) /* ET_DYN */
+       {
+         rc = write_load_command (header, false,
+                                  EXECUTABLE_BASE);
+
+         if (!rc && offset && *offset == (USER_WORD) -1)
+           *offset = EXECUTABLE_BASE + header->p_vaddr;
+       }
+      else
+       {
+         rc = write_load_command (header, false, 0);
+
+         if (!rc && offset && *offset == (USER_WORD) -1)
+           *offset = header->p_vaddr;
+       }
+
+      break;
+
+    case 3: /* PT_INTERP */
+      /* This describes another executable that must be loaded.  Open
+        the interpreter and process each of its headers as well.  */
+      rc = process_interpreter (fd, header, entry);
+      break;
+
+    case 1685382481: /* PT_GNU_STACK */
+      /* TODO */
+      rc = 0;
+      break;
+
+#if defined __mips__ && !defined MIPS_NABI
+    case 0x70000003: /* PT_MIPS_ABIFLAGS */
+      /* Record this header for later use.  */
+      rc1 = pread (fd, &exec_abi, sizeof exec_abi,
+                  header->p_offset);
+
+      if (rc1 != sizeof exec_abi)
+       return 1;
+
+      exec_abiflags = &exec_abi;
+      rc = 0;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+    }
+
+  return rc;
+}
+
+/* Prepend one or two extra arguments ARG1 and ARG2 to a pending
+   execve system call.  Replace the argument immediately after
+   with ARG3.
+
+   TRACEE is the tracee performing the system call, and REGS are its
+   current user registers.  Value is 1 upon failure, else 0.  */
+
+static int
+insert_args (struct exec_tracee *tracee, USER_REGS_STRUCT *regs,
+            const char *arg1, const char *arg2, const char *arg3)
+{
+  USER_WORD argv, argc, word, new;
+  USER_WORD new1, new2, new3, i;
+  size_t text_size, effective_size;
+  USER_REGS_STRUCT original;
+
+  /* First, get a pointer to the current argument vector.  */
+  argv = regs->SYSCALL_ARG1_REG;
+
+  /* Now figure out how many arguments there are.  */
+  argc = 0;
+  while (true)
+    {
+      /* Clear errno.  PTRACE_PEEKDATA returns the word read the same
+        way failure indications are returned, so the only way to
+        catch IO errors is by clearing errno before the call to
+        ptrace and checking it afterwards.  */
+
+      errno = 0;
+      word = ptrace (PTRACE_PEEKDATA, tracee->pid,
+                    (void *) argv, NULL);
+      argv += sizeof (USER_WORD);
+
+      if (errno)
+       return 1;
+
+      if (!word)
+       break;
+
+      ++argc;
+    };
+
+  /* Allocate enough to hold that many arguments, alongside the argc
+     text.  */
+
+  text_size = (strlen (arg1) + 1
+              + (arg2 ? strlen (arg2) + 1 : 0)
+              + strlen (arg3) + 1);
+
+  /* Round it up to the user word size.  */
+  text_size += sizeof (USER_WORD) - 1;
+  text_size &= ~(sizeof (USER_WORD) - 1);
+
+  /* Now allocate the new argv.  Make sure argc is at least 1; it
+     needs to hold ARG3.  */
+
+  effective_size = sizeof word * (MAX (1, argc) + 2) + text_size;
+
+  if (arg2)
+    effective_size += sizeof word;
+
+  /* Copy regs to original so that user_alloca knows it should append
+     the ABI red zone.  */
+
+  memcpy (&original, regs, sizeof *regs);
+  new = user_alloca (tracee, &original, regs,
+                    effective_size);
+
+  if (!new)
+    goto fail;
+
+  /* Figure out where argv starts.  */
+
+  new3 = new + text_size;
+
+  /* Now write the first two strings.  */
+
+  new1 = new + strlen (arg1) + 1;
+  new2 = new1 + (arg2 ? strlen (arg2) + 1 : 0);
+
+  if (user_copy (tracee, (const unsigned char *) arg1,
+                new, new1 - new))
+    goto fail;
+
+  if (arg2 && user_copy (tracee, (const unsigned char *) arg2,
+                        new1, new2 - new1))
+    goto fail;
+
+  /* Write the replacement arg3, the file name of the executable.  */
+
+  if (user_copy (tracee, (const unsigned char *) arg3,
+                new2, new3 - new2))
+    goto fail;
+
+  /* Start copying argv back to new2.  First, write the one or two new
+     arguments.  */
+
+  if (ptrace (PTRACE_POKETEXT, tracee->pid,
+             (void *) new3, (void *) new))
+    goto fail;
+
+  new3 += sizeof new3;
+
+  if (arg2 && ptrace (PTRACE_POKETEXT, tracee->pid,
+                     (void *) new3, (void *) new1))
+    goto fail;
+  else if (arg2)
+    new3 += sizeof new3;
+
+  /* Next, write the third argument.  */
+
+  if (ptrace (PTRACE_POKETEXT, tracee->pid, (void *) new3,
+             (void *) new2))
+    goto fail;
+
+  new3 += sizeof new3;
+
+  /* Copy the remaining arguments back.  */
+
+  argv = regs->SYSCALL_ARG1_REG;
+
+  if (argc)
+    {
+      /* Make sure the trailing NULL is included.  */
+      argc += 1;
+
+      /* Now copy each argument in argv, starting from argv[1].  */
+
+      for (i = 1; i < argc; ++i)
+       {
+         /* Read one argument.  */
+         word = ptrace (PTRACE_PEEKDATA, tracee->pid,
+                        (void *) (argv + i * sizeof argv), NULL);
+
+         /* Write one argument, then increment new3.  */
+
+         if (ptrace (PTRACE_POKETEXT, tracee->pid,
+                     (void *) new3, (void *) word))
+           goto fail;
+
+         new3 += sizeof new3;
+       }
+    }
+  else
+    {
+      /* Just write the trailing NULL.  */
+
+      if (ptrace (PTRACE_POKETEXT, tracee->pid,
+                 (void *) new3, (void *) 0))
+       goto fail;
+
+      new3 += sizeof new3;
+    }
+
+  /* Assert that new3 is not out of bounds.  */
+  assert (new3 == new + effective_size);
+
+  /* And that it is properly aligned.  */
+  assert (!(new3 & (sizeof new3 - 2)));
+
+  /* Now modify the system call argument to point to new +
+     text_size.  */
+
+  regs->SYSCALL_ARG1_REG = new + text_size;
+
+#ifdef __aarch64__
+  if (aarch64_set_regs (tracee->pid, regs, false))
+    goto fail;
+#else /* !__aarch64__ */
+  if (ptrace (PTRACE_SETREGS, tracee->pid, NULL, regs))
+    goto fail;
+#endif /* __aarch64__ */
+
+  /* Success.  */
+
+  return 0;
+
+ fail:
+  /* Restore the original stack pointer.  */
+#ifdef __aarch64__
+  aarch64_set_regs (tracee->pid, &original, false);
+#else /* !__aarch64__ */
+  ptrace (PTRACE_SETREGS, tracee->pid, NULL, &original);
+#endif /* __aarch64__ */
+  errno = ENOMEM;
+  return 1;
+}
+
+
+
+/* Format PID, an unsigned process identifier, in base 10.  Place the
+   result in *IN, and return a pointer to the byte after the
+   result.  REM should be NULL.  */
+
+static char *
+format_pid (char *in, unsigned int pid)
+{
+  unsigned int digits[32], *fill;
+
+  fill = digits;
+
+  for (; pid != 0; pid = pid / 10)
+    *fill++ = pid % 10;
+
+  /* Insert 0 if the number would otherwise be empty.  */
+
+  if (fill == digits)
+    *fill++ = 0;
+
+  while (fill != digits)
+    {
+      --fill;
+      *in++ = '0' + *fill;
+    }
+
+  *in = '\0';
+  return in;
+}
+
+/* Return a sequence of actions required to load the executable under
+   the file NAME for the given TRACEE.  First, see if the file starts
+   with #!; in that case, find the program to open and use that
+   instead.
+
+   If REENTRANT is not defined, NAME is actually a buffer of size
+   PATH_MAX + 80.  In that case, copy over the file name actually
+   opened.
+
+   Next, read the executable header, and add the necessary memory
+   mappings for each file.  Finally, return the action data and its
+   size in *SIZE.
+
+   Finally, use REGS to add the required interpreter arguments to the
+   caller's argv.
+
+   Value is NULL upon failure, with errno set accordingly.  */
+
+char *
+exec_0 (char *name, struct exec_tracee *tracee,
+       size_t *size, USER_REGS_STRUCT *regs)
+{
+  int fd, rc, i;
+  elf_header header;
+  const char *interpreter_name, *extra;
+  program_header program;
+  USER_WORD entry, program_entry, offset;
+  USER_WORD header_offset;
+  struct exec_jump_command jump;
+#if defined __mips__ && !defined MIPS_NABI
+  int fpu_mode;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+  char buffer[80], buffer1[PATH_MAX + 80], *rewrite;
+  ssize_t link_size;
+  size_t remaining;
+
+  /* If the process is trying to run /proc/self/exe, make it run
+     itself instead.  */
+
+  if (!strcmp (name, "/proc/self/exe") && tracee->exec_file)
+    {
+      strncpy (name, tracee->exec_file, PATH_MAX - 1);
+      name[PATH_MAX] = '\0';
+    }
+  else
+    {
+      /* If name is not absolute, then make it relative to TRACEE's
+        cwd.  Use stpcpy, as sprintf is not reentrant.  */
+
+      if (name[0] && name[0] != '/')
+       {
+         /* Clear `buffer'.  */
+         memset (buffer, 0, sizeof buffer);
+         memset (buffer1, 0, sizeof buffer);
+
+         /* Copy over /proc, the PID, and /cwd/.  */
+         rewrite = stpcpy (buffer, "/proc/");
+         rewrite = format_pid (rewrite, tracee->pid);
+         strcpy (rewrite, "/cwd");
+
+         /* Resolve this symbolic link.  */
+
+         link_size = readlink (buffer, buffer1,
+                               PATH_MAX + 1);
+
+         if (link_size < 0)
+           return NULL;
+
+         /* Check that the name is a reasonable size.  */
+
+         if (link_size > PATH_MAX)
+           {
+             /* The name is too long.  */
+             errno = ENAMETOOLONG;
+             return NULL;
+           }
+
+         /* Add a directory separator if necessary.  */
+      
+         if (!link_size || buffer1[link_size - 1] != '/')
+           buffer1[link_size] = '/', link_size++;
+
+         rewrite = buffer1 + link_size;
+         remaining = buffer1 + sizeof buffer1 - rewrite - 1;
+         rewrite = stpncpy (rewrite, name, remaining);
+
+         /* Replace name with buffer1.  */
+#ifndef REENTRANT
+         strcpy (name, buffer1);
+#endif /* REENTRANT */
+       }
+    }
+
+  /* Check that the file is accessible and executable.  */
+
+  if (access (name, X_OK))
+    return NULL;
+
+  fd = open (name, O_RDONLY);
+  if (fd < 0)
+    return NULL;
+
+  /* Now read the header.  */
+
+  extra = NULL;
+  interpreter_name = check_interpreter (name, fd, &extra);
+  if (!interpreter_name)
+    goto fail;
+
+  /* Open the interpreter instead, if necessary.  */
+  if (interpreter_name != name)
+    {
+      close (fd);
+      fd = open (interpreter_name, O_RDONLY);
+      if (fd < 0)
+       return NULL;
+
+      /* Now, rewrite the argument list to include `interpreter_name'
+        and perhaps `extra'.  */
+
+      if (insert_args (tracee, regs, interpreter_name,
+                      extra, name))
+       goto fail1;
+    }
+
+  rc = read (fd, &header, sizeof header);
+
+  if (rc < sizeof header)
+    goto fail1;
+
+  /* Verify that this is indeed an ELF file.  */
+
+  if (header.e_ident[0] != 0x7f
+      || header.e_ident[1] != 'E'
+      || header.e_ident[2] != 'L'
+      || header.e_ident[3] != 'F')
+    goto fail1;
+
+  /* Now check that the class is correct.  */
+#ifdef EXEC_64
+  if (header.e_ident[4] != 2)
+    goto fail1;
+#else /* !EXEC_64 */
+  if (header.e_ident[4] != 1)
+    goto fail1;
+#endif /* EXEC_64 */
+
+  /* And the endianness.  */
+#ifndef WORDS_BIGENDIAN
+  if (header.e_ident[5] != 1)
+    goto fail1;
+#else /* WORDS_BIGENDIAN */
+  if (header.e_ident[5] != 2)
+    goto fail1;
+#endif /* EXEC_64 */
+
+  /* Check that this is an executable.  */
+  if (header.e_type != 2 && header.e_type != 3)
+    goto fail1;
+
+  /* Now check that the ELF program header makes sense.  */
+  if (header.e_phnum > 0xffff
+      || (header.e_phentsize
+         != sizeof (program_header)))
+    goto fail1;
+
+  /* Seek to the first program header and read each one.  */
+  rc = lseek (fd, header.e_phoff, SEEK_SET);
+  if (rc < 0)
+    goto fail1;
+  loader_area_used = 0;
+
+  /* Write the command used to open the executable.  */
+  if (write_open_command (interpreter_name, false))
+    goto fail1;
+
+  /* Apply base addresses for PIC code.  */
+
+  if (header.e_type == 3) /* ET_DYN */
+    offset = EXECUTABLE_BASE;
+  else
+    offset = 0;
+
+  /* entry and program_entry are initially the same, but entry may be
+     set to that of the interpreter if one is present.  */
+
+  entry = header.e_entry + offset;
+  program_entry = header.e_entry;
+
+#if defined __mips__ && !defined MIPS_NABI
+  /* Clear MIPS ABI flags.  */
+  exec_abiflags = NULL;
+  interpreter_abiflags = NULL;
+  interpreter_header = NULL;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+
+  /* Set header_offset to -1; `process_program_header' then updates it
+     to that of the first mapping.  */
+  header_offset = -1;
+
+  for (i = 0; i < header.e_phnum; ++i)
+    {
+      rc = read (fd, &program, sizeof program);
+      if (rc < sizeof program)
+       goto fail1;
+
+      if (process_program_header (interpreter_name, fd,
+                                 &program, &header,
+                                 &entry, &header_offset))
+       goto fail1;
+    }
+
+  /* Write the entry point and program entry.  */
+
+  jump.command = 3;
+  jump.entry = entry;
+
+  /* Now calculate values for the aux vector.  */
+
+  jump.at_entry = program_entry + offset;
+  jump.at_phent = header.e_phentsize;
+  jump.at_phnum = header.e_phnum;
+  jump.at_base = (entry == header.e_entry + offset
+                 ? EXECUTABLE_BASE
+                 : INTERPRETER_BASE);
+
+#if defined __mips__ && !defined MIPS_NABI
+  /* Finally, calculate the FPU mode wanted by the executable.  */
+
+  if (determine_fpu_mode (&header, interpreter_header,
+                         &fpu_mode, exec_abiflags,
+                         interpreter_abiflags))
+    /* N.B. that `determine_fpu_mode' sets errno.  */
+    goto fail;
+
+  /* If the processor is too new to support FR0 operation, place the
+     executable in floating point emulation mode.  */
+
+  if (fpu_mode == FP_FR0 && !cpu_supports_fr0_p ())
+    fpu_mode = FP_FRE;
+
+  jump.fpu_mode = fpu_mode;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+
+  /* The offset used for at_phdr should be that of the first
+     mapping.  */
+
+  if (header_offset == (USER_WORD) -1)
+    header_offset = 0;
+
+  jump.at_phdr = header.e_phoff + header_offset;
+
+  if (sizeof loader_area - loader_area_used < sizeof jump)
+    goto fail1;
+
+  memcpy (loader_area + loader_area_used, &jump,
+         sizeof jump);
+  loader_area_used += sizeof jump;
+
+  /* Close the file descriptor and return the number of bytes
+     used.  */
+
+  close (fd);
+  *size = loader_area_used;
+
+  /* Make sure the loader area is properly aligned.  */
+  assert (!(loader_area_used & (sizeof (USER_WORD) - 1)));
+  return loader_area;
+
+ fail1:
+  errno = ENOEXEC;
+ fail:
+  close (fd);
+  return NULL;
+}
diff --git a/exec/exec.h b/exec/exec.h
new file mode 100644
index 00000000000..8ee74d7ca8b
--- /dev/null
+++ b/exec/exec.h
@@ -0,0 +1,201 @@
+/* Program execution for Emacs.
+
+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/>.  */
+
+
+
+#ifndef _EXEC_H_
+#define _EXEC_H_
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif /* HAVE_STDINT_H */
+
+#include <sys/types.h>
+
+#include USER_HEADER
+
+/* Define a replacement for `uint64_t' if it's not present in the C
+   library.  */
+
+#ifndef UINT64_MAX
+
+typedef struct
+{
+  uint32_t word1;
+  uint32_t word2;
+} xint64_t;
+
+#else /* UINT64_MAX */
+typedef uint64_t xint64_t;
+#endif /* !UINT64_MAX */
+
+
+
+/* 32-bit ELF headers.  */
+
+struct elf_header_32
+{
+  unsigned char e_ident[16];
+  uint16_t      e_type;
+  uint16_t      e_machine;
+  uint32_t      e_version;
+  uint32_t      e_entry;
+  uint32_t      e_phoff;
+  uint32_t      e_shoff;
+  uint32_t      e_flags;
+  uint16_t      e_ehsize;
+  uint16_t      e_phentsize;
+  uint16_t      e_phnum;
+  uint16_t      e_shentsize;
+  uint16_t      e_shnum;
+  uint16_t      e_shstrndx;
+};
+
+struct program_header_32
+{
+  uint32_t p_type;
+  uint32_t p_offset;
+  uint32_t p_vaddr;
+  uint32_t p_paddr;
+  uint32_t p_filesz;
+  uint32_t p_memsz;
+  uint32_t p_flags;
+  uint32_t p_align;
+};
+
+struct dt_entry_32
+{
+  uint32_t d_tag;
+  uint32_t d_val;
+};
+
+
+
+struct elf_header_64
+{
+  unsigned char e_ident[16];
+  uint16_t      e_type;
+  uint16_t      e_machine;
+  uint32_t      e_version;
+  xint64_t      e_entry;
+  xint64_t      e_phoff;
+  xint64_t      e_shoff;
+  uint32_t      e_flags;
+  uint16_t      e_ehsize;
+  uint16_t      e_phentsize;
+  uint16_t      e_phnum;
+  uint16_t      e_shentsize;
+  uint16_t      e_shnum;
+  uint16_t      e_shstrndx;
+};
+
+struct program_header_64
+{
+  uint32_t p_type;
+  uint32_t p_flags;
+  xint64_t p_offset;
+  xint64_t p_vaddr;
+  xint64_t p_paddr;
+  xint64_t p_filesz;
+  xint64_t p_memsz;
+  xint64_t p_align;
+};
+
+struct dt_entry_64
+{
+  xint64_t d_tag;
+  xint64_t d_val;
+};
+
+
+
+/* Define some types to the correct values.  */
+
+#ifdef EXEC_64
+typedef struct elf_header_64 elf_header;
+typedef struct program_header_64 program_header;
+typedef struct dt_entry_64 dt_entry;
+#else /* !EXEC_64 */
+typedef struct elf_header_32 elf_header;
+typedef struct program_header_32 program_header;
+typedef struct dt_entry_32 dt_entry;
+#endif /* EXEC_64 */
+
+
+
+/* Defined in trace.c.  */
+
+/* Structure describing a process being traced.  */
+
+struct exec_tracee
+{
+  /* The next process being traced.  */
+  struct exec_tracee *next;
+
+  /* The thread ID of this process.  */
+  pid_t pid;
+
+  /* Whether or not the tracee is currently waiting for a system call
+     to complete.  */
+  bool waiting_for_syscall : 1;
+
+  /* Whether or not the tracee has been created but is not yet
+     processed by `handle_clone'.  */
+  bool new_child : 1;
+
+#ifndef REENTRANT
+  /* Name of the executable being run.  */
+  char *exec_file;
+#endif /* !REENTRANT */
+};
+
+
+
+#ifdef __aarch64__
+
+extern int aarch64_get_regs (pid_t, USER_REGS_STRUCT *);
+extern int aarch64_set_regs (pid_t, USER_REGS_STRUCT *, bool);
+
+#endif /* __aarch64__ */
+
+
+
+extern USER_WORD user_alloca (struct exec_tracee *, USER_REGS_STRUCT *,
+                             USER_REGS_STRUCT *, USER_WORD);
+extern int user_copy (struct exec_tracee *, const unsigned char *,
+                     USER_WORD, USER_WORD);
+extern void exec_init (const char *);
+
+
+
+extern int tracing_execve (const char *, char *const *,
+                          char *const *);
+extern int after_fork (pid_t);
+extern pid_t exec_waitpid (pid_t, int *, int);
+
+
+
+/* Defined in exec.c.  */
+
+extern char *exec_0 (char *, struct exec_tracee *,
+                    size_t *, USER_REGS_STRUCT *);
+
+
+
+#endif /* _EXEC_H_ */
diff --git a/exec/exec1.c b/exec/exec1.c
new file mode 100644
index 00000000000..d77ca8adf54
--- /dev/null
+++ b/exec/exec1.c
@@ -0,0 +1,94 @@
+/* Program execution for Emacs.
+
+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 <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/wait.h>
+
+#include "exec.h"
+
+/* exec1 is a program which takes another program and its arguments,
+   forks, and executes that program, all while tracing it and its
+   children to use the program execution mechanism defined in exec.c.
+
+   This is necessary to bypass security restrictions which prohibit
+   Emacs from loading executables from certain directories, by, in
+   effect, replacing the executable loader in the Linux kernel.  */
+
+
+
+int
+main (int argc, char **argv)
+{
+  pid_t pid, pid1;
+  extern char **environ;
+  int wstatus;
+
+  pid1 = getpid ();
+  pid = fork ();
+
+  if (!pid)
+    {
+      /* Set the process group used to the parent.  */
+      if (setpgid (0, pid1))
+       perror ("setpgid");
+
+      tracing_execve (argv[2], argv + 2, environ);
+
+      /* An error occured.  Exit with failure.  */
+      exit (127);
+    }
+  else
+    {
+      /* Provide the file name of the loader.  */
+      exec_init (argv[1]);
+
+      if (after_fork (pid))
+       exit (127);
+
+      /* Start waiting for the process to exit.  */
+
+      while (true)
+       {
+         pid1 = exec_waitpid (-1, &wstatus, 0);
+
+         /* If the child process exits normally, exit with its status
+            code.  If not, raise the signal that caused it to
+            exit.  */
+
+         if (pid == pid1)
+           {
+             if (WIFEXITED (wstatus))
+               exit (WEXITSTATUS (wstatus));
+             else /* if WIFSIGNALED (wstatus) */
+               {
+                 raise (WTERMSIG (wstatus));
+
+                 /* Just in case the signal raised doesn't cause an
+                    exit.  */
+                 exit (127);
+               }
+           }
+
+         /* Otherwise, continue looping.  */
+       }
+    }
+}
diff --git a/exec/install-sh b/exec/install-sh
new file mode 100755
index 00000000000..e046efdf0a3
--- /dev/null
+++ b/exec/install-sh
@@ -0,0 +1,541 @@
+#!/usr/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2020-11-14.01; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# 'make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+tab='  '
+nl='
+'
+IFS=" $tab$nl"
+
+# Set DOITPROG to "echo" to test this script.
+
+doit=${DOITPROG-}
+doit_exec=${doit:-exec}
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+# Create dirs (including intermediate dirs) using mode 755.
+# This is like GNU 'install' as of coreutils 8.32 (2020).
+mkdir_umask=22
+
+backupsuffix=
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+is_target_a_directory=possibly
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+   or: $0 [OPTION]... SRCFILES... DIRECTORY
+   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+   or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+     --help     display this help and exit.
+     --version  display version info and exit.
+
+  -c            (ignored)
+  -C            install only if different (preserve data modification time)
+  -d            create directories instead of installing files.
+  -g GROUP      $chgrpprog installed files to GROUP.
+  -m MODE       $chmodprog installed files to MODE.
+  -o USER       $chownprog installed files to USER.
+  -p            pass -p to $cpprog.
+  -s            $stripprog installed files.
+  -S SUFFIX     attempt to back up existing files, with suffix SUFFIX.
+  -t DIRECTORY  install into DIRECTORY.
+  -T            report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+  RMPROG STRIPPROG
+
+By default, rm is invoked with -f; when overridden with RMPROG,
+it's up to you to specify -f if you want it.
+
+If -S is not specified, no backups are attempted.
+
+Email bug reports to bug-automake@gnu.org.
+Automake home page: https://www.gnu.org/software/automake/
+"
+
+while test $# -ne 0; do
+  case $1 in
+    -c) ;;
+
+    -C) copy_on_change=true;;
+
+    -d) dir_arg=true;;
+
+    -g) chgrpcmd="$chgrpprog $2"
+        shift;;
+
+    --help) echo "$usage"; exit $?;;
+
+    -m) mode=$2
+        case $mode in
+          *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+            echo "$0: invalid mode: $mode" >&2
+            exit 1;;
+        esac
+        shift;;
+
+    -o) chowncmd="$chownprog $2"
+        shift;;
+
+    -p) cpprog="$cpprog -p";;
+
+    -s) stripcmd=$stripprog;;
+
+    -S) backupsuffix="$2"
+        shift;;
+
+    -t)
+        is_target_a_directory=always
+        dst_arg=$2
+        # Protect names problematic for 'test' and other utilities.
+        case $dst_arg in
+          -* | [=\(\)!]) dst_arg=./$dst_arg;;
+        esac
+        shift;;
+
+    -T) is_target_a_directory=never;;
+
+    --version) echo "$0 $scriptversion"; exit $?;;
+
+    --) shift
+        break;;
+
+    -*) echo "$0: invalid option: $1" >&2
+        exit 1;;
+
+    *)  break;;
+  esac
+  shift
+done
+
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+  if test -n "$dst_arg"; then
+    echo "$0: target directory not allowed when installing a directory." >&2
+    exit 1
+  fi
+fi
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+  # When -d is used, all remaining arguments are directories to create.
+  # When -t is used, the destination is already specified.
+  # Otherwise, the last argument is the destination.  Remove it from $@.
+  for arg
+  do
+    if test -n "$dst_arg"; then
+      # $@ is not empty: it contains at least $arg.
+      set fnord "$@" "$dst_arg"
+      shift # fnord
+    fi
+    shift # arg
+    dst_arg=$arg
+    # Protect names problematic for 'test' and other utilities.
+    case $dst_arg in
+      -* | [=\(\)!]) dst_arg=./$dst_arg;;
+    esac
+  done
+fi
+
+if test $# -eq 0; then
+  if test -z "$dir_arg"; then
+    echo "$0: no input file specified." >&2
+    exit 1
+  fi
+  # It's OK to call 'install-sh -d' without argument.
+  # This can happen when creating conditional directories.
+  exit 0
+fi
+
+if test -z "$dir_arg"; then
+  if test $# -gt 1 || test "$is_target_a_directory" = always; then
+    if test ! -d "$dst_arg"; then
+      echo "$0: $dst_arg: Is not a directory." >&2
+      exit 1
+    fi
+  fi
+fi
+
+if test -z "$dir_arg"; then
+  do_exit='(exit $ret); exit $ret'
+  trap "ret=129; $do_exit" 1
+  trap "ret=130; $do_exit" 2
+  trap "ret=141; $do_exit" 13
+  trap "ret=143; $do_exit" 15
+
+  # Set umask so as not to create temps with too-generous modes.
+  # However, 'strip' requires both read and write access to temps.
+  case $mode in
+    # Optimize common cases.
+    *644) cp_umask=133;;
+    *755) cp_umask=22;;
+
+    *[0-7])
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw='% 200'
+      fi
+      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+    *)
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw=,u+rw
+      fi
+      cp_umask=$mode$u_plus_rw;;
+  esac
+fi
+
+for src
+do
+  # Protect names problematic for 'test' and other utilities.
+  case $src in
+    -* | [=\(\)!]) src=./$src;;
+  esac
+
+  if test -n "$dir_arg"; then
+    dst=$src
+    dstdir=$dst
+    test -d "$dstdir"
+    dstdir_status=$?
+    # Don't chown directories that already exist.
+    if test $dstdir_status = 0; then
+      chowncmd=""
+    fi
+  else
+
+    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+    # might cause directories to be created, which would be especially bad
+    # if $src (and thus $dsttmp) contains '*'.
+    if test ! -f "$src" && test ! -d "$src"; then
+      echo "$0: $src does not exist." >&2
+      exit 1
+    fi
+
+    if test -z "$dst_arg"; then
+      echo "$0: no destination specified." >&2
+      exit 1
+    fi
+    dst=$dst_arg
+
+    # If destination is a directory, append the input filename.
+    if test -d "$dst"; then
+      if test "$is_target_a_directory" = never; then
+        echo "$0: $dst_arg: Is a directory" >&2
+        exit 1
+      fi
+      dstdir=$dst
+      dstbase=`basename "$src"`
+      case $dst in
+       */) dst=$dst$dstbase;;
+       *)  dst=$dst/$dstbase;;
+      esac
+      dstdir_status=0
+    else
+      dstdir=`dirname "$dst"`
+      test -d "$dstdir"
+      dstdir_status=$?
+    fi
+  fi
+
+  case $dstdir in
+    */) dstdirslash=$dstdir;;
+    *)  dstdirslash=$dstdir/;;
+  esac
+
+  obsolete_mkdir_used=false
+
+  if test $dstdir_status != 0; then
+    case $posix_mkdir in
+      '')
+        # With -d, create the new directory with the user-specified mode.
+        # Otherwise, rely on $mkdir_umask.
+        if test -n "$dir_arg"; then
+          mkdir_mode=-m$mode
+        else
+          mkdir_mode=
+        fi
+
+        posix_mkdir=false
+       # The $RANDOM variable is not portable (e.g., dash).  Use it
+       # here however when possible just to lower collision chance.
+       tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+
+       trap '
+         ret=$?
+         rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null
+         exit $ret
+       ' 0
+
+       # Because "mkdir -p" follows existing symlinks and we likely work
+       # directly in world-writeable /tmp, make sure that the '$tmpdir'
+       # directory is successfully created first before we actually test
+       # 'mkdir -p'.
+       if (umask $mkdir_umask &&
+           $mkdirprog $mkdir_mode "$tmpdir" &&
+           exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
+       then
+         if test -z "$dir_arg" || {
+              # Check for POSIX incompatibilities with -m.
+              # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+              # other-writable bit of parent directory when it shouldn't.
+              # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+              test_tmpdir="$tmpdir/a"
+              ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
+              case $ls_ld_tmpdir in
+                d????-?r-*) different_mode=700;;
+                d????-?--*) different_mode=755;;
+                *) false;;
+              esac &&
+              $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
+                ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
+                test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+              }
+            }
+         then posix_mkdir=:
+         fi
+         rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
+       else
+         # Remove any dirs left behind by ancient mkdir implementations.
+         rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
+       fi
+       trap '' 0;;
+    esac
+
+    if
+      $posix_mkdir && (
+        umask $mkdir_umask &&
+        $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+      )
+    then :
+    else
+
+      # mkdir does not conform to POSIX,
+      # or it failed possibly due to a race condition.  Create the
+      # directory the slow way, step by step, checking for races as we go.
+
+      case $dstdir in
+        /*) prefix='/';;
+        [-=\(\)!]*) prefix='./';;
+        *)  prefix='';;
+      esac
+
+      oIFS=$IFS
+      IFS=/
+      set -f
+      set fnord $dstdir
+      shift
+      set +f
+      IFS=$oIFS
+
+      prefixes=
+
+      for d
+      do
+        test X"$d" = X && continue
+
+        prefix=$prefix$d
+        if test -d "$prefix"; then
+          prefixes=
+        else
+          if $posix_mkdir; then
+            (umask $mkdir_umask &&
+             $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+            # Don't fail if two instances are running concurrently.
+            test -d "$prefix" || exit 1
+          else
+            case $prefix in
+              *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+              *) qprefix=$prefix;;
+            esac
+            prefixes="$prefixes '$qprefix'"
+          fi
+        fi
+        prefix=$prefix/
+      done
+
+      if test -n "$prefixes"; then
+        # Don't fail if two instances are running concurrently.
+        (umask $mkdir_umask &&
+         eval "\$doit_exec \$mkdirprog $prefixes") ||
+          test -d "$dstdir" || exit 1
+        obsolete_mkdir_used=true
+      fi
+    fi
+  fi
+
+  if test -n "$dir_arg"; then
+    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+  else
+
+    # Make a couple of temp file names in the proper directory.
+    dsttmp=${dstdirslash}_inst.$$_
+    rmtmp=${dstdirslash}_rm.$$_
+
+    # Trap to clean up those temp files at exit.
+    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+    # Copy the file name to the temp name.
+    (umask $cp_umask &&
+     { test -z "$stripcmd" || {
+        # Create $dsttmp read-write so that cp doesn't create it read-only,
+        # which would cause strip to fail.
+        if test -z "$doit"; then
+          : >"$dsttmp" # No need to fork-exec 'touch'.
+        else
+          $doit touch "$dsttmp"
+        fi
+       }
+     } &&
+     $doit_exec $cpprog "$src" "$dsttmp") &&
+
+    # and set any options; do chmod last to preserve setuid bits.
+    #
+    # If any of these fail, we abort the whole thing.  If we want to
+    # ignore errors from any of these, just make sure not to ignore
+    # errors from the above "$doit $cpprog $src $dsttmp" command.
+    #
+    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+    { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+    { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+    # If -C, don't bother to copy if it wouldn't change the file.
+    if $copy_on_change &&
+       old=`LC_ALL=C ls -dlL "$dst"     2>/dev/null` &&
+       new=`LC_ALL=C ls -dlL "$dsttmp"  2>/dev/null` &&
+       set -f &&
+       set X $old && old=:$2:$4:$5:$6 &&
+       set X $new && new=:$2:$4:$5:$6 &&
+       set +f &&
+       test "$old" = "$new" &&
+       $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+    then
+      rm -f "$dsttmp"
+    else
+      # If $backupsuffix is set, and the file being installed
+      # already exists, attempt a backup.  Don't worry if it fails,
+      # e.g., if mv doesn't support -f.
+      if test -n "$backupsuffix" && test -f "$dst"; then
+        $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null
+      fi
+
+      # Rename the file to the real destination.
+      $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+      # The rename failed, perhaps because mv can't rename something else
+      # to itself, or perhaps because mv is so ancient that it does not
+      # support -f.
+      {
+        # Now remove or move aside any old file at destination location.
+        # We try this two ways since rm can't unlink itself on some
+        # systems and the destination file might be busy for other
+        # reasons.  In this case, the final cleanup might fail but the new
+        # file should still install successfully.
+        {
+          test ! -f "$dst" ||
+          $doit $rmcmd "$dst" 2>/dev/null ||
+          { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+            { $doit $rmcmd "$rmtmp" 2>/dev/null; :; }
+          } ||
+          { echo "$0: cannot unlink or rename $dst" >&2
+            (exit 1); exit 1
+          }
+        } &&
+
+        # Now rename the file to the real destination.
+        $doit $mvcmd "$dsttmp" "$dst"
+      }
+    fi || exit 1
+
+    trap '' 0
+  fi
+done
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/exec/loader-aarch64.s b/exec/loader-aarch64.s
new file mode 100644
index 00000000000..da8ec1f4977
--- /dev/null
+++ b/exec/loader-aarch64.s
@@ -0,0 +1,187 @@
+// 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/>.
+
+// Notice that aarch64 requires that sp be aligned to 16 bytes while
+// accessing memory from sp, so x20 is used to chase down the load
+// area.
+
+       .section .text
+       .global _start
+_start:
+       //mov   x8, 101                 // SYS_nanosleep
+       //adr   x0, timespec            // req
+       //mov   x1, #0                  // rem
+       //svc   #0                      // syscall
+       mov     x20, sp                 // x20 = sp
+       ldr     x10, [x20]              // x10 = original SP
+       add     x20, x20, #16           // x20 = start of load area
+       mov     x28, #-1                // x28 = secondary fd
+.next_action:
+       ldr     x11, [x20]              // action number
+       and     x12, x11, #-17          // actual action number
+       cbz     x12, .open_file         // open file?
+       cmp     x12, #3                 // jump?
+       beq     .rest_of_exec
+       cmp     x12, #4                 // anonymous mmap?
+       beq     .do_mmap_anon
+.do_mmap:
+       ldr     x0, [x20, 8]            // vm_address
+       ldr     x1, [x20, 32]           // length
+       ldr     x2, [x20, 24]           // protection
+       ldr     x3, [x20, 40]           // flags
+       tst     x11, #16                // primary fd?
+       mov     x4, x29                 // primary fd
+       beq     .do_mmap_1
+       mov     x4, x28                 // secondary fd
+.do_mmap_1:
+       mov     x8, #222                // SYS_mmap
+       ldr     x5, [x20, 16]           // file_offset
+       svc     #0                      // syscall
+       ldr     x9, [x20, 8]            // length
+       cmp     x0, x9                  // mmap result
+       bne     .perror                 // print error
+       ldr     x3, [x20, 48]           // clear
+       add     x1, x1, x0              // x1 = vm_address + end
+       sub     x3, x1, x3              // x3 = x1 - clear
+       mov     x0, #0                  // x0 = 0
+.fill64:
+       sub     x2, x1, x3              // x2 = x1 - x3
+       cmp     x2, #63                 // x2 >= 64?
+       ble     .fillb                  // start filling bytes
+       stp     x0, x0, [x3]            // x3[0] = 0, x3[1] = 0
+       stp     x0, x0, [x3, 16]        // x3[2] = 0, x3[3] = 0
+       stp     x0, x0, [x3, 32]        // x3[4] = 0, x3[5] = 0
+       stp     x0, x0, [x3, 48]        // x3[6] = 0, x3[7] = 0
+       add     x3, x3, #64             // x3 += 8
+       b       .fill64
+.fillb:
+       cmp     x1, x3                  // x1 == x3?
+       beq     .continue               // done
+       strb    w0, [x3], #1            // ((char *) x3)++ = 0
+       b       .fillb
+.continue:
+       add     x20, x20, #56           // next action
+       b       .next_action
+.do_mmap_anon:
+       ldr     x0, [x20, 8]            // vm_address
+       ldr     x1, [x20, 32]           // length
+       ldr     x2, [x20, 24]           // protection
+       ldr     x3, [x20, 40]           // flags
+       mov     x4, #-1                 // fd
+       b       .do_mmap_1
+.open_file:
+       mov     x8, #56                 // SYS_openat
+       mov     x0, #-100               // AT_FDCWD
+       add     x1, x20, #8             // file name
+       mov     x2, #0                  // O_RDONLY
+       mov     x3, #0                  // mode
+       svc     #0                      // syscall
+       cmp     x0, #-1                 // rc < 0?
+       ble     .perror
+       mov     x19, x1                 // x19 == x1
+.nextc:
+       ldrb    w2, [x1], #1            // b = *x1++
+       cmp     w2, #47                 // dir separator?
+       bne     .nextc1                 // not dir separator
+       mov     x19, x1                 // x19 = char past separator
+.nextc1:
+       cbnz    w2, .nextc              // b?
+       add     x1, x1, #7              // round up x1
+       and     x20, x1, #-8            // mask for round, set x20
+       tst     x11, #16                // primary fd?
+       bne     .secondary              // secondary fd
+       mov     x29, x0                 // primary fd
+       mov     x8, #167                // SYS_prctl
+       mov     x0, #15                 // PR_SET_NAME
+       mov     x1, x19                 // basename
+       mov     x2, #0                  // arg2
+       mov     x3, #0                  // arg3
+       mov     x4, #0                  // arg4
+       mov     x5, #0                  // arg5
+       svc     #0                      // syscall
+       b       .next_action            // next action
+.secondary:
+       mov     x28, x0                 // secondary fd
+       b       .next_action            // next action.
+.perror:
+       mov     x8, #93                 // SYS_exit
+       mvn     x0, x0                  // x1 = ~x0
+       add     x0, x0, 1               // x1 += 1
+       svc     #0                      // exit
+.rest_of_exec:
+       mov     x7, x20                 // x7 = x20
+       mov     x20, x10                // x20 = x10
+       ldr     x9, [x20]               // argc
+       add     x9, x9, #2              // x9 += 2
+       lsl     x9, x9, #3              // argc * 8
+       add     x20, x20, x9            // now past argv
+.skipenv:
+       ldr     x9, [x20], #8           // x9 = *envp++
+       cbnz    x9, .skipenv            // x9?
+.one_auxv:
+       ldr     x9, [x20], #16          // x9 = *sp, sp += 2
+       cbz     x9, .cleanup            // !x9?
+       cmp     x9, #3                  // is AT_PHDR?
+       beq     .replace_phdr           // replace
+       cmp     x9, #4                  // is AT_PHENT?
+       beq     .replace_phent          // replace
+       cmp     x9, #5                  // is AT_PHNUM?
+       beq     .replace_phnum          // replace
+       cmp     x9, #9                  // is AT_ENTRY?
+       beq     .replace_entry          // replace
+       cmp     x9, #7                  // is AT_BASE?
+       beq     .replace_base           // replace
+       b       .one_auxv               // next auxv
+.replace_phdr:
+       ldr     x9, [x7, 40]            // at_phdr
+       str     x9, [x20, -8]           // store value
+       b       .one_auxv
+.replace_phent:
+       ldr     x9, [x7, 24]            // at_phent
+       str     x9, [x20, -8]           // store value
+       b       .one_auxv
+.replace_phnum:
+       ldr     x9, [x7, 32]            // at_phnum
+       str     x9, [x20, -8]           // store value
+       b       .one_auxv
+.replace_entry:
+       ldr     x9, [x7, 16]            // at_entry
+       str     x9, [x20, -8]           // store value
+       b       .one_auxv
+.replace_base:
+       ldr     x9, [x7, 48]            // at_base
+       str     x9, [x20, -8]           // store value
+       b       .one_auxv
+.cleanup:
+       cmp     x28, #-1                // is secondary fd set?
+       bne     .cleanup1               // not set
+       mov     x8, #57                 // SYS_close
+       mov     x0, x28                 // secondary fd
+       svc     #0                      // syscall
+.cleanup1:
+       mov     x8, #57                 // SYS_close
+       mov     x0, x29                 // primary fd
+       svc     #0                      // syscall
+.enter:
+       mov     sp, x10                 // restore original SP
+       mov     x0, #0                  // clear rtld_fini
+       ldr     x1, [x7, 8]             // branch to code
+       br      x1
+
+timespec:
+       .quad 10
+       .quad 10
diff --git a/exec/loader-armeabi.s b/exec/loader-armeabi.s
new file mode 100644
index 00000000000..32b2a5268d6
--- /dev/null
+++ b/exec/loader-armeabi.s
@@ -0,0 +1,204 @@
+@ 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/>.
+
+       .section .text
+       .global _start
+_start:
+       @mov    r7, #162                @ SYS_nanosleep
+       @adr    r0, timespec            @ req
+       @mov    r1, #0                  @ rem
+       @swi    #0                      @ syscall
+       mov     r8, sp                  @ r8 = sp
+       ldr     r9, [r8], #8            @ r9 = original sp, r8 += 8
+       mov     r14, #-1                @ r14 = secondary fd
+.next_action:
+       ldr     r11, [r8]               @ r11 = action number
+       and     r12, r11, #-17          @ actual action number
+       cmp     r12, #0                 @ open file?
+       beq     .open_file              @ open file.
+       cmp     r12, #3                 @ jump?
+       beq     .rest_of_exec           @ jump to code.
+       cmp     r12, #4                 @ anonymous mmap?
+       beq     .do_mmap_anon           @ anonymous mmap.
+.do_mmap:
+       add     r6, r8, #4              @ r6 = r8 + 4
+       ldm     r6!, {r0, r5}           @ vm_address, file_offset
+       ldm     r6!, {r1, r2}           @ protection, length
+       mov     r3, r1                  @ swap
+       lsr     r5, #12                 @ divide file offset by page size
+       mov     r1, r2                  @ swap
+       mov     r2, r3                  @ swap
+       ldm     r6!, {r3, r12}          @ flags, clear
+       tst     r11, #16                @ primary fd?
+       mov     r4, r10                 @ primary fd
+       beq     .do_mmap_1
+       mov     r4, r14                 @ secondary fd
+.do_mmap_1:
+       mov     r7, #192                @ SYS_mmap2
+       swi     #0                      @ syscall
+       ldr     r2, [r8, #4]            @ vm_address
+       cmp     r2, r0                  @ rc == vm_address?
+       bne     .perror
+       add     r0, r1, r2              @ r0 = length + vm_address
+       sub     r3, r0, r12             @ r3 = r0 - clear
+       mov     r1, #0                  @ r1 = 0
+.align:
+       cmp     r0, r3                  @ r0 == r3?
+       beq     .continue               @ continue
+       tst     r3, #3                  @ r3 & 3?
+       bne     .fill32                 @ fill aligned
+       strb    r1, [r3], #1            @ fill byte
+       b       .align                  @ align again
+.fill32:
+       sub     r2, r0, r3              @ r2 = r0 - r3
+       cmp     r2, #31                 @ r2 >= 32?
+       ble     .fillb                  @ start filling bytes
+       str     r1, [r3], #4            @ *r3++ = 0
+       str     r1, [r3], #4            @ *r3++ = 0
+       str     r1, [r3], #4            @ *r3++ = 0
+       str     r1, [r3], #4            @ *r3++ = 0
+       str     r1, [r3], #4            @ *r3++ = 0
+       str     r1, [r3], #4            @ *r3++ = 0
+       str     r1, [r3], #4            @ *r3++ = 0
+       str     r1, [r3], #4            @ *r3++ = 0
+       b       .fill32
+.fillb:
+       cmp     r0, r3                  @ r0 == r3
+       beq     .continue               @ done
+       strb    r1, [r3], #1            @ ((char *) r3)++ = 0
+       b       .fillb
+.continue:
+       add     r8, r8, #28             @ next action
+       b       .next_action
+.do_mmap_anon:
+       add     r6, r8, #4              @ r6 = r8 + 4
+       ldm     r6!, {r0, r5}           @ vm_address, file_offset
+       ldm     r6!, {r1, r2}           @ protection, length
+       mov     r3, r1                  @ swap
+       lsr     r5, #12                 @ divide file offset by page size
+       mov     r1, r2                  @ swap
+       mov     r2, r3                  @ swap
+       ldm     r6!, {r3, r12}          @ flags, clear
+       mov     r4, #-1                 @ fd
+       b       .do_mmap_1
+.open_file:
+       mov     r7, #5                  @ SYS_open
+       add     r0, r8, #4              @ file name
+       mov     r1, #0                  @ O_RDONLY
+       mov     r2, #0                  @ mode
+       swi     #0                      @ syscall
+       cmp     r0, #-1                 @ r0 <= -1?
+       ble     .perror
+       add     r8, r8, #4              @ r8 = start of string
+       mov     r1, r8                  @ r1 = r8
+.nextc:
+       ldrb    r2, [r8], #1            @ b = *r0++
+       cmp     r2, #47                 @ dir separator?
+       bne     .nextc1                 @ not dir separator
+       mov     r1, r8                  @ r1 = char past separator
+.nextc1:
+       cmp     r2, #0                  @ b?
+       bne     .nextc                  @ next character
+       add     r8, r8, #3              @ round up r8
+       and     r8, r8, #-4             @ mask for round, set r8
+       tst     r11, #16                @ primary fd?
+       bne     .secondary              @ secondary fd
+       mov     r10, r0                 @ primary fd
+       mov     r7, #172                @ SYS_prctl
+       mov     r0, #15                 @ PR_SET_NAME, r1 = name
+       mov     r2, #0                  @ arg2
+       mov     r3, #0                  @ arg3
+       mov     r4, #0                  @ arg4
+       mov     r5, #0                  @ arg5
+       swi     #0                      @ syscall
+       b       .next_action            @ next action
+.secondary:
+       mov     r14, r0                 @ secondary fd
+       b       .next_action            @ next action
+.perror:
+       mov     r7, #1                  @ SYS_exit
+       mvn     r0, r0                  @ r0 = ~r0
+       add     r0, r0, #1              @ r0 += 1
+       swi     #0
+.rest_of_exec:
+       mov     r7, r9                  @ r7 = original SP
+       ldr     r6, [r7]                @ argc
+       add     r6, r6, #2              @ argc + 2
+       lsl     r6, r6, #2              @ argc *= 4
+       add     r7, r7, r6              @ now past argv
+.skipenv:
+       ldr     r6, [r7], #4            @ r6 = *r7++
+       cmp     r6, #0                  @ r6?
+       bne     .skipenv                @ r6?
+.one_auxv:
+       ldr     r6, [r7], #8            @ r6 = *r7, r7 += 2
+       cmp     r6, #0                  @ !r6?
+       beq     .cleanup                @ r6?
+       cmp     r6, #3                  @ is AT_PHDR?
+       beq     .replace_phdr           @ replace
+       cmp     r6, #4                  @ is AT_PHENT?
+       beq     .replace_phent          @ replace
+       cmp     r6, #5                  @ is AT_PHNUM?
+       beq     .replace_phnum          @ replace
+       cmp     r6, #9                  @ is AT_ENTRY?
+       beq     .replace_entry          @ replace
+       cmp     r6, #7                  @ is AT_BASE?
+       beq     .replace_base           @ replace
+       b       .one_auxv               @ next auxv
+.replace_phdr:
+       ldr     r6, [r8, #20]           @ at_phdr
+       str     r6, [r7, #-4]           @ store value
+       b       .one_auxv
+.replace_phent:
+       ldr     r6, [r8, #12]           @ at_phent
+       str     r6, [r7, #-4]           @ store value
+       b       .one_auxv
+.replace_phnum:
+       ldr     r6, [r8, #16]           @ at_phnum
+       str     r6, [r7, #-4]           @ store value
+       b       .one_auxv
+.replace_entry:
+       ldr     r6, [r8, #8]            @ at_entry
+       str     r6, [r7, #-4]           @ store value
+       b       .one_auxv
+.replace_base:
+       ldr     r6, [r8, #24]           @ at_base
+       str     r6, [r7, #-4]           @ store value
+       b       .one_auxv
+.cleanup:
+       cmp     r14, #-1                @ secondary fd set?
+       bne     .cleanup1               @ not set
+       mov     r7, #6                  @ SYS_close
+       mov     r0, r14                 @ secondary fd
+       swi     #0                      @ syscall
+.cleanup1:
+       mov     r7, #6                  @ SYS_close
+       mov     r0, r10                 @ primary fd
+       swi     #0                      @ syscall
+.enter:
+       mov     sp, r9                  @ restore original SP
+       mov     r0, #0                  @ clear rtld_fini
+       ldr     r1, [r8, #4]            @ branch to code
+       bx      r1
+
+timespec:
+       .long 10
+       .long 10
+
+@ Local Variables:
+@ asm-comment-char: 64
+@ End:
diff --git a/exec/loader-mips64el.s b/exec/loader-mips64el.s
new file mode 100644
index 00000000000..f4a6f918497
--- /dev/null
+++ b/exec/loader-mips64el.s
@@ -0,0 +1,234 @@
+# 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-mips.m4')
+
+       .set noreorder                  # delay slots managed by hand
+       .set noat                       # no assembler macros
+       .section .text
+       .global __start
+__start:
+dnl    li      $v0, 5034               # SYS_nanosleep
+dnl    dla     $a0, .timespec          # rqtp
+dnl    li      $a1, 0                  # rmtp
+dnl    syscall                         # syscall
+       ld      $s2, ($sp)              # original stack pointer
+       DADDI3( $s0, $sp, 16)           # start of load area
+       DADDI2( $sp, -16)               # primary fd, secondary fd
+       li      $t0, -1                 # secondary fd
+       sd      $t0, 8($sp)             # initialize secondary fd
+.next_action:
+       ld      $s1, ($s0)              # action number
+       andi    $t0, $s1, 15            # t0 = action number & 15
+       beqz    $t0, .open_file         # open file?
+       nop                             # delay slot
+       DADDI2( $t0, -3)                # t0 -= 3
+       beqz    $t0, .rest_of_exec      # jump to code
+       nop                             # delay slot
+       li      $t1, 1
+       beq     $t0, $t1, .do_mmap_anon # anonymous mmap?
+       nop                             # delay slot
+.do_mmap:
+       ld      $t0, 8($s0)             # vm address
+       ld      $t1, 16($s0)            # file_offset
+       ld      $t2, 24($s0)            # protection
+       ld      $t3, 32($s0)            # length
+       ld      $v0, 40($s0)            # flags
+       ld      $v1, ($sp)              # primary fd
+       andi    $s3, $s1, 16            # s1 & 16?
+       beqz    $s3, .do_mmap_1         # secondary fd?
+       nop                             # delay slot
+       ld      $v1, 8($sp)             # secondary fd
+.do_mmap_1:
+       move    $a0, $t0                # syscall arg
+       move    $a1, $t3                # syscall arg
+       move    $a2, $t2                # syscall arg
+       move    $a3, $v0                # syscall arg
+       move    $a4, $v1                # syscall arg
+       move    $a5, $t1                # syscall arg
+       li      $v0, 5009               # SYS_mmap
+       syscall                         # syscall
+       bne     $a3, $zero, .perror     # perror?
+       nop                             # delay slot
+       ld      $t1, 48($s0)            # clear
+       dadd    $t0, $a0, $a1           # t0 = end of mapping
+       dsub    $t1, $t0, $t1           # t1 = t0 - clear
+.align:
+       beq     $t0, $t1, .continue     # already finished
+       nop                             # delay slot
+       andi    $t2, $t1, 7             # t1 & 7?
+       bnez    $t2, .filld             # start filling longs
+       nop                             # delay slot
+.filld:
+       dsub    $t2, $t0, $t1           # t2 = t0 - t1
+       sltiu   $t2, $t2, 64            # t2 < 64?
+       bne     $t2, $zero, .fillb      # fill bytes
+       nop                             # delay slot
+       sd      $zero, ($t1)            # zero doubleword
+       DADDI2( $t1, 8)                 # next doubleword
+       sd      $zero, ($t1)            # zero doubleword
+       DADDI2( $t1, 8)                 # next doubleword
+       sd      $zero, ($t1)            # zero doubleword
+       DADDI2( $t1, 8)                 # next doubleword
+       sd      $zero, ($t1)            # zero doubleword
+       DADDI2( $t1, 8)                 # next doubleword
+       sd      $zero, ($t1)            # zero doubleword
+       DADDI2( $t1, 8)                 # next doubleword
+       sd      $zero, ($t1)            # zero doubleword
+       DADDI2( $t1, 8)                 # next doubleword
+       sd      $zero, ($t1)            # zero doubleword
+       DADDI2( $t1, 8)                 # next doubleword
+       sd      $zero, ($t1)            # zero doubleword
+       DADDI2( $t1, 8)                 # next doubleword
+       j       .filld                  # fill either doubleword or byte
+       nop                             # delay slot
+.fillb:
+       beq     $t0, $t1, .continue     # already finished?
+       nop                             # delay slot
+       sb      $zero, ($t1)            # clear byte
+       DADDI2( $t1, 1)                 # t1++
+.continue:
+       DADDI2( $s0, 56)                # s0 = next action
+       j       .next_action            # next action
+       nop                             # delay slot
+.do_mmap_anon:
+       ld      $t0, 8($s0)             # vm address
+       ld      $t1, 16($s0)            # file_offset
+       ld      $t2, 24($s0)            # protection
+       ld      $t3, 32($s0)            # length
+       ld      $v0, 40($s0)            # flags
+       li      $v1, -1                 # fd
+       j       .do_mmap_1              # do mmap
+       nop                             # branch delay slot
+.open_file:
+       li      $v0, 5002               # SYS_open
+       DADDI3( $a0, $s0, 8)            # start of name
+       move    $a1, $zero              # flags = O_RDONLY
+       move    $a2, $zero              # mode = 0
+       syscall                         # syscall
+       bne     $a3, $zero, .perror     # perror
+       nop                             # delay slot
+       DADDI2( $s0, 8)                 # start of string
+       move    $t3, $s0                # t3 = s0
+.nextc:
+       lb      $t0, ($s0)              # load byte
+       DADDI2( $s0, 1)                 # s0++
+       li      $t1, 47                 # directory separator `/'
+       bne     $t0, $t1, .nextc1       # is separator char?
+       nop                             # delay slot
+       move    $t3, $s0                # t3 = char past separator
+.nextc1:
+       bnez    $t0, .nextc             # next character?
+       nop                             # delay slot
+       DADDI2( $s0, 7)                 # adjust for round
+       li      $t2, -8                 # t2 = -8
+       and     $s0, $s0, $t2           # mask for round
+       andi    $t0, $s1, 16            # t1 = s1 & 16
+       move    $t1, $sp                # address of primary fd
+       beqz    $t0, .primary           # primary fd?
+       nop                             # delay slot
+       DADDI2( $t1, 8)                 # address of secondary fd
+       sd      $v0, ($t1)              # store fd
+       j       .next_action            # next action
+       nop                             # delay slot
+.primary:
+       sd      $v0, ($t1)              # store fd
+       li      $v0, 5153               # SYS_prctl
+       li      $a0, 15                 # PR_SET_NAME
+       move    $a1, $t3                # char past separator
+       move    $a2, $zero              # a2
+       move    $a3, $zero              # a3
+       move    $a4, $zero              # a4
+       move    $a5, $zero              # a5
+       syscall                         # syscall
+       j       .next_action            # next action
+       nop                             # delay slot
+.perror:
+       move    $a0, $v0                # errno
+       li      $v0, 5058               # SYS_exit
+       syscall                         # syscall
+.rest_of_exec:
+       move    $s1, $s2                # original SP
+       ld      $t0, ($s1)              # argc
+       dsll    $t0, $t0, 3             # argc *= 8
+       DADDI2( $t0, 16)                # argc += 16
+       dadd    $s1, $s1, $t0           # s1 = start of envp
+.skipenv:
+       ld      $t0, ($s1)              # t0 = *s1
+       DADDI2( $s1, 8)                 # s1++
+       bne     $t0, $zero, .skipenv    # skip again
+       nop                             # delay slot
+       dla     $t3, .auxvtab           # address of auxv table
+.one_auxv:
+       ld      $t0, ($s1)              # t0 = auxv type
+       li      $t1, 10                 # t1 = 10
+       beqz    $t0, .finish            # is AT_IGNORE?
+       nop                             # delay slot
+       sltu    $t1, $t0, $t1           # t1 = t0 < num offsets
+       beqz    $t1, .next              # next auxv
+       nop                             # delay slot
+       dsll    $t1, $t0, 2             # t1 = t0 * 4
+       dadd    $t1, $t3, $t1           # t1 = .auxvtab + t1
+       lw      $t2, ($t1)              # t2 = *t1
+       beqz    $t2, .next              # skip auxv
+       nop                             # delay slot
+       dadd    $t2, $s0, $t2           # t2 = s0 + t2
+       ld      $t2, ($t2)              # t2 = *t2
+       sd      $t2, 8($s1)             # set auxv value
+.next:
+       DADDI2( $s1, 16)                # next auxv
+       j       .one_auxv               # next auxv
+       nop                             # delay slot
+.finish:
+       ld      $t0, 8($sp)             # secondary fd
+       li      $t1, -1                 # t1 = -1
+       ld      $s1, ($sp)              # s1 = primary fd
+       li      $v0, 5003               # SYS_close
+       beq     $t0, $t2, .finish1      # secondary fd set?
+       nop                             # delay slot
+       move    $a0, $t0                # secondary fd
+       syscall                         # syscall
+       li      $v0, 5003               # SYS_close
+.finish1:
+       move    $a0, $s1                # primary fd
+       syscall                         # syscall
+.jump:
+       move    $v0, $zero              # rtld_fini
+       ld      $t0, 8($s0)             # entry
+       move    $sp, $s2                # restore stack pointer, delay slot
+       jr      $t0                     # enter
+       nop                             # delay slot
+
+.auxvtab:
+       .long   0                       # 0
+       .long   0                       # 1
+       .long   0                       # 2
+       .long   40                      # 3 AT_PHDR
+       .long   24                      # 4 AT_PHENT
+       .long   32                      # 5 AT_PHNUM
+       .long   0                       # 6
+       .long   48                      # 7 AT_BASE
+       .long   0                       # 8
+       .long   16                      # 9 AT_ENTRY
+
+.timespec:
+       .quad   10
+       .quad   10
+
+# Local Variables:
+# asm-comment-char: 35
+# End:
diff --git a/exec/loader-mipsel.s b/exec/loader-mipsel.s
new file mode 100644
index 00000000000..baba3f05a94
--- /dev/null
+++ b/exec/loader-mipsel.s
@@ -0,0 +1,236 @@
+# 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-mips.m4')
+
+# Make sure not to use t4 through t7, in order to maintain portability
+# with N32 ABI systems.
+
+       .set noreorder                  # delay slots managed by hand
+       .section .text
+       .global __start
+__start:
+dnl    li      $v0, SYSCALL_nanosleep  # SYS_nanosleep
+dnl    la      $a0, .timespec          # rqtp
+dnl    li      $a1, 0                  # rmtp
+dnl    syscall                         # syscall
+       lw      $s6, ($sp)              # original stack pointer
+       addi    $s0, $sp, 8             # start of load area
+       addi    $sp, -8                 # primary fd, secondary fd
+       li      $t0, -1                 # secondary fd
+       sw      $t0, 4($sp)             # initialize secondary fd
+.next_action:
+       lw      $s2, ($s0)              # action number
+       nop                             # delay slot
+       andi    $t0, $s2, 15            # t0 = s2 & 15
+       beqz    $t0, .open_file         # open file?
+       li      $t1, 3                  # t1 = 3, delay slot
+       beq     $t0, $t1, .rest_of_exec # jump to code
+       li      $t1, 4                  # t1 = 4, delay slot
+       beq     $t0, $t1, .do_mmap_anon # anonymous mmap
+.do_mmap:
+       lw      $a0, 4($s0)             # vm_address, delay slot
+       lw      $v1, 8($s0)             # file_offset
+       lw      $a2, 12($s0)            # protection
+       lw      $a1, 16($s0)            # length
+       lw      $a3, 20($s0)            # flags
+       lw      $v0, ($sp)              # primary fd
+       andi    $t1, $s2, 16            # t1 = s2 & 16
+       beqz    $t1, .do_mmap_1         # secondary fd?
+       nop                             # delay slot
+       lw      $v0, 4($sp)             # secondary fd
+       nop                             # delay slot
+.do_mmap_1:
+SYSCALL(`$v0',`$v1',`$zero',`$zero')   # syscall args
+       li      $v0, SYSCALL_mmap       # SYS_mmap
+       syscall                         # syscall
+       bne     $a3, $zero, .perror     # perror
+RESTORE()                              # delay slot, restore sp
+       lw      $s5, 24($s0)            # clear
+       add     $t0, $a0, $a1           # t0 = length + vm_address, delay slot
+       sub     $t1, $t0, $s5           # t1 = t0 - clear
+.align:
+       beq     $t0, $t1, .continue     # already finished?
+       nop                             # delay slot
+       andi    $t2, $t1, 3             # t1 & 3?
+       bnez    $t2, .fillw             # start filling longs
+       nop                             # delay slot
+       sb      $zero, ($t1)            # clear byte
+       addi    $t1, $t1, 1             # t1++
+       j       .align                  # continue
+       nop                             # delay slot
+.fillw:
+       sub     $t2, $t0, $t1           # t2 = t0 - t1
+       sltiu   $t2, $t2, 32            # r2 < 32?
+       bne     $t2, $zero, .fillb      # fill bytes
+       nop                             # delay slot
+       sw      $zero, ($t1)            # zero word
+       addi    $t1, $t1, 4             # next word
+       sw      $zero, ($t1)            # zero word
+       addi    $t1, $t1, 4             # next word
+       sw      $zero, ($t1)            # zero word
+       addi    $t1, $t1, 4             # next word
+       sw      $zero, ($t1)            # zero word
+       addi    $t1, $t1, 4             # next word
+       sw      $zero, ($t1)            # zero word
+       addi    $t1, $t1, 4             # next word
+       sw      $zero, ($t1)            # zero word
+       addi    $t1, $t1, 4             # next word
+       sw      $zero, ($t1)            # zero word
+       addi    $t1, $t1, 4             # next word
+       sw      $zero, ($t1)            # zero word
+       addi    $t1, $t1, 4             # next word
+       j       .fillw                  # fill either word or byte
+       nop                             # delay slot
+.fillb:
+       beq     $t0, $t1, .continue     # already finished?
+       nop                             # delay slot
+       sb      $zero, ($t1)            # clear byte
+       addi    $t1, $t1, 1             # t1++
+.continue:
+       addi    $s0, $s0, 28            # s0 = next action
+       j       .next_action            # next action
+       nop                             # delay slot
+.do_mmap_anon:
+       lw      $v1, 8($s0)             # file_offset
+       lw      $a2, 12($s0)            # protection
+       lw      $a1, 16($s0)            # length
+       lw      $a3, 20($s0)            # flags
+       li      $t4, -1                 # fd
+       j       .do_mmap_1              # do mmap
+       nop                             # delay slot
+.open_file:
+       li      $v0, SYSCALL_open       # SYS_open
+       addi    $a0, $s0, 4             # start of name
+       move    $a1, $zero              # flags = O_RDONLY
+       move    $a2, $zero              # mode = 0
+       syscall                         # syscall
+       bne     $a3, $zero, .perror     # perror
+       addi    $s0, $s0, 4             # start of string, delay slot
+       move    $t3, $s0                # t3 = char past separator
+.nextc:
+       lb      $t0, ($s0)              # load byte
+       addi    $s0, $s0, 1             # s0++
+       li      $t1, 47                 # directory separator `/'
+       bne     $t0, $t1, .nextc1       # is separator char?
+       nop                             # delay slot
+       move    $t3, $s0                # t3 = char past separator
+.nextc1:
+       bnez    $t0, .nextc             # next character?
+       nop                             # delay slot
+       addi    $s0, $s0, 3             # adjust for round
+       li      $t2, -4                 # t2 = -4
+       and     $s0, $s0, $t2           # mask for round
+       andi    $t0, $s2, 16            # t1 = s2 & 16
+       beqz    $t0, .primary           # primary fd?
+       move    $t0, $sp                # address of primary fd, delay slot
+       addi    $t0, $t0, 4             # address of secondary fd
+       j       .next_action            # next action
+.primary:
+       sw      $v0, ($t0)              # store fd, delay slot
+       li      $v0, SYSCALL_prctl      # SYS_prctl
+       li      $a0, 15                 # PR_SET_NAME
+       move    $a1, $t3                # name
+       move    $a2, $zero              # arg1
+       move    $a3, $zero              # arg2
+SYSCALL(`$a2',`$a2',`$a2',`$a2')       # syscall args
+       syscall                         # syscall
+RESTORE()                              # restore sp
+       j       .next_action            # next action
+       nop                             # delay slot
+.perror:
+       move    $a0, $v0                # errno
+       li      $v0, SYSCALL_exit       # SYS_exit
+       syscall                         # syscall
+.rest_of_exec:
+       move    $s1, $s6                # s1 = original SP
+       lw      $t0, ($s1)              # argc
+       nop                             # delay slot
+       sll     $t0, $t0, 2             # argc *= 4
+       addi    $t0, $t0, 8             # argc += 8
+       add     $s1, $s1, $t0           # s1 = start of envp
+.skipenv:
+       lw      $t0, ($s1)              # t0 = *s1
+       addi    $s1, $s1, 4             # s1++
+       bne     $t0, $zero, .skipenv    # skip again
+       nop                             # delay slot
+       la      $s2, .auxvtab           # address of auxv table
+.one_auxv:
+       lw      $t0, ($s1)              # t0 = auxv type
+       li      $t1, 10                 # t1 = 10, delay slot
+       beqz    $t0, .finish            # is AT_IGNORE?
+       sltu    $t1, $t0, $t1           # t1 = t0 < num offsets, delay slot
+       beq     $t1, $zero, .next       # next auxv
+       sll     $t1, $t0, 2             # t1 = t0 * 4, delay slot
+       add     $t1, $s2, $t1           # t1 = .auxvtab + t1
+       lw      $t2, ($t1)              # t2 = *t1
+       nop                             # delay slot
+       beqz    $t2, .next              # skip auxv
+       add     $t2, $s0, $t2           # t2 = s0 + t2
+       lw      $t2, ($t2)              # t2 = *t2
+       nop                             # delay slot
+       sw      $t2, 4($s1)             # set auxv value
+.next:
+       addi    $s1, $s1, 8             # next auxv
+       j       .one_auxv               # next auxv
+       nop                             # delay slot
+.finish:
+       lw      $t0, 4($sp)             # secondary fd
+       lw      $s1, ($sp)              # primary fd, delay slot, preserved
+       li      $t2, -1                 # immediate -1
+       beq     $t0, $t2, .finish1      # secondary fd set?
+       li      $v0, SYSCALL_close      # SYS_close, delay slot
+       move    $a0, $t0                # fd
+       syscall                         # syscall
+       li      $v0, SYSCALL_close      # SYS_close
+.finish1:
+       move    $a0, $s1                # primary fd
+       syscall                         # syscall
+       li      $v0, SYSCALL_prctl      # SYS_prctl
+       li      $a0, 45                 # PR_SET_FP_MODE
+       lw      $a1, 28($s0)            # fpu_mode
+       move    $a2, $zero              # arg3
+       move    $a3, $zero              # arg4
+SYSCALL(`$a2',`$a2',`$a2',`$a2')       # syscall args
+       syscall                         # syscall
+RESTORE()                              # restore sp
+.jump:
+       move    $v0, $zero              # rtld_fini
+       lw      $t0, 4($s0)             # entry
+       move    $sp, $s6                # restore stack pointer, delay slot
+       jr      $t0                     # enter
+       nop                             # delay slot
+
+.auxvtab:
+       .long   0                       # 0
+       .long   0                       # 1
+       .long   0                       # 2
+       .long   20                      # 3 AT_PHDR
+       .long   12                      # 4 AT_PHENT
+       .long   16                      # 5 AT_PHNUM
+       .long   0                       # 6
+       .long   24                      # 7 AT_BASE
+       .long   0                       # 8
+       .long   8                       # 9 AT_ENTRY
+
+.timespec:
+       .long   10
+       .long   10
+
+# Local Variables:
+# asm-comment-char: 35
+# End:
diff --git a/exec/loader-x86.s b/exec/loader-x86.s
new file mode 100644
index 00000000000..6329e7f33b1
--- /dev/null
+++ b/exec/loader-x86.s
@@ -0,0 +1,203 @@
+define(`CC', `
+dnl')
+
+CC Copyright (C) 2023 Free Software Foundation, Inc.
+CC
+CC This file is part of GNU Emacs.
+CC
+CC GNU Emacs is free software: you can redistribute it and/or modify
+CC it under the terms of the GNU General Public License as published
+CC by the Free Software Foundation, either version 3 of the License,
+CC or (at your option) any later version.
+CC
+CC GNU Emacs is distributed in the hope that it will be useful, but
+CC WITHOUT ANY WARRANTY; without even the implied warranty of
+CC MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+CC General Public License for more details.
+CC
+CC You should have received a copy of the GNU General Public License
+CC along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+       .section .text
+       .global _start
+_start:
+dnl    movl    $162, %eax              CC SYS_nanosleep
+dnl    leal    timespec, %ebx
+dnl    xorl    %ecx, %ecx
+dnl    int     $0x80
+       leal    8(%esp), %ebp           CC ebp = start of load area
+       subl    $8, %esp                CC (%esp) = primary fd, 4(%esp) = 
secondary fd
+       movl    $-1, 4(%esp)
+.next_action:
+       movl    (%ebp), %edx            CC edx = action number
+       andl    $-17, %edx
+       cmpl    $0, %edx                CC open file?
+       je      .open_file
+       cmpl    $3, %edx                CC jump?
+       je      .rest_of_exec
+       cmpl    $4, %edx                CC anonymous mmap?
+       je      .do_mmap_anon
+.do_mmap:
+       subl    $24, %esp
+       movl    $90, %eax               CC SYS_old_mmap
+       movl    %esp, %ebx
+       movl    4(%ebp), %ecx           CC address
+       movl    %ecx, (%esp)
+       movl    16(%ebp), %ecx          CC length
+       movl    %ecx, 4(%esp)
+       movl    12(%ebp), %ecx          CC protection
+       movl    %ecx, 8(%esp)
+       movl    20(%ebp), %ecx          CC flags
+       movl    %ecx, 12(%esp)
+       testl   $16, (%ebp)             CC primary?
+       movl    28(%esp), %ecx
+       cmovzl  24(%esp), %ecx
+       movl    %ecx, 16(%esp)          CC fd
+       movl    8(%ebp), %ecx           CC offset
+       movl    %ecx, 20(%esp)
+.do_mmap_1:
+       int     $0x80
+       addl    $24, %esp               CC restore esp
+       cmpl    $-1, %eax               CC mmap failed?
+       je      .perror
+       movl    24(%ebp), %ecx          CC clear
+       testl   %ecx, %ecx
+       jz      .continue
+       movl    4(%ebp), %esi           CC start of mapping
+       addl    16(%ebp), %esi          CC end of mapping
+       subl    %ecx, %esi              CC start of clear area
+.again:
+       testl   %ecx, %ecx
+       jz      .continue
+       subl    $1, %ecx
+       movb    $0, (%esi, %ecx, 1)
+       jmp     .again
+.continue:
+       leal    28(%ebp), %ebp
+       jmp     .next_action
+.do_mmap_anon:
+       subl    $24, %esp
+       movl    $90, %eax               CC SYS_old_mmap
+       movl    %esp, %ebx
+       movl    4(%ebp), %ecx           CC address
+       movl    %ecx, (%esp)
+       movl    16(%ebp), %ecx          CC length
+       movl    %ecx, 4(%esp)
+       movl    12(%ebp), %ecx          CC protection
+       movl    %ecx, 8(%esp)
+       movl    20(%ebp), %ecx          CC flags
+       movl    %ecx, 12(%esp)
+       movl    $-1, 16(%esp)           CC fd
+       movl    8(%ebp), %ecx           CC offset
+       movl    %ecx, 20(%esp)
+       jmp     .do_mmap_1
+.open_file:
+       movl    $5, %eax                CC SYS_open
+       leal    4(%ebp), %ebx           CC ebx = %esp + 8
+       pushl   %ebx
+       xorl    %ecx, %ecx              CC flags = O_RDONLY
+       xorl    %edx, %edx              CC mode = 0
+       int     $0x80
+       cmpl    $-1, %eax               CC open failed?
+       jle     .perror
+       movl    %ebp, %esi              CC (esi) = original action number
+       popl    %ebp                    CC ebp = start of string
+       movl    %ebp, %ecx              CC char past separator
+       decl    %ebp
+.nextc:
+       incl    %ebp
+       movb    (%ebp), %dl             CC dl = *ebp
+       cmpb    $47, %dl                CC dl == '\?'?
+       jne     .nextc1
+       leal    1(%ebp), %ecx           CC ecx = char past separator
+.nextc1:
+       cmpb    $0, %dl                 CC dl == 0?
+       jne     .nextc
+       addl    $4, %ebp                CC adjust past ebp prior to rounding
+       andl    $-4, %ebp               CC round ebp up to the next long
+       testl   $16, (%esi)             CC original action number & 16?
+       jz      .primary
+       movl    %eax, 4(%esp)           CC secondary fd = eax
+       jmp     .next_action
+.primary:
+       pushl   %ebp
+       xorl    %esi, %esi              CC arg3
+       movl    %eax, 4(%esp)           CC primary fd = eax
+       xorl    %edx, %edx              CC arg2
+       movl    $15, %ebx               CC PR_SET_NAME, arg1 = ecx
+       xorl    %edi, %edi              CC arg4
+       movl    $172, %eax              CC SYS_prctl
+       xorl    %ebp, %ebp              CC arg5
+       int     $0x80                   CC syscall
+       popl    %ebp
+       jmp     .next_action
+.perror:
+       movl    %eax, %ebx
+       negl    %ebx
+       movl    $1, %eax
+       int     $0x80
+.rest_of_exec:
+       movl    8(%esp), %ecx           CC ecx = original stack pointer
+       movl    (%ecx), %esi            CC esi = argc
+       leal    8(%ecx, %esi, 4), %ecx  CC ecx = start of environ
+.skip_environ:
+       movl    (%ecx), %esi            CC envp[N]
+       addl    $4, %ecx
+       testl   %esi, %esi              CC envp[n] ?
+       jnz     .skip_environ           CC otherwise, esi is now at the start 
of auxv
+.one_auxv:
+       movl    (%ecx), %esi            CC auxv type
+       leal    8(%ecx), %ecx           CC skip to next auxv
+       testl   %esi, %esi              CC is 0?
+       jz      .cleanup
+       cmpl    $3, %esi                CC is AT_PHDR
+       je      .replace_phdr
+       cmpl    $4, %esi                CC is AT_PHENT?
+       je      .replace_phent
+       cmpl    $5, %esi                CC is AT_PHNUM?
+       je      .replace_phnum
+       cmpl    $9, %esi                CC is AT_ENTRY?
+       je      .replace_entry
+       cmpl    $7, %esi                CC is AT_BASE
+       je      .replace_base
+       jmp     .one_auxv
+.replace_phdr:
+       movl    20(%ebp), %esi
+       movl    %esi, -4(%ecx)
+       jmp     .one_auxv
+.replace_phent:
+       movl    12(%ebp), %esi
+       movl    %esi, -4(%ecx)
+       jmp     .one_auxv
+.replace_phnum:
+       movl    16(%ebp), %esi
+       movl    %esi, -4(%ecx)
+       jmp     .one_auxv
+.replace_entry:
+       movl    8(%ebp), %esi
+       movl    %esi, -4(%ecx)
+       jmp     .one_auxv
+.replace_base:
+       movl    24(%ebp), %esi
+       movl    %esi, -4(%ecx)
+       jmp     .one_auxv
+.cleanup:
+       movl    $6, %eax                CC SYS_close
+       cmpl    $-1, 4(%esp)            CC see if interpreter fd is set
+       je      .cleanup_1
+       movl    4(%esp), %ebx
+       int     $0x80
+       movl    $6, %eax                CC SYS_close
+.cleanup_1:
+       movl    (%esp), %ebx
+       int     $0x80
+.enter:
+       pushl   $0
+       popfl                           CC restore floating point state
+       movl    8(%esp), %esp           CC restore initial stack pointer
+       xorl    %edx, %edx              CC clear rtld_fini
+       jmpl    *4(%ebp)                CC entry
+
+timespec:
+       .long   10
+       .long   10
diff --git a/exec/loader-x86_64.s b/exec/loader-x86_64.s
new file mode 100644
index 00000000000..acba609b202
--- /dev/null
+++ b/exec/loader-x86_64.s
@@ -0,0 +1,195 @@
+define(`CC', `
+dnl')
+
+CC Copyright (C) 2023 Free Software Foundation, Inc.
+CC
+CC This file is part of GNU Emacs.
+CC
+CC GNU Emacs is free software: you can redistribute it and/or modify
+CC it under the terms of the GNU General Public License as published
+CC by the Free Software Foundation, either version 3 of the License,
+CC or (at your option) any later version.
+CC
+CC GNU Emacs is distributed in the hope that it will be useful, but
+CC WITHOUT ANY WARRANTY; without even the implied warranty of
+CC MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+CC General Public License for more details.
+CC
+CC You should have received a copy of the GNU General Public License
+CC along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+       .section .text
+       .global _start
+_start:
+dnl    movq    $35, %rax               CC SYS_nanosleep
+dnl    leaq    timespec(%rip), %rdi
+dnl    xorq    %rsi, %rsi
+dnl    syscall
+       popq    %r13                    CC original SP
+       popq    %r15                    CC size of load area.
+       movq    $-1, %r12               CC r12 is the interpreter fd
+.next_action:
+       movq    (%rsp), %r14            CC action number
+       movq    %r14, %r15              CC original action number
+       andq    $-17, %r14
+       cmpq    $0, %r14                CC open file?
+       je      .open_file
+       cmpq    $3, %r14                CC jump?
+       je      .rest_of_exec
+       cmpq    $4, %r14                CC anonymous mmap?
+       je      .do_mmap_anon
+.do_mmap:
+       movq    $9, %rax                CC SYS_mmap
+       movq    8(%rsp), %rdi           CC address
+       movq    16(%rsp), %r9           CC offset
+       movq    24(%rsp), %rdx          CC protection
+       movq    32(%rsp), %rsi          CC length
+       movq    40(%rsp), %r10          CC flags
+                                       CC set r8 to the primary fd unless r15 
& 16
+       testq   $16, %r15
+       movq    %r12, %r8
+       cmovzq  %rbx, %r8
+.do_mmap_1:
+       syscall
+       cmpq    $-1, %rax               CC mmap failed
+       je      .perror
+       movq    48(%rsp), %r9           CC clear
+       testq   %r9, %r9
+       jz      .continue
+       movq    8(%rsp), %r10           CC start of mapping
+       addq    32(%rsp), %r10          CC end of mapping
+       subq    %r9, %r10               CC start of clear area
+.again:
+       testq   %r9, %r9
+       jz      .continue
+       subq    $1, %r9
+       movb    $0, (%r10, %r9, 1)
+       jmp     .again
+.continue:
+       leaq    56(%rsp), %rsp
+       jmp     .next_action
+.do_mmap_anon:
+       movq    $9, %rax                CC SYS_mmap
+       movq    8(%rsp), %rdi           CC address
+       movq    16(%rsp), %r9           CC offset
+       movq    24(%rsp), %rdx          CC protection
+       movq    32(%rsp), %rsi          CC length
+       movq    40(%rsp), %r10          CC flags
+       movq    $-1, %r8                CC -1
+       jmp     .do_mmap_1
+.open_file:
+       movq    $2, %rax                CC SYS_open
+       leaq    8(%rsp), %rdi           CC rdi = %rsp + 8
+       xorq    %rsi, %rsi              CC flags = O_RDONLY
+       xorq    %rdx, %rdx              CC mode = 0
+       syscall
+       cmpq    $-1, %rax               CC open failed
+       jle     .perror
+       movq    %rdi, %rsp              CC rsp = start of string
+       subq    $1, %rsp
+       movq    %rsp, %r14              CC r14 = start of string
+.nextc:
+       addq    $1, %rsp
+       movb    (%rsp), %dil            CC rdi = *rsp
+       cmpb    $47, %dil               CC *rsp == '/'?
+       jne     .nextc1
+       movq    %rsp, %r14              CC r14 = rsp
+       addq    $1, %r14                CC r14 = char past separator
+.nextc1:
+       cmpb    $0, %dil                CC *rsp == 0?
+       jne     .nextc
+       addq    $8, %rsp                CC adjust past rsp prior to rounding
+       andq    $-8, %rsp               CC round rsp up to the next quad
+       testq   $16, %r15               CC r15 & 16?
+       jz      .primary
+       movq    %rax, %r12              CC otherwise, move fd to r12
+       jmp     .next_action
+.primary:
+       movq    %rax, %rbx              CC if not, move fd to rbx
+       movq    $157, %rax              CC SYS_prctl
+       movq    $15, %rdi               CC PR_SET_NAME
+       movq    %r14, %rsi              CC arg1
+       xorq    %rdx, %rdx              CC arg2
+       xorq    %r10, %r10              CC arg3
+       xorq    %r8, %r8                CC arg4
+       xorq    %r9, %r9                CC arg5
+       syscall
+       jmp     .next_action
+.perror:
+       movq    %rax, %r12              CC error code
+       negq    %r12
+       movq    $1, %rax                CC SYS_write
+       movq    $1, %rdi                CC stdout
+       leaq    error(%rip), %rsi       CC buffer
+       movq    $23, %rdx               CC count
+       syscall
+       movq    $60, %rax               CC SYS_exit
+       movq    %r12, %rdi              CC code
+       syscall
+.rest_of_exec:                         CC rsp now points to six quads:
+       movq    %rsp, %r8               CC now, they are r8
+       movq    %r13, %rsp              CC restore SP
+       popq    %r10                    CC argc
+       leaq    8(%rsp,%r10,8), %rsp    CC now at start of environ
+.skip_environ:
+       popq    %r10                    CC envp[N]
+       testq   %r10, %r10              CC envp[n]?
+       jnz     .skip_environ           CC otherwise, rsp is now at the start 
of auxv
+.one_auxv:
+       popq    %rcx                    CC auxv type
+       addq    $8, %rsp                CC skip value
+       testq   %rcx, %rcx              CC is 0?
+       jz      .cleanup
+       cmpq    $3, %rcx                CC is AT_PHDR?
+       je      .replace_phdr
+       cmpq    $4, %rcx                CC is AT_PHENT?
+       je      .replace_phent
+       cmpq    $5, %rcx                CC is AT_PHNUM?
+       je      .replace_phnum
+       cmpq    $9, %rcx                CC is AT_ENTRY?
+       je      .replace_entry
+       cmpq    $7, %rcx                CC is AT_BASE?
+       je      .replace_base
+       jmp     .one_auxv
+.replace_phdr:
+       movq    40(%r8), %r9
+       movq    %r9, -8(%rsp)           CC set at_phdr
+       jmp     .one_auxv
+.replace_phent:
+       movq    24(%r8), %r9
+       movq    %r9, -8(%rsp)           CC set at_phent
+       jmp     .one_auxv
+.replace_phnum:
+       movq    32(%r8), %r9
+       movq    %r9, -8(%rsp)           CC set at_phnum
+       jmp     .one_auxv
+.replace_entry:
+       movq    16(%r8), %r9
+       movq    %r9, -8(%rsp)           CC set at_entry
+       jmp     .one_auxv
+.replace_base:
+       movq    48(%r8), %r9
+       movq    %r9, -8(%rsp)           CC set at_base
+       jmp     .one_auxv
+.cleanup:
+       movq    $3, %rax                CC SYS_close
+       cmpq    $-1, %r12               CC see if interpreter fd is set
+       je      .cleanup_1
+       movq    %r12, %rdi
+       syscall
+       movq    $3, %rax                CC SYS_close
+.cleanup_1:
+       movq    %rbx, %rdi
+       syscall
+.enter:
+       pushq   $0
+       popfq                           CC clear FP state
+       movq    %r13, %rsp              CC restore SP
+       xorq    %rdx, %rdx              CC clear rtld_fini
+       jmpq    *8(%r8)                 CC entry
+
+error:
+       .ascii  "_start: internal error."
+timespec:
+       .quad   10
+       .quad   10
diff --git a/exec/mipsel-user.h b/exec/mipsel-user.h
new file mode 100644
index 00000000000..dc3f98eb4e7
--- /dev/null
+++ b/exec/mipsel-user.h
@@ -0,0 +1,43 @@
+/* Program execution for Emacs.
+
+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/>.  */
+
+
+
+#ifndef _MIPSEL_USER_H_
+#define _MIPSEL_USER_H_
+
+#include <sys/user.h>
+
+#ifndef ELF_NGREG
+#define ELF_NGREG       45
+#endif /* ELF_NGREG */
+
+
+
+/* This file defines a structure containing user mode general purpose
+   registers on 32-bit mipsel systems.  */
+
+struct mipsel_regs
+{
+  /* General purpose registers.  */
+  uint64_t gregs[ELF_NGREG];
+};
+
+#endif /* _MIPSEL_USER_H_ */
+
diff --git a/exec/mipsfpu.c b/exec/mipsfpu.c
new file mode 100644
index 00000000000..f5fa5720804
--- /dev/null
+++ b/exec/mipsfpu.c
@@ -0,0 +1,289 @@
+/* Program execution for Emacs.
+
+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 <errno.h>
+
+#include "mipsfpu.h"
+
+
+
+/* OABI MIPS systems support several different modes of execution.
+   Each mode differs in the size and utilization of the hardware
+   floating-point registers.
+
+   Linux normally sets the floating point mode to one appropriate for
+   execution, taking into account the floating point modes of the
+   interpreter and executable binaries.  However, this logic is
+   forsaken when the `execve' system call is overwritten.
+
+   Thus, the correct floating point mode must be determined and set
+   within the loader binary.  */
+
+
+
+/* Various constants used throughout this code.  */
+
+#define MIPS_ABI_FP_ANY                0       /* FP ABI doesn't matter */
+#define MIPS_ABI_FP_DOUBLE     1       /* -mdouble-float */
+#define MIPS_ABI_FP_SINGLE     2       /* -msingle-float */
+#define MIPS_ABI_FP_SOFT       3       /* -msoft-float */
+#define MIPS_ABI_FP_OLD_64     4       /* -mips32r2 -mfp64 */
+#define MIPS_ABI_FP_XX         5       /* -mfpxx */
+#define MIPS_ABI_FP_64         6       /* -mips32r2 -mfp64 */
+#define MIPS_ABI_FP_64A                7       /* -mips32r2 -mfp64 
-mno-odd-spreg */
+
+#define EF_MIPS_NOREORDER      1     /* A .noreorder directive was used.  */
+#define EF_MIPS_PIC            2     /* Contains PIC code.  */
+#define EF_MIPS_CPIC           4     /* Uses PIC calling sequence.  */
+#define EF_MIPS_XGOT           8
+#define EF_MIPS_64BIT_WHIRL    16
+#define EF_MIPS_ABI2           32
+#define EF_MIPS_ABI_ON32       64
+#define EF_MIPS_FP64           512  /* Uses FP64 (12 callee-saved).  */
+#define EF_MIPS_NAN2008                1024  /* Uses IEEE 754-2008 NaN 
encoding.  */
+#define EF_MIPS_ARCH           0xf0000000 /* MIPS architecture level.  */
+
+
+
+/* Structure describing the requirements of a single floating-point
+   ABI.  */
+
+struct mode_description
+{
+  /* Whether or not the ABI only executes single precision
+     instructions, and can operate in both 32-bit or 64-bit floating
+     point mode.  */
+  bool single;
+
+  /* Whether or not the ABI performs floating point operations in
+     software, using integer registers.  */
+  bool soft;
+
+  /* Whether or not the ABI requires the use of 64-bit floating point
+     registers.  */
+  bool fr1;
+
+  /* Whether or not the ABI requires the use of 64-bit floating point
+     registers on NABI systems, and 32-bit ones on OABI systems.  */
+  bool frdefault;
+
+  /* Whether or not this ABI requires single precision floating point
+     emulation.  */
+  bool fre;
+};
+
+static struct mode_description fpu_reqs[] =
+  {
+    [MIPS_ABI_FP_ANY]    = { true,  true,  true,  true,  true,  },
+    [MIPS_ABI_FP_DOUBLE] = { false, false, false, true,  true,  },
+    [MIPS_ABI_FP_SINGLE] = { true,  false, false, false, false, },
+    [MIPS_ABI_FP_SOFT]   = { false, true,  false, false, false, },
+    [MIPS_ABI_FP_OLD_64] = { false, false, false, false, false, },
+    [MIPS_ABI_FP_XX]     = { false, false, true,  true,  true,  },
+    [MIPS_ABI_FP_64]     = { false, false, true,  false, false, },
+    [MIPS_ABI_FP_64A]    = { false, false, true,  false, true,  },
+  };
+
+
+
+/* Return whether or not the given floating-point ABI is valid.  */
+
+static bool
+valid_abi_p (int abi)
+{
+  switch (abi)
+    {
+    case MIPS_ABI_FP_ANY:
+    case MIPS_ABI_FP_DOUBLE:
+    case MIPS_ABI_FP_SINGLE:
+    case MIPS_ABI_FP_SOFT:
+    case MIPS_ABI_FP_OLD_64:
+    case MIPS_ABI_FP_XX:
+    case MIPS_ABI_FP_64:
+    case MIPS_ABI_FP_64A:
+      return true;
+
+    default:
+      return false;
+    }
+}
+
+/* Return the floating point mode appropriate for the specified
+   floating point ABI.  */
+
+static int
+fp_mode_for_abi (int abi)
+{
+  struct mode_description *desc;
+
+  desc = &fpu_reqs[abi];
+
+  if (desc->fre)
+    return FP_FRE;
+  else if (desc->fr1)
+    return FP_FR1;
+
+  return FP_FR0;
+}
+
+/* Determine whether or not the CPU is capable of operating in FR0
+   floating point mode.  */
+
+bool
+cpu_supports_fr0_p (void)
+{
+#if defined __mips_isa_rev && __mips_isa_rev >= 6
+  return true;
+#else /* !defined __mips_isa_rev | mips_isa_rev < 6 */
+  return false;
+#endif /* defined __mips_isa_rev && mips_isa_rev >= 6 */
+}
+
+/* Determine the FPU mode for the executable whose ELF header is
+   HEADER.  If INTERPRETER is non-NULL, also take an interpreter whose
+   header is INTERPRETER into account.
+
+   ABIFLAGS should be HEADER's corresponding PT_MIPS_ABIFLAGS program
+   header, and ABIFLAGS1 should be that of INTERPRETER, if set.  Both
+   fields may be NULL if no PT_MIPS_ABIFLAGS header is present; in
+   that case, use HEADER->e_flags to determine the ABI instead.
+
+   Return the FPU mode in *MODE.  Value is 0 upon success, 1
+   otherwise, with errno set.  */
+
+int
+determine_fpu_mode (elf_header *header, elf_header *interpreter,
+                   int *mode, struct mips_elf_abi_flags *abiflags,
+                   struct mips_elf_abi_flags *abiflags1)
+{
+  int exec_abi, interpreter_abi;
+  struct mode_description *exec_desc, *interpreter_desc, common;
+
+  /* Figure out the executable's floating point ABI.  First, consult
+     header->e_flags, and use the old 64-bit floating point ABI if it
+     is specified.  */
+
+  exec_abi = MIPS_ABI_FP_ANY;
+
+  /* First, check HEADER->e_flags.  */
+
+  if (header->e_flags & EF_MIPS_FP64)
+    exec_abi = MIPS_ABI_FP_OLD_64;
+
+  /* Next, use ABIFLAGS if it exists.  */
+
+  if (abiflags && valid_abi_p (abiflags->fp_abi))
+    exec_abi = abiflags->fp_abi;
+  else if (abiflags)
+    {
+      errno = ENOEXEC;
+      return 1;
+    }
+
+  /* Now determine that of the interpreter.  */
+
+  interpreter_abi = MIPS_ABI_FP_ANY;
+
+  if (interpreter)
+    {
+      if (interpreter->e_flags & EF_MIPS_FP64)
+       interpreter_abi = MIPS_ABI_FP_OLD_64;
+
+      if (abiflags1 && valid_abi_p (abiflags->fp_abi))
+       interpreter_abi = abiflags->fp_abi;
+      else if (abiflags1)
+       {
+         errno = ELIBBAD;
+         return 1;
+       }
+    }
+
+  /* If no interpreter flag is set, just return that of the
+     executable.  */
+
+  if (!interpreter)
+    {
+      *mode = fp_mode_for_abi (exec_abi);
+      return 0;
+    }
+
+  /* Otherwise, compare both ABIs and try to find one which will run
+     both kinds of code.
+
+     First, see if there's an easy way out: both ABIs are identical,
+     or one ABI is MIPS_ABI_FP_ANY.  */
+
+  if (exec_abi == interpreter_abi)
+    {
+      *mode = fp_mode_for_abi (exec_abi);
+      return 0;
+    }
+  else if (exec_abi == MIPS_ABI_FP_ANY)
+    {
+      *mode = fp_mode_for_abi (interpreter_abi);
+      return 0;
+    }
+  else if (interpreter_abi == MIPS_ABI_FP_ANY)
+    {
+      *mode = fp_mode_for_abi (exec_abi);
+      return 0;
+    }
+
+  /* If that doesn't work, compare various characteristics of both
+     ABIs and select an appropriate floating point mode.  */
+
+  exec_desc = &fpu_reqs[exec_abi];
+  interpreter_desc = &fpu_reqs[interpreter_abi];
+
+  /* Merge both sets of requirements.  */
+  common.single = exec_desc->single && interpreter_desc->single;
+  common.soft = exec_desc->soft && interpreter_desc->soft;
+  common.fr1 = exec_desc->fr1 && interpreter_desc->fr1;
+  common.frdefault = exec_desc->frdefault && interpreter_desc->frdefault;
+  common.fre = exec_desc->fre && interpreter_desc->fre;
+
+  /* Default to a mode capable of running code expecting 32-bit
+     registers.  */
+
+  if (!(header->e_flags & EF_MIPS_ABI2))
+    *mode = FP_FR0;
+  else
+    /* But in this case, use FR1.  */
+    *mode = FP_FR1;
+
+  if (common.fre && !common.frdefault && !common.fr1)
+    /* Floating point emulation mode is required.  */
+    *mode = FP_FRE;
+  else if ((common.fr1 && common.frdefault)
+          || (common.single && !common.frdefault)
+          || common.fr1)
+    /* 64-bit mode is required.  */
+    *mode = FP_FR1;
+  else if (!common.fre && !common.frdefault
+          && !common.fr1 && !common.single
+          && !common.soft)
+    {
+      /* The floating point modes specified are incompatible.  */
+      errno = ELIBBAD;
+      return -1;
+    }
+
+  return 0;
+}
diff --git a/exec/mipsfpu.h b/exec/mipsfpu.h
new file mode 100644
index 00000000000..2315db59e93
--- /dev/null
+++ b/exec/mipsfpu.h
@@ -0,0 +1,82 @@
+/* Program execution for Emacs.
+
+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/>.  */
+
+
+
+#ifndef _MIPSFPU_H_
+#define _MIPSFPU_H_
+
+#include "exec.h"
+
+struct mips_elf_abi_flags
+{
+  /* Version of flags structure.  */
+  uint16_t version;
+
+  /* The level of the ISA: 1-5, 32, 64.  */
+  uint8_t isa_level;
+
+  /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise.  */
+  uint8_t isa_rev;
+
+  /* The size of general purpose registers.  */
+  uint8_t gpr_size;
+
+  /* The size of co-processor 1 registers.  */
+  uint8_t cpr1_size;
+
+  /* The size of co-processor 2 registers.  */
+  uint8_t cpr2_size;
+
+  /* The floating-point ABI.  */
+  uint8_t fp_abi;
+
+  /* Mask of processor-specific extensions.  */
+  uint32_t isa_ext;
+
+  /* Mask of ASEs used.  */
+  uint32_t ases;
+
+  /* Mask of general flags.  */
+  uint32_t flags1;
+
+  /* Mask of general flags.  */
+  uint32_t flags2;
+};
+
+
+
+/* Floating point modes.  */
+
+#define FP_FR0         0
+#define FP_FR1         1
+#define FP_FRE         3
+
+
+
+/* Defined in mipsfpu.c.  */
+
+extern bool cpu_supports_fr0_p (void);
+extern int determine_fpu_mode (elf_header *, elf_header *,
+                              int *, struct mips_elf_abi_flags *,
+                              struct mips_elf_abi_flags *);
+
+
+
+#endif /* _MIPSFPU_H_ */
diff --git a/exec/test.c b/exec/test.c
new file mode 100644
index 00000000000..fa2a848837c
--- /dev/null
+++ b/exec/test.c
@@ -0,0 +1,105 @@
+/* Program execution for Emacs.
+
+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 <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include <sys/wait.h>
+
+#include "exec.h"
+
+
+
+static void
+print_usage (void)
+{
+  fprintf (stderr, "test loader-name program [args...]\n"
+          "Run the given program using the specified loader.\n");
+}
+
+
+
+extern char **environ;
+
+/* This program uses libexec to wrap the execution of a child
+   process.  */
+
+int
+main (int argc, char **argv)
+{
+  pid_t pid, child;
+  int sig;
+  sigset_t sigset;
+
+  /* Check that there are a sufficient number of arguments.  */
+
+  if (argc < 3)
+    {
+      print_usage ();
+      return 1;
+    }
+
+  exec_init (argv[1]);
+
+  /* Block SIGCHLD to avoid reentrant modification of the child
+     process list.  */
+
+  sigemptyset (&sigset);
+  sigaddset (&sigset, SIGCHLD);
+  sigprocmask (SIG_BLOCK, &sigset, NULL);
+
+  if (!(pid = fork ()))
+    {
+      tracing_execve (argv[2], argv + 2, environ);
+      fprintf (stderr, "tracing_execve: %s\n",
+              strerror (errno));
+      exit (1);
+    }
+  else if (after_fork (pid))
+    {
+      fprintf (stderr, "after_fork: %s\n",
+              strerror (errno));
+      exit (1);
+    }
+
+  /* Now start waiting for child processes to exit.  */
+
+  while (true)
+    {
+      child = exec_waitpid (-1, &sig, 0);
+
+      /* If pid is -1, a system call has been handled.  */
+
+      if (child == -1)
+       continue;
+
+      /* If the main process exits, then exit as well.  */
+
+      if (child == pid && !WIFSTOPPED (sig))
+       return (WIFEXITED (sig)
+               ? WEXITSTATUS (sig)
+               : WTERMSIG (sig));
+    }
+}
diff --git a/exec/trace.c b/exec/trace.c
new file mode 100644
index 00000000000..3b384792d0a
--- /dev/null
+++ b/exec/trace.c
@@ -0,0 +1,1432 @@
+/* Program execution for Emacs.
+
+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 <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "exec.h"
+
+#include SYSCALL_HEADER
+#include USER_HEADER
+
+#ifdef __aarch64__
+#include <sys/uio.h> /* for struct iovec */
+#include <linux/elf.h> /* for NT_* */
+#endif /* __aarch64__ */
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h> /* for process_vm_readv */
+#endif /* HAVE_SYS_UIO_H */
+
+#ifndef SYS_SECCOMP
+#define SYS_SECCOMP 1
+#endif /* SYS_SECCOMP */
+
+#ifndef PTRACE_GETEVENTMSG
+#define PTRACE_GETEVENTMSG 0x4201
+#endif /* PTRACE_GETEVENTMSG */
+
+
+
+/* Program tracing functions.
+
+   The main entry point is the function `tracing_execve', which traces
+   the thread and calls exec.  Each time that thread calls `clone',
+   the new child is traced as well.
+
+   Instead of calling `waitpid', call `exec_waitpid' instead.  */
+
+
+
+/* Number of tracees children are allowed to create.  */
+#define MAX_TRACEES 4096
+
+#ifdef __aarch64__
+
+/* Place PID's registers into *REGS.  Return 1 upon failure, else
+   0.  */
+
+int
+aarch64_get_regs (pid_t pid, USER_REGS_STRUCT *regs)
+{
+  struct iovec iov;
+
+  iov.iov_base = regs;
+  iov.iov_len = sizeof *regs;
+
+  return (ptrace (PTRACE_GETREGSET, pid, NT_PRSTATUS,
+                 &iov) != 0);
+}
+
+/* Set PID's registers to *REGS.  If SYSCALL_P, also update the
+   current system call number to the `x8' register.
+
+   Value is 1 upon failure, else 0.  */
+
+int
+aarch64_set_regs (pid_t pid, USER_REGS_STRUCT *regs,
+                 bool syscall_p)
+{
+  struct iovec iov;
+  USER_WORD callno;
+  long rc;
+
+  /* Write the user registers.  */
+
+  iov.iov_base = regs;
+  iov.iov_len = sizeof *regs;
+
+  rc = ptrace (PTRACE_SETREGSET, pid, NT_PRSTATUS,
+              &iov);
+  if (rc < 0)
+    return 1;
+
+  /* Now, write the system call number if necessary.  */
+
+  if (syscall_p)
+    {
+      callno = regs->regs[8];
+      iov.iov_base = &callno;
+      iov.iov_len = sizeof callno;
+
+      return (ptrace (PTRACE_SETREGSET, pid, NT_ARM_SYSTEM_CALL,
+                     &iov) != 0);
+    }
+
+  return 0;
+}
+
+#endif /* __aarch64__ */
+
+
+
+/* List of all processes which are being traced.  */
+static struct exec_tracee *tracing_processes;
+
+
+
+/* Read N bytes from TRACEE's memory, starting at the specified user
+   ADDRESS.  Return its contents in BUFFER.
+
+   If there are unreadable pages within ADDRESS + N, the contents of
+   BUFFER after the first such page becomes undefined.  */
+
+static void
+read_memory (struct exec_tracee *tracee, char *buffer,
+            USER_WORD n, USER_WORD address)
+{
+  USER_WORD word, n_words, n_bytes, i;
+  long rc;
+#ifdef HAVE_PROCESS_VM
+  struct iovec iov, remote;
+
+  /* If `process_vm_readv' is available, use it instead.  */
+
+  iov.iov_base = buffer;
+  iov.iov_len = n;
+  remote.iov_base = (void *) address;
+  remote.iov_len = n;
+
+  /* Return immediately if successful.  As long as some bytes were
+     read, consider the read to have been a success.  */
+
+  if (n <= SSIZE_MAX
+      && ((size_t) process_vm_readv (tracee->pid, &iov, 1,
+                                    &remote, 1, 0) != -1))
+    return;
+
+#endif /* HAVE_PROCESS_VM */
+
+  /* First, read entire words from the tracee.  */
+  n_words = n & ~(sizeof (USER_WORD) - 1);
+
+  /* Next, determine the number of bytes to read from the last
+     word.  */
+  n_bytes = n & (sizeof (USER_WORD) - 1);
+
+  /* Start reading words.  */
+  i = 0;
+  while (n_words)
+    {
+      rc = ptrace (PTRACE_PEEKTEXT, tracee->pid,
+                  (void *) address + i, NULL);
+      word = rc;
+      memcpy (buffer, &word, sizeof word);
+      buffer += sizeof word;
+      i += sizeof word;
+      n_words -= sizeof word;
+    }
+
+  /* Now, read the remaining bytes.  */
+  assert (n_bytes < sizeof (word));
+
+  if (n_bytes)
+    {
+      rc = ptrace (PTRACE_PEEKTEXT, tracee->pid,
+                  (void *) address + i, NULL);
+      word = rc;
+
+      /* Copy only n_bytes to the caller.  */
+      memcpy (buffer, &word, n_bytes);
+    }
+}
+
+/* Allocate N bytes of memory from TRACEE's stack.  Return the address
+   of that memory upon success, else 0.
+
+   Place the updated user-mode registers of TRACEE in *NEW_REGS, which
+   should initially contain the current stack pointer of TRACEE.
+
+   REGS should contain the user mode registers of TRACEE prior to the
+   system call starting; it is not updated to reflect any changes.  */
+
+USER_WORD
+user_alloca (struct exec_tracee *tracee, USER_REGS_STRUCT *regs,
+            USER_REGS_STRUCT *new_regs, USER_WORD n)
+{
+  USER_WORD sp, old_sp;
+
+  /* Get the current stack pointer.  */
+  old_sp = sp = new_regs->STACK_POINTER;
+
+#if RED_ZONE_SIZE
+  /* Some ABI rules specify a ``red zone'' around the stack pointer
+     that is reserved for compiler optimizations.  */
+
+#ifdef STACK_GROWS_DOWNWARDS
+  if (sp == regs->STACK_POINTER)
+    sp -= RED_ZONE_SIZE;
+#else /* !STACK_GROWS_DOWNWARDS */
+  if (sp == regs->STACK_POINTER)
+    sp += RED_ZONE_SIZE;
+#endif /* STACK_GROWS_DOWNWARDS */
+#endif /* RED_ZONE_SIZE */
+
+  /* Now take N off the stack.  */
+
+#ifdef STACK_GROWS_DOWNWARDS
+  sp = sp - n;
+
+  /* Check for overflow.  */
+
+  if (sp > new_regs->STACK_POINTER)
+    return 0;
+#else /* !STACK_GROWS_DOWNWARDS */
+  sp = sp + n;
+
+  /* Check for overflow.  */
+
+  if (sp < new_regs->STACK_POINTER)
+    return 0;
+#endif /* STACK_GROWS_DOWNWARDS */
+
+  /* Set the stack pointer.  */
+  new_regs->STACK_POINTER = sp;
+
+#ifdef __aarch64__
+  if (aarch64_set_regs (tracee->pid, new_regs, false))
+    goto fail;
+#else /* !__aarch64__ */
+  if (ptrace (PTRACE_SETREGS, tracee->pid, NULL,
+             new_regs))
+    goto fail;
+#endif /* __aarch64__ */
+
+  /* Now return the start of the new area.  */
+#ifdef STACK_GROWS_DOWNWARDS
+  return sp;
+#else /* !STACK_GROWS_DOWNWARDS */
+  return sp - n;
+#endif /* STACK_GROWS_DOWNWARDS */
+
+ fail:
+  /* Restore the old stack pointer.  */
+  new_regs->STACK_POINTER = old_sp;
+  return 0;
+}
+
+/* Copy N bytes to ADDRESS in TRACEE's address space from BUFFER.
+   Value is 0 upon success, else 1.  */
+
+int
+user_copy (struct exec_tracee *tracee, const unsigned char *buffer,
+          USER_WORD address, USER_WORD n)
+{
+  USER_WORD start, end, word;
+  unsigned char *bytes;
+#ifdef HAVE_PROCESS_VM
+  struct iovec iov, remote;
+
+  /* Try to use `process_vm_writev' if possible, but fall back to
+     ptrace if something bad happens.  */
+
+  iov.iov_base = (void *) buffer;
+  iov.iov_len = n;
+  remote.iov_base = (void *) address;
+  remote.iov_len = n;
+
+  if (n <= SSIZE_MAX
+      && ((size_t) process_vm_writev (tracee->pid, &iov, 1,
+                                     &remote, 1, 0) == n))
+    return 0;
+#endif /* HAVE_PROCESS_VM */
+
+  /* Calculate the start and end positions for the write.  */
+
+  start = address;
+  end = address + n;
+
+  /* Write from start to the last word.  */
+
+  while (start < end)
+    {
+      if (start + sizeof word <= end)
+       {
+         /* Write a word by itself and increment start.  */
+         memcpy (&word, buffer, sizeof word);
+         buffer += sizeof word;
+
+         if (ptrace (PTRACE_POKEDATA, tracee->pid,
+                     (void *) start, (void *) word))
+           return 1;
+
+         start += sizeof word;
+       }
+      else
+       {
+         /* Only end - start bytes should be written.
+            Read the word at start from tracee->pid, then write
+            it back with changes.  */
+
+         word = ptrace (PTRACE_PEEKDATA, tracee->pid,
+                        (void *) start, NULL);
+         bytes = (unsigned char *) &word;
+         memcpy (bytes, buffer, end - start);
+
+         if (ptrace (PTRACE_POKEDATA, tracee->pid,
+                     (void *) start, (void *) word))
+           return 1;
+
+         /* Writing was successful.  */
+         return 0;
+       }
+    }
+
+  return 0;
+}
+
+
+
+/* Chain of free exec_tracee structures.  */
+static struct exec_tracee *free_tracees;
+
+/* Remove the specified TRACEE from the chain of all processes being
+   traced.  */
+
+static void
+remove_tracee (struct exec_tracee *tracee)
+{
+  struct exec_tracee **last;
+
+  last = &tracing_processes;
+  while (*last)
+    {
+      if (*last == tracee)
+       {
+         *last = tracee->next;
+
+         /* Link the tracee onto the list of free tracees.  */
+         tracee->next = free_tracees;
+
+#ifndef REENTRANT
+         /* Free the exec file, if any.  */
+         free (tracee->exec_file);
+         tracee->exec_file = NULL;
+#endif /* REENTRANT */
+
+         free_tracees = tracee;
+
+         return;
+       }
+      else
+       last = &(*last)->next;
+    }
+}
+
+
+
+/* Child process tracing.  */
+
+/* Array of `struct exec_tracees' that they are allocated from.  */
+static struct exec_tracee static_tracees[MAX_TRACEES];
+
+/* Number of tracees currently allocated.  */
+static int tracees;
+
+/* Return the `struct exec_tracee' corresponding to the specified
+   PROCESS.  */
+
+static struct exec_tracee *
+find_tracee (pid_t process)
+{
+  struct exec_tracee *tracee;
+
+  for (tracee = tracing_processes; tracee; tracee = tracee->next)
+    {
+      if (tracee->pid == process)
+       return tracee;
+    }
+
+  return NULL;
+}
+
+/* Prepare to handle the completion of a `clone' system call.
+
+   If the new clone is not yet being traced, create a new tracee for
+   PARENT's child, copying over its current command line.  Then, set
+   `new_child' in the new tracee.  Otherwise, continue it until the
+   next syscall.  */
+
+static void
+handle_clone_prepare (struct exec_tracee *parent)
+{
+#ifndef REENTRANT
+  long rc;
+  unsigned long pid;
+  struct exec_tracee *tracee;
+
+  rc = ptrace (PTRACE_GETEVENTMSG, parent->pid, NULL,
+              &pid);
+  if (rc)
+    return;
+
+  /* See if the tracee already exists.  */
+  tracee = find_tracee (pid);
+
+  if (tracee)
+    {
+      /* Continue the tracee.  Record its command line, as that has
+        not yet been done.  */
+
+      assert (tracee->new_child);
+      tracee->new_child = false;
+      tracee->exec_file = NULL;
+      ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0);
+
+      if (parent->exec_file)
+       tracee->exec_file = strdup (parent->exec_file);
+      return;
+    }
+
+  if (free_tracees)
+    {
+      tracee = free_tracees;
+      free_tracees = free_tracees->next;
+    }
+  else if (tracees < MAX_TRACEES)
+    {
+      tracee = &static_tracees[tracees];
+      tracees++;
+    }
+#ifndef REENTRANT
+  /* Try to allocate a tracee using `malloc' if this library is
+     not being built to run inside a signal handler.  */
+  else if ((tracee = malloc (sizeof *tracee)))
+    ;
+#endif /* REENTRANT */
+  else
+    return;
+
+  tracee->pid = pid;
+  tracee->next = tracing_processes;
+  tracee->waiting_for_syscall = false;
+  tracee->new_child = true;
+  tracee->exec_file = NULL;
+  tracing_processes = tracee;
+
+  /* Copy over the command line.  */
+
+  if (parent->exec_file)
+    tracee->exec_file = strdup (parent->exec_file);
+#endif /* REENTRANT */
+}
+
+/* Handle the completion of a `clone' or `clone3' system call,
+   resulting in the creation of the process PID.  If TRACEE is NULL,
+   allocate a new tracee structure from a static area for the
+   processes's pid, then set TRACEE->new_child to true and await the
+   parent's corresponding ptrace event to arrive; otherwise, just
+   clear TRACEE->new_child.
+
+   Value is 0 upon success, 2 if TRACEE should remain suspended until
+   the parent's ptrace-stop, and 1 otherwise.  */
+
+static int
+handle_clone (struct exec_tracee *tracee, pid_t pid)
+{
+  long rc;
+  int flags, value;
+
+  /* Now allocate a new tracee, either from static_tracees or the free
+     list, if no tracee was supplied.  */
+
+  value = 0;
+
+  if (!tracee)
+    {
+      if (free_tracees)
+       {
+         tracee = free_tracees;
+         free_tracees = free_tracees->next;
+       }
+      else if (tracees < MAX_TRACEES)
+       {
+         tracee = &static_tracees[tracees];
+         tracees++;
+       }
+#ifndef REENTRANT
+      /* Try to allocate a tracee using `malloc' if this library is
+        not being built to run inside a signal handler.  */
+      else if ((tracee = malloc (sizeof *tracee)))
+       ;
+#endif /* REENTRANT */
+      else
+       return 1;
+
+      tracee->pid = pid;
+      tracee->next = tracing_processes;
+      tracee->waiting_for_syscall = false;
+#ifndef REENTRANT
+      tracee->exec_file = NULL;
+#endif /* REENTRANT */
+      tracing_processes = tracee;
+      tracee->new_child = true;
+
+      /* Wait for the ptrace-stop to happen in the parent.  */
+      value = 2;
+    }
+  else
+    /* Clear the flag saying that this is a newly created child
+       process.  */
+    tracee->new_child = false;
+
+  /* Apply required options to the child, so that the kernel
+     automatically traces children and makes it easy to differentiate
+     between system call traps and other kinds of traps.  */
+
+  flags  = PTRACE_O_TRACECLONE;
+  flags |= PTRACE_O_TRACEVFORK;
+  flags |= PTRACE_O_TRACEFORK;
+  flags |= PTRACE_O_TRACESYSGOOD;
+  flags |= PTRACE_O_TRACEEXIT;
+
+  rc = ptrace (PTRACE_SETOPTIONS, pid, 0, flags);
+
+  if (rc)
+    goto bail;
+
+  if (value != 2)
+    {
+      /* The new tracee is currently stopped.  Continue it until the next
+        system call.  */
+
+      rc = ptrace (PTRACE_SYSCALL, pid, 0, 0);
+
+      if (rc)
+       goto bail;
+    }
+
+  return value;
+
+ bail:
+  remove_tracee (tracee);
+  return 1;
+}
+
+
+
+/* NOTICE: none of these functions should ever call `malloc' or
+   another async signal unsafe function.  */
+
+/* File name of the loader binary.  */
+static const char *loader_name;
+
+
+
+/* Return whether or not the trap signal described by SIGNAL is
+   generated by a system call being attempted by a tracee.  */
+
+static bool
+syscall_trap_p (siginfo_t *signal)
+{
+  /* SIGTRAP delivered by the kernel means this is a system call
+     stop.  */
+  return (signal->si_code == SIGTRAP
+         || signal->si_code == (SIGTRAP | SI_KERNEL));
+}
+
+/* Check if the wait status STATUS indicates a system call trap.
+   TRACEE is the process whose stop STATUS describes.  If TRACEE exits
+   while this information is being determined, return -1; if STATUS
+   indicates some other kind of stop, return 1 after continuing
+   TRACEE.  Value is 0 otherwise.  */
+
+static int
+check_signal (struct exec_tracee *tracee, int status)
+{
+  siginfo_t siginfo;
+
+  switch ((status & 0xfff00) >> 8)
+    {
+    case SIGTRAP:
+      /* Now, use PTRACE_GETSIGINFO to determine whether or not the
+        signal was delivered in response to a system call.  */
+
+      if (ptrace (PTRACE_GETSIGINFO, tracee->pid, 0, &siginfo))
+       return -1;
+
+      if (!syscall_trap_p (&siginfo))
+       {
+         if (siginfo.si_code < 0)
+           /* SIGTRAP delivered from userspace.  Pass it on.  */
+           ptrace (PTRACE_SYSCALL, tracee->pid, 0, SIGTRAP);
+         else
+           ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0);
+
+         return 1;
+       }
+
+    case SIGTRAP | 0x80: /* SIGTRAP | 0x80 specifically refers to
+                           system call traps.  */
+      break;
+
+#ifdef SIGSYS
+    case SIGSYS:
+      if (ptrace (PTRACE_GETSIGINFO, tracee->pid, 0, &siginfo))
+       return -1;
+
+      /* Continue the process until the next syscall, but don't
+        pass through the signal if an emulated syscall led to
+        it.  */
+#ifdef HAVE_SIGINFO_T_SI_SYSCALL
+#ifndef __arm__
+      ptrace (PTRACE_SYSCALL, tracee->pid,
+             0, ((siginfo.si_code == SYS_SECCOMP
+                  && siginfo.si_syscall == -1)
+                 ? 0 : status));
+#else /* __arm__ */
+      ptrace (PTRACE_SYSCALL, tracee->pid,
+             0, ((siginfo.si_code == SYS_SECCOMP
+                  && siginfo.si_syscall == 222)
+                 ? 0 : status));
+#endif /* !__arm__ */
+#else /* !HAVE_SIGINFO_T_SI_SYSCALL */
+      /* Drop this signal, since what caused it is unknown.  */
+      ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0);
+#endif /* HAVE_SIGINFO_T_SI_SYSCALL */
+      return 1;
+#endif /* SIGSYS */
+
+    default:
+      /* Continue the process until the next syscall.  */
+      ptrace (PTRACE_SYSCALL, tracee->pid, 0, status);
+      return 1;
+    }
+
+  return 0;
+}
+
+
+
+/* Handle an `exec' system call from the given TRACEE.  REGS are the
+   tracee's current user-mode registers.
+
+   Rewrite the system call arguments to use the loader binary.  Then,
+   continue the system call until the loader is loaded.  Write the
+   information necessary to load the original executable into the
+   loader's stack.
+
+   Value is 0 upon success, 1 upon a generic failure before the loader
+   is loaded, 2 if the process has stopped, and 3 if something failed,
+   but it is too late to handle it.
+
+   Set errno appropriately upon returning a generic failure.  */
+
+static int
+handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs)
+{
+  char buffer[PATH_MAX + 80], *area;
+  USER_REGS_STRUCT original;
+  size_t size, loader_size;
+  USER_WORD loader, size1, sp;
+  int rc, wstatus;
+  siginfo_t siginfo;
+
+  /* Save the old stack pointer.  */
+  sp = regs->STACK_POINTER;
+
+  /* Read the file name.  */
+  read_memory (tracee, buffer, PATH_MAX,
+              regs->SYSCALL_ARG_REG);
+
+  /* Make sure BUFFER is NULL terminated.  */
+
+  if (!memchr (buffer, '\0', PATH_MAX))
+    {
+      errno = ENAMETOOLONG;
+      return 1;
+    }
+
+  /* Copy over the registers as they originally were.  */
+  memcpy (&original, regs, sizeof *regs);
+
+  /* Figure out what the loader needs to do.  */
+ again1:
+  area = exec_0 (buffer, tracee, &size, regs);
+
+  if (!area)
+    {
+      /* Handle SIGINTR errors caused by IO.  */
+      if (errno == EINTR)
+       goto again1;
+
+      return 1;
+    }
+
+  /* Rewrite the first argument to point to the loader.  */
+
+  loader_size = strlen (loader_name) + 1;
+  loader = user_alloca (tracee, &original, regs,
+                       loader_size);
+
+  if (!loader)
+    {
+      errno = ENOMEM;
+      return 1;
+    }
+
+  if (user_copy (tracee, (unsigned char *) loader_name,
+                loader, loader_size))
+    {
+      errno = EIO;
+      return 1;
+    }
+
+  regs->SYSCALL_ARG_REG = loader;
+
+#ifdef __aarch64__
+
+  if (aarch64_set_regs (tracee->pid, regs, false))
+    {
+      errno = EIO;
+      return 1;
+    }
+
+#else /* !__aarch64__ */
+
+  if (ptrace (PTRACE_SETREGS, tracee->pid, NULL,
+             regs))
+    {
+      errno = EIO;
+      return 1;
+    }
+
+#endif /* __aarch64__ */
+
+  /* Continue the system call until loader starts.  */
+
+  if (ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL))
+    {
+      errno = EIO;
+      return 1;
+    }
+
+#ifndef REENTRANT
+  /* Now that the loader has started, record the value to use for
+     /proc/self/exe.  Don't give up just because strdup fails.
+
+     Note that exec_0 copies the absolute file name into buffer.  */
+
+  if (tracee->exec_file)
+    free (tracee->exec_file);
+  tracee->exec_file = strdup (buffer);
+#endif /* REENTRANT */
+
+ again:
+  rc = waitpid (tracee->pid, &wstatus, __WALL);
+  if (rc == -1 && errno == EINTR)
+    goto again;
+
+  if (rc < 0)
+    return 1;
+
+  if (!WIFSTOPPED (wstatus))
+    /* The process has been killed in response to a signal.
+       In this case, simply return 2.  */
+    return 2;
+  else
+    {
+      /* Then, check if STATUS is not a syscall-stop, and try again if
+        it isn't.  */
+      rc = check_signal (tracee, wstatus);
+
+      if (rc == -1)
+       return 2;
+      else if (rc)
+       goto again;
+
+      /* Retrieve the signal information and determine whether or not
+        the system call has completed.  */
+
+      if (ptrace (PTRACE_GETSIGINFO, tracee->pid, 0,
+                 &siginfo))
+       return 3;
+
+      if (!syscall_trap_p (&siginfo))
+       {
+         /* Continue.  */
+         if (ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0))
+           return 3;
+
+         goto again;
+       }
+    }
+
+#ifdef __aarch64__
+
+  if (aarch64_get_regs (tracee->pid, &original))
+    return 3;
+
+#else /* !__aarch64__ */
+
+  /* The system call has now completed.  Get the registers again.  */
+
+  if (ptrace (PTRACE_GETREGS, tracee->pid, NULL,
+             &original))
+    return 3;
+
+#endif /* __aarch64__ */
+
+  *regs = original;
+
+  /* Upon failure, wait for the next system call and return
+     success.  */
+
+  if (original.SYSCALL_RET_REG)
+    {
+      /* Restore the original stack pointer.  */
+      regs->STACK_POINTER = sp;
+
+#ifdef __aarch64__
+      aarch64_set_regs (tracee->pid, regs, false);
+#else /* !__aarch64__ */
+      ptrace (PTRACE_SETREGS, tracee->pid, NULL, regs);
+#endif /* __aarch64__ */
+
+      goto exec_failure;
+    }
+
+  /* Write the loader area to the stack, followed by its size and the
+     original stack pointer.  */
+
+  loader = user_alloca (tracee, &original, regs,
+                       size + sizeof loader * 2);
+  if (!loader)
+    return 3;
+
+  size1 = size;
+
+#ifndef STACK_GROWS_DOWNWARDS
+
+  NOT_IMPLEMENTED;
+
+#else /* STACK_GROWS_DOWNWARDS */
+
+  if (user_copy (tracee, (unsigned char *) area,
+                loader + sizeof size1 * 2, size)
+      || user_copy (tracee, (unsigned char *) &size1,
+                   loader + sizeof size1, sizeof size1))
+    return 3;
+
+  size1 = original.STACK_POINTER;
+
+  if (user_copy (tracee, (unsigned char *) &size1,
+                loader, sizeof size1))
+    return 3;
+
+#endif /* STACK_GROWS_DOWNWARDS */
+
+  /* Continue.  */
+  if (ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0))
+    return 3;
+
+  return 0;
+
+ exec_failure:
+  return 3;
+}
+
+/* Handle a `readlink' or `readlinkat' system call.
+
+   CALLNO is the system call number, and REGS are the current user
+   registers of the TRACEE.
+
+   If the file name specified in either a `readlink' or `readlinkat'
+   system call is `/proc/self/exe', write the name of the executable
+   being run into the buffer specified in the system call.  Do not
+   handle relative file names at the moment.
+
+   Return the number of bytes written to the tracee's buffer in
+   *RESULT.
+
+   Value is 0 upon success.  Value is 1 upon failure, and 2 if the
+   system call has been emulated.  */
+
+static int
+handle_readlinkat (USER_WORD callno, USER_REGS_STRUCT *regs,
+                  struct exec_tracee *tracee, USER_WORD *result)
+{
+#ifdef REENTRANT
+  /* readlinkat cannot be handled specially when the library is built
+     to be reentrant, as the file name information cannot be
+     recorded.  */
+  return 0;
+#else /* !REENTRANT */
+
+  char buffer[PATH_MAX + 1];
+  USER_WORD address, return_buffer, size;
+  size_t length;
+
+  /* Read the file name.  */
+
+#ifdef READLINK_SYSCALL
+  if (callno == READLINK_SYSCALL)
+    {
+      address = regs->SYSCALL_ARG_REG;
+      return_buffer = regs->SYSCALL_ARG1_REG;
+      size = regs->SYSCALL_ARG2_REG;
+    }
+  else
+#endif /* READLINK_SYSCALL */
+    {
+      address = regs->SYSCALL_ARG1_REG;
+      return_buffer = regs->SYSCALL_ARG2_REG;
+      size = regs->SYSCALL_ARG3_REG;
+    }
+
+  read_memory (tracee, buffer, PATH_MAX, address);
+
+  /* Make sure BUFFER is NULL terminated.  */
+
+  if (!memchr (buffer, '\0', PATH_MAX))
+    {
+      errno = ENAMETOOLONG;
+      return 1;
+    }
+
+  /* Now check if the caller is looking for /proc/self/exe.
+
+     dirfd can be ignored, as for now only absolute file names are
+     handled.  FIXME.  */
+
+  if (strcmp (buffer, "/proc/self/exe") || !tracee->exec_file)
+    return 0;
+
+  /* Copy over tracee->exec_file.  Truncate it to PATH_MAX, length, or
+     size, whichever is less.  */
+
+  length = strlen (tracee->exec_file);
+  length = MIN (size, MIN (PATH_MAX, length));
+  strncpy (buffer, tracee->exec_file, length);
+
+  if (user_copy (tracee, (unsigned char *) buffer,
+                return_buffer, length))
+    {
+      errno = EIO;
+      return 1;
+    }
+
+  *result = length;
+  return 2;
+#endif /* REENTRANT */
+}
+
+/* Process the system call at which TRACEE is stopped.  If the system
+   call is not known or not exec, send TRACEE on its way.  Otherwise,
+   rewrite it to load the loader and perform an appropriate action.  */
+
+static void
+process_system_call (struct exec_tracee *tracee)
+{
+  USER_REGS_STRUCT regs;
+  int rc, wstatus, save_errno;
+  USER_WORD callno, sp;
+  USER_WORD result;
+  bool reporting_error;
+
+#ifdef __aarch64__
+  rc = aarch64_get_regs (tracee->pid, &regs);
+#else /* !__aarch64__ */
+  rc = ptrace (PTRACE_GETREGS, tracee->pid, NULL,
+              &regs);
+#endif /* __aarch64__ */
+
+  /* TODO: what to do if this fails? */
+  if (rc < 0)
+    return;
+
+  /* Save the stack pointer.  */
+  sp = regs.STACK_POINTER;
+
+  /* Now dispatch based on the system call.  */
+  callno = regs.SYSCALL_NUM_REG;
+  switch (callno)
+    {
+    case EXEC_SYSCALL:
+
+      /* exec system calls should be handled synchronously.  */
+      assert (!tracee->waiting_for_syscall);
+      rc = handle_exec (tracee, &regs);
+
+      switch (rc)
+       {
+       case 3:
+         /* It's too late to do anything about this error,.  */
+         break;
+
+       case 2:
+         /* The process has gone away.  */
+         remove_tracee (tracee);
+         break;
+
+       case 1:
+         /* An error has occured; errno is set to the error.  */
+         goto report_syscall_error;
+       }
+
+      break;
+
+#ifdef READLINK_SYSCALL
+    case READLINK_SYSCALL:
+#endif /* READLINK_SYSCALL */
+    case READLINKAT_SYSCALL:
+
+      /* Handle this readlinkat system call.  */
+      rc = handle_readlinkat (callno, &regs, tracee,
+                             &result);
+
+      /* rc means the same as in `handle_exec'.  */
+
+      if (rc == 1)
+       goto report_syscall_error;
+      else if (rc == 2)
+       goto emulate_syscall;
+
+      /* Fallthrough.  */
+
+    default:
+      /* Don't wait for the system call to finish; instead, the system
+        will DTRT upon the next call to PTRACE_SYSCALL after the
+        syscall-trap signal is delivered.  */
+
+      rc = ptrace (PTRACE_SYSCALL, tracee->pid,
+                  NULL, NULL);
+      if (rc < 0)
+       return;
+
+      tracee->waiting_for_syscall = !tracee->waiting_for_syscall;
+    }
+
+  return;
+
+ report_syscall_error:
+  reporting_error = true;
+  goto common;
+
+ emulate_syscall:
+  reporting_error = false;
+ common:
+
+  /* Reporting an error or emulating a system call works by setting
+     the system call number to -1, letting it continue, and then
+     substituting errno for ENOSYS in the case of an error.
+
+     Make sure that the stack pointer is restored to its original
+     position upon exit, or bad things can happen.  */
+
+  /* First, save errno; system calls below will clobber it.  */
+  save_errno = errno;
+
+  regs.SYSCALL_NUM_REG = -1;
+  regs.STACK_POINTER   = sp;
+
+#ifdef __aarch64__
+  if (aarch64_set_regs (tracee->pid, &regs, true))
+    return;
+#else /* !__aarch64__ */
+
+#ifdef __arm__
+  /* On ARM systems, a special request is used to update the system
+     call number as known to the kernel.  In addition, the system call
+     number must be valid, so use `tuxcall'.  Hopefully, nobody will
+     run this on a kernel with Tux.  */
+
+  if (ptrace (PTRACE_SET_SYSCALL, tracee->pid, NULL, 222))
+    return;
+#endif /* __arm__ */
+
+  if (ptrace (PTRACE_SETREGS, tracee->pid, NULL, &regs))
+    return;
+#endif /* __aarch64__ */
+
+  /* Do this invalid system call.  */
+  if (ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL))
+    return;
+
+ again1:
+  rc = waitpid (tracee->pid, &wstatus, __WALL);
+  if (rc == -1 && errno == EINTR)
+    goto again1;
+
+  /* Return if waitpid fails.  */
+
+  if (rc == -1)
+    return;
+
+  /* If the process received a signal, see if the signal is SIGSYS and
+     from seccomp.  If so, discard it.  */
+
+  if (WIFSTOPPED (wstatus))
+    {
+      rc = check_signal (tracee, wstatus);
+
+      if (rc == -1)
+       return;
+      else if (rc)
+       goto again1;
+    }
+
+  if (!WIFSTOPPED (wstatus))
+    /* The process has been killed in response to a signal.  In this
+       case, simply unlink the tracee and return.  */
+    remove_tracee (tracee);
+  else if (reporting_error)
+    {
+#ifdef __mips__
+      /* MIPS systems place errno in v0 and set a3 to 1.  */
+      regs.gregs[2] = save_errno;
+      regs.gregs[7] = 1;
+#else /* !__mips__ */
+      regs.SYSCALL_RET_REG = -save_errno;
+#endif /* __mips__ */
+
+      /* Report errno.  */
+#ifdef __aarch64__
+      aarch64_set_regs (tracee->pid, &regs, false);
+#else /* !__aarch64__ */
+      ptrace (PTRACE_SETREGS, tracee->pid, NULL, &regs);
+#endif /* __aarch64__ */
+
+      /* Now wait for the next system call to happen.  */
+      ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL);
+    }
+  else
+    {
+      /* No error is being reported.  Return the result in the
+        appropriate registers.  */
+
+#ifdef __mips__
+      /* MIPS systems place errno in v0 and set a3 to 1.  */
+      regs.gregs[2] = result;
+      regs.gregs[7] = 0;
+#else /* !__mips__ */
+      regs.SYSCALL_RET_REG = result;
+#endif /* __mips__ */
+
+      /* Report errno.  */
+#ifdef __aarch64__
+      aarch64_set_regs (tracee->pid, &regs, false);
+#else /* !__aarch64__ */
+      ptrace (PTRACE_SETREGS, tracee->pid, NULL, &regs);
+#endif /* __aarch64__ */
+
+      /* Now wait for the next system call to happen.  */
+      ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL);
+    }
+}
+
+
+
+/* Like `execve', but asks the parent to begin tracing this thread.
+   Fail if tracing is unsuccessful.  */
+
+int
+tracing_execve (const char *file, char *const *argv,
+               char *const *envp)
+{
+  int rc;
+
+  /* Start tracing self.  */
+  rc = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
+  if (rc)
+    return rc;
+
+  /* Notify the parent to enter signal-delivery-stop.  */
+  raise (SIGSTOP);
+  return execve (file, argv, envp);
+}
+
+/* Wait for PID to trace itself, and make a record of that process.
+   Value is 1 or 2 upon failure, 0 otherwise.  Make sure that SIGCHLD
+   is blocked around calls to this function.
+
+   If failure occurs because PID exited, value is 2; upon any other
+   kind of failure, value is 1.  */
+
+int
+after_fork (pid_t pid)
+{
+  int wstatus, rc, flags;
+  struct exec_tracee *tracee;
+
+  /* First, wait for something to happen to PID.  */
+ again:
+  rc = waitpid (pid, &wstatus, __WALL);
+  if (rc != pid && errno == EINTR)
+    goto again;
+
+  if (rc != pid)
+    return 1;
+
+  /* If the child exited (or in general wasn't traced), return 2.  */
+
+  if (!WIFSTOPPED (wstatus))
+    return 2;
+
+  /* Apply required options to the child, so that the kernel
+     automatically traces children and makes it easy to differentiate
+     between system call traps and other kinds of traps.  */
+
+  flags  = PTRACE_O_TRACECLONE;
+  flags |= PTRACE_O_TRACEVFORK;
+  flags |= PTRACE_O_TRACEFORK;
+  flags |= PTRACE_O_TRACESYSGOOD;
+  flags |= PTRACE_O_TRACEEXIT;
+
+  rc = ptrace (PTRACE_SETOPTIONS, pid, 0, flags);
+
+  if (rc)
+    {
+      /* If the kernel can't trace child processes upon creation and
+        exit, then it can't work reliably.  */
+      ptrace (PTRACE_DETACH, pid, 0, 0);
+      return 1;
+    }
+
+  /* Request that the child stop upon the next system call.  */
+  rc = ptrace (PTRACE_SYSCALL, pid, 0, 0);
+  if (rc)
+    return 1;
+
+  /* Enter the child in `tracing_processes'.  */
+
+  if (free_tracees)
+    {
+      tracee = free_tracees;
+      free_tracees = free_tracees->next;
+    }
+  else
+    tracee = malloc (sizeof *tracee);
+
+  if (!tracee)
+    return 1;
+
+  tracee->pid = pid;
+  tracee->next = tracing_processes;
+  tracee->waiting_for_syscall = false;
+  tracee->new_child = false;
+#ifndef REENTRANT
+  tracee->exec_file = NULL;
+#endif /* REENTRANT */
+  tracing_processes = tracee;
+  return 0;
+}
+
+/* Wait for a child process to exit, like `waitpid'.  However, if a
+   child stops to perform a system call, send it on its way and return
+   -1.  OPTIONS must not contain WUNTRACED.  */
+
+pid_t
+exec_waitpid (pid_t pid, int *wstatus, int options)
+{
+  int status;
+  struct exec_tracee *tracee;
+  siginfo_t siginfo;
+
+  pid = waitpid (pid, &status, options | __WALL);
+  if (pid < 0)
+    return pid;
+
+  /* Copy status into *WSTATUS if specified.  */
+  if (wstatus)
+    *wstatus = status;
+
+  /* WIFSTOPPED (status) means that the process has been stopped in
+     response to a system call.  Find its tracee and process the
+     system call.  */
+
+  if (WIFSTOPPED (status))
+    {
+      tracee = find_tracee (pid);
+
+      if (!tracee || tracee->new_child)
+       {
+         if (WSTOPSIG (status) == SIGSTOP)
+           /* A new process has been created and stopped.  Record
+              it now.  */
+           handle_clone (tracee, pid);
+
+         return -1;
+       }
+
+      /* Now extract the stop signal, including ptrace event bits.  */
+      status &= 0xfff00;
+      status = status >> 8;
+
+      switch (status)
+       {
+       case SIGTRAP:
+         /* Now, use PTRACE_GETSIGINFO to determine whether or not the
+            signal was delivered in response to a system call.  */
+
+         if (ptrace (PTRACE_GETSIGINFO, pid, 0, &siginfo))
+           return -1;
+
+         if (!syscall_trap_p (&siginfo))
+           {
+             if (siginfo.si_code < 0)
+               /* SIGTRAP delivered from userspace.  Pass it on.  */
+               ptrace (PTRACE_SYSCALL, pid, 0, SIGTRAP);
+             else
+               ptrace (PTRACE_SYSCALL, pid, 0, 0);
+
+             return -1;
+           }
+
+       case SIGTRAP | 0x80: /* SIGTRAP | 0x80 specifically refers to
+                               system call traps.  */
+         /* Otherwise, process the system call and continue waiting.  */
+         process_system_call (tracee);
+         return -1;
+
+       case SIGTRAP | (PTRACE_EVENT_EXIT << 8):
+         /* The tracee has exited.  Make it finish correctly.  */
+         ptrace (PTRACE_SYSCALL, pid, 0, 0);
+         remove_tracee (tracee);
+         return -1;
+
+       case SIGTRAP | (PTRACE_EVENT_FORK << 8):
+       case SIGTRAP | (PTRACE_EVENT_VFORK << 8):
+       case SIGTRAP | (PTRACE_EVENT_CLONE << 8):
+
+         /* Both PTRACE_EVENT_CLONE and SIGSTOP must arrive before a
+            process is continued.  Otherwise, its parent's cmdline
+            cannot be obtained and propagated.
+
+            If the PID of the new process is currently not being
+            traced, create a new tracee.  Set `new_child' to true,
+            and copy over the old command line in preparation for a
+            SIGSTOP signal being delivered to it.
+
+            Otherwise, start the tracee running until the next
+            syscall.  */
+
+         handle_clone_prepare (tracee);
+
+         /* These events are handled by tracing SIGSTOP signals sent
+            to unknown tracees.  Make sure not to pass through
+            status, as there's no signal really being delivered.  */
+         ptrace (PTRACE_SYSCALL, pid, 0, 0);
+         return -1;
+
+#ifdef SIGSYS
+       case SIGSYS:
+         if (ptrace (PTRACE_GETSIGINFO, pid, 0, &siginfo))
+           return -1;
+
+         /* Continue the process until the next syscall, but don't
+            pass through the signal if an emulated syscall led to
+            it.  */
+#ifdef HAVE_SIGINFO_T_SI_SYSCALL
+#ifndef __arm__
+         ptrace (PTRACE_SYSCALL, pid, 0, ((siginfo.si_code == SYS_SECCOMP
+                                           && siginfo.si_syscall == -1)
+                                          ? 0 : status));
+#else /* __arm__ */
+         ptrace (PTRACE_SYSCALL, pid, 0, ((siginfo.si_code == SYS_SECCOMP
+                                           && siginfo.si_syscall == 222)
+                                          ? 0 : status));
+#endif /* !__arm__ */
+#else /* !HAVE_SIGINFO_T_SI_SYSCALL */
+         /* Drop this signal, since what caused it is unknown.  */
+         ptrace (PTRACE_SYSCALL, pid, 0, 0);
+#endif /* HAVE_SIGINFO_T_SI_SYSCALL */
+         return -1;
+#endif /* SIGSYS */
+
+       default:
+         /* Continue the process until the next syscall.  */
+         ptrace (PTRACE_SYSCALL, pid, 0, status);
+         return -1;
+       }
+    }
+  else
+    {
+      /* The process has exited.  Unlink the associated tracee.  */
+      tracee = find_tracee (pid);
+
+      if (tracee)
+       remove_tracee (tracee);
+
+      return pid;
+    }
+}
+
+
+
+/* Initialize the exec library.  LOADER should be the file name of the
+   loader binary; it is not copied.  */
+
+void
+exec_init (const char *loader)
+{
+  loader_name = loader;
+}
diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in
new file mode 100644
index 00000000000..895e7f88c57
--- /dev/null
+++ b/java/AndroidManifest.xml.in
@@ -0,0 +1,225 @@
+<!-- @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/>. -->
+
+<!-- targetSandboxVersion must be 1.  Otherwise, fascist security
+     restrictions prevent Emacs from making HTTP connections.  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android";
+         package="org.gnu.emacs"
+         android:targetSandboxVersion="1"
+         android:installLocation="auto"
+         android:requestLegacyExternalStorage="true"
+         @ANDROID_SHARED_USER_ID@
+         @ANDROID_SHARED_USER_NAME@
+         android:versionCode="@emacs_major_version@"
+         android:versionName="@version@">
+
+  <!-- Paste in every permission in existence so Emacs can do
+       anything.  -->
+
+  <uses-permission android:name="android.permission.READ_CONTACTS" />
+  <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+  <uses-permission android:name="android.permission.VIBRATE" />
+  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+  <uses-permission android:name="android.permission.INTERNET" />
+  <uses-permission android:name="android.permission.SET_WALLPAPER" />
+  <!-- Despite the claim that WRITE_EXTERNAL_STORAGE also covers
+       reading from external storage, specifying READ_EXTERNAL_STORAGE
+       seems to still be necessary on some versions of Android.
+       (bug#64445) -->
+  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+  <uses-permission android:name="android.permission.SEND_SMS" />
+  <uses-permission android:name="android.permission.RECEIVE_SMS" />
+  <uses-permission android:name="android.permission.RECEIVE_MMS"/>
+  <uses-permission android:name="android.permission.WRITE_SMS"/>
+  <uses-permission android:name="android.permission.READ_SMS"/>
+  <uses-permission android:name="android.permission.NFC" />
+  <uses-permission android:name="android.permission.TRANSMIT_IR" />
+  <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+  <uses-permission android:name="android.permission.WAKE_LOCK"/>
+  <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+  <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
+  <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
+  <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+  <uses-permission android:name="android.permission.RECORD_AUDIO" />
+  <uses-permission android:name="android.permission.CAMERA" />
+
+  <!-- This is required on Android 11 or later to access /sdcard.  -->
+
+  <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
+
+  <uses-sdk android:minSdkVersion="@ANDROID_MIN_SDK@"
+           android:targetSdkVersion="33"/>
+
+  <application android:name="org.gnu.emacs.EmacsApplication"
+              android:label="Emacs"
+              android:icon="@drawable/emacs"
+              android:hardwareAccelerated="true"
+              android:supportsRtl="true"
+              android:theme="@style/EmacsStyle"
+              android:debuggable="@ANDROID_DEBUGGABLE@"
+              android:allowBackup="true"
+              android:extractNativeLibs="true">
+
+    <activity android:name="org.gnu.emacs.EmacsActivity"
+             android:launchMode="singleInstance"
+             android:windowSoftInputMode="adjustResize"
+             android:exported="true"
+             
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
+      <intent-filter>
+        <action android:name="android.intent.action.MAIN" />
+        <category android:name="android.intent.category.DEFAULT" />
+        <category android:name="android.intent.category.LAUNCHER" />
+      </intent-filter>
+    </activity>
+
+    <activity android:name="org.gnu.emacs.EmacsOpenActivity"
+             android:taskAffinity="open.dialog"
+             android:excludeFromRecents="true"
+             android:exported="true">
+
+      <!-- Allow Emacs to open all kinds of files known to Android.  -->
+
+      <intent-filter>
+        <action android:name="android.intent.action.VIEW"/>
+       <action android:name="android.intent.action.EDIT"/>
+       <action android:name="android.intent.action.PICK"/>
+
+        <category android:name="android.intent.category.DEFAULT"/>
+
+       <data android:mimeType="image/aces"/>
+       <data android:mimeType="image/avci"/>
+       <data android:mimeType="image/avcs"/>
+       <data android:mimeType="image/avif"/>
+       <data android:mimeType="image/bmp"/>
+       <data android:mimeType="image/cgm"/>
+       <data android:mimeType="image/dicom-rle"/>
+       <data android:mimeType="image/dpx"/>
+       <data android:mimeType="image/emf"/>
+       <data android:mimeType="image/example"/>
+       <data android:mimeType="image/fits"/>
+       <data android:mimeType="image/g3fax"/>
+       <data android:mimeType="image/heic"/>
+       <data android:mimeType="image/heic-sequence"/>
+       <data android:mimeType="image/heif"/>
+       <data android:mimeType="image/heif-sequence"/>
+       <data android:mimeType="image/hej2k"/>
+       <data android:mimeType="image/hsj2"/>
+       <data android:mimeType="image/jls"/>
+       <data android:mimeType="image/jp2"/>
+       <data android:mimeType="image/jph"/>
+       <data android:mimeType="image/jphc"/>
+       <data android:mimeType="image/jpm"/>
+       <data android:mimeType="image/jpx"/>
+       <data android:mimeType="image/jxr"/>
+       <data android:mimeType="image/jxrA"/>
+       <data android:mimeType="image/jxrS"/>
+       <data android:mimeType="image/jxs"/>
+       <data android:mimeType="image/jxsc"/>
+       <data android:mimeType="image/jxsi"/>
+       <data android:mimeType="image/jxss"/>
+       <data android:mimeType="image/ktx"/>
+       <data android:mimeType="image/ktx2"/>
+       <data android:mimeType="image/naplps"/>
+       <data android:mimeType="image/png"/>
+       <data android:mimeType="image/prs.btif"/>
+       <data android:mimeType="image/prs.pti"/>
+       <data android:mimeType="image/pwg-raster"/>
+       <data android:mimeType="image/svg+xml"/>
+       <data android:mimeType="image/t38"/>
+       <data android:mimeType="image/tiff"/>
+       <data android:mimeType="image/tiff-fx"/>
+       <data android:mimeType="image/xpm"/>
+       <data android:mimeType="text/*"/>
+        <data android:mimeType="application/*xml"/>
+        <data android:mimeType="application/atom+xml"/>
+        <data android:mimeType="application/dxf"/>
+        <data android:mimeType="application/ecmascript"/>
+        <data android:mimeType="application/javascript"/>
+        <data android:mimeType="application/json"/>
+        <data android:mimeType="application/*log*"/>
+        <data android:mimeType="application/octet-stream"/>
+        <data android:mimeType="application/soap+xm"/>
+        <data android:mimeType="application/x-caramel"/>
+        <data android:mimeType="application/x-klaunch"/>
+        <data android:mimeType="application/x-latex"/>
+        <data android:mimeType="application/x-sh"/>
+        <data android:mimeType="application/x-tcl"/>
+        <data android:mimeType="application/x-tex*"/>
+        <data android:mimeType="application/x-troff*"/>
+        <data android:mimeType="application/xhtml+xml"/>
+        <data android:mimeType="application/xml*"/>
+        <data android:mimeType="application/zip"/>
+        <data android:mimeType="application/x-zip-compressed"/>
+      </intent-filter>
+    </activity>
+
+    <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" />
+        <category android:name="android.intent.category.DEFAULT" />
+      </intent-filter>
+    </activity>
+
+    <!-- Android 6 and earlier don't display ``application
+         preferences'' activities in Settings, so display the
+         preferences activity as a launcher icon instead.  -->
+
+    <activity android:autoRemoveFromRecents="true"
+              android:label="Emacs options"
+             android:enabled="@bool/isBeforeNougat"
+             android:exported="@bool/isBeforeNougat"
+             android:icon="@drawable/emacs_wrench"
+              android:name=".EmacsLauncherPreferencesActivity">
+      <intent-filter>
+        <action android:name="android.intent.action.MAIN" />
+        <category android:name="android.intent.category.DEFAULT" />
+        <category android:name="android.intent.category.LAUNCHER" />
+      </intent-filter>
+    </activity>
+
+    <provider android:name="org.gnu.emacs.EmacsDocumentsProvider"
+             android:authorities="org.gnu.emacs"
+             android:exported="true"
+             android:grantUriPermissions="true"
+             android:permission="android.permission.MANAGE_DOCUMENTS"
+             android:enabled="@bool/isAtLeastKitKat">
+      <intent-filter>
+       <action
+           android:name="android.content.action.DOCUMENTS_PROVIDER"/>
+      </intent-filter>
+    </provider>
+
+    <service android:name="org.gnu.emacs.EmacsService"
+            android:directBootAware="false"
+            android:enabled="true"
+            android:exported="false"
+            android:label="GNU Emacs service"/>
+  </application>
+</manifest>
diff --git a/java/INSTALL b/java/INSTALL
new file mode 100644
index 00000000000..0646db426e0
--- /dev/null
+++ b/java/INSTALL
@@ -0,0 +1,977 @@
+Installation instructions for Android
+Copyright (C) 2023 Free Software Foundation, Inc.
+See the end of the file for license conditions.
+
+Please read the entirety of this file before attempting to build Emacs
+as an application package which can run on Android devices.
+
+When building from the source repository, make sure to read
+INSTALL.REPO as well.
+
+
+
+Android is an unusual operating system in that program binaries cannot
+be produced on computers running Android themselves.  Instead, they
+must be built on some other computer using a set of tools known as the
+``Android SDK'' (Software Development Kit) and the ``Android NDK''
+(Native Development Kit.)  Appropriate versions of both must be
+obtained to build GNU Emacs; after being built, the generated binaries
+will work on almost all Android devices.  This document does not
+elaborate on how both sets of tools can be obtained.  However, for
+your freedom's sake, you should use the Android SDK provided by the
+Debian project.
+
+In addition to the Android SDK and Android NDK, Emacs also requires
+the Java compiler from OpenJDK 1.7.0 to be installed on your system,
+along with a working `m4' macro processor.  Building on GNU systems is
+all that is officially supported.  We are told that Mac OS works too,
+and other Unix systems will likely work as well, but MS Windows and
+Cygwin will not.
+
+Once all of those tools are obtained, you may invoke the `configure'
+script like so:
+
+  ./configure --with-android=/path/to/android.jar \
+             ANDROID_CC=/path/to/android/ndk/cc  \
+             SDK_BUILD_TOOLS=/path/to/sdk/build/tools
+
+Replacing the paths in the command line above with:
+
+  - the path to the `android.jar' headers which come with the Android
+    SDK.  They must correspond to Android version 13 (API level 33) or
+    later.
+
+  - the path to the C compiler in the Android NDK, for the kind of CPU
+    you are building Emacs to run on.
+
+  - the path to the directory in the Android SDK containing binaries
+    such as `aapt', `apksigner', and `d8'.  These are used to build
+    the application package.
+
+Where the type of CPU can either be `armeabi', `armv7*', `i686',
+`x86_64', `mips', or `mips64'.
+
+After the configuration process completes, you may run:
+
+  make all
+
+Once `make' finishes, there should be a file in the `java' directory
+named along the lines of:
+
+  emacs-<version>-<api-version>-<abi>.apk
+
+where <api-version> is the oldest version of Android that the package
+will run on, and <abi> is the type of Android machine the package was
+built for.
+
+The generated package can be uploaded onto an SD card (or similar
+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
+each version of Android in those versions of the NDK.
+
+Before running `configure', you must identify three variables:
+
+  - What kind of Android system you are building Emacs for.
+
+  - The minimum API version of Android you want to build Emacs for.
+
+  - The locations of the system root and include files for that
+    version of Android in the NDK.
+
+That information must then be specified as arguments to the NDK C
+compiler.  For example:
+
+  ./configure [...] \
+     ANDROID_CC="i686-linux-android-gcc \
+                --sysroot=/path/to/ndk/platforms/android-14/arch-x86/"
+     ANDROID_CFLAGS="-isystem /path/to/ndk/sysroot/usr/include \
+                    -isystem 
/path/to/ndk/sysroot/usr/include/i686-linux-android \
+                    -D__ANDROID_API__=14"
+
+Where __ANDROID_API__ and the version identifier in
+"platforms/android-14" defines the version of Android you are building
+for, and the include directories specify the paths to the relevant
+Android headers.  In addition, it may be necessary to specify
+"-gdwarf-2", due to a bug in the Android NDK.
+
+Even older versions of the Android SDK do not require the extra
+`-isystem' directives.
+
+Emacs is known to run on Android 2.2 (API version 8) or later, with
+the NDK r10b or later.  We wanted to make Emacs work on even older
+versions of Android, but they are missing the required JNI graphics
+library that allows Emacs to display text from C code.
+
+Due to an extremely nasty bug in the Android 2.2 system, the generated
+Emacs package cannot be compressed in builds for Android 2.2.  As a
+result, the Emacs package will be approximately 100 megabytes larger
+than a compressed package for a newer version of Android.
+
+
+BUILDING C++ DEPENDENCIES
+
+With a new version of the NDK, dependencies containing C++ code should
+build without any futher configuration.  However, older versions
+require that you use the ``make_standalone_toolchain.py'' script in
+the NDK distribution to create a ``standalone toolchain'', and use
+that instead, in order for C++ headers to be found.
+
+See https://developer.android.com/ndk/guides/standalone_toolchain for
+more details; when a ``standalone toolchain'' is specified, the
+configure script will try to determine the location of the C++
+compiler based on the C compiler specified.  If that automatic
+detection does not work, you can specify a C++ compiler yourself, like
+so:
+
+  ./configure --with-ndk-cxx=/path/to/toolchain/bin/i686-linux-android-g++
+
+Some versions of the NDK have a bug, where GCC fails to locate
+``stddef.h'' after being copied to a standalone toolchain.  To work
+around this problem (which normally exhibits itself when building C++
+code), add:
+
+   -isystem /path/to/toolchain/include/c++/4.9.x
+
+to ANDROID_CFLAGS.
+
+
+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 A SHARED USER ID
+
+Sometimes it may be desirable to build Emacs so that it is able to
+access executables and application data from another program.  To
+achieve this, that other program must have a ``shared user ID'', and
+be signed with the same signing key used to sign Emacs (normally
+`emacs.keystore'.)
+
+Once you have both that signing key and its ``shared user ID'', you
+can give it to configure:
+
+    ./configure --with-shared-user-id=MY.SHARED.USER.ID
+
+For instance,
+
+    ./configure --with-shared-user-id=com.termux
+
+will result in Termux (https://termux.dev)'s application data being
+accessible to Emacs, within its own application data directory located
+at `/data/data/com.termux/files'.
+
+Don't do this if you already have Emacs installed with a different
+shared user ID, as the system does not allow programs to change their
+user IDs after being installed.
+
+
+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"
+
+If `configure' complains about not being able to find
+``libc++_shared.so'', then you must locate that file in your copy of
+the NDK, and specify it like so:
+
+  ./configure --with-ndk-cxx-shared=/path/to/sysroot/libc++_shared.so
+
+Emacs will then read the ``Android.mk'' file in each directory, and
+automatically build and use those modules.
+
+When building for Intel systems, some ``ndk-build'' modules require
+the Netwide Assembler, usually installed under ``nasm'', to be present
+on the system that is building Emacs.
+
+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, along with what has to be patched to make them work:
+
+  libpng       - https://android.googlesource.com/platform/external/libpng
+  libwebp      - https://android.googlesource.com/platform/external/webp
+     (You must apply the patch at the end of this file for the resulting
+      binary to work on armv7 devices.)
+  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))
+  libjpeg-turbo - 
https://android.googlesource.com/platform/external/libjpeg-turbo
+     (You must add LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) before
+      its Android.mk includes $(BUILD_SHARED_LIBRARY))
+  libxml2      - https://android.googlesource.com/platform/external/libxml2/
+     (You must also place the dependency icu4c in ``--with-ndk-path'',
+      and apply the patch at the end of this file.)
+  icu4c                - 
https://android.googlesource.com/platform/external/icu/
+     (You must apply the patch at the end of this file.)
+  sqlite3      - https://android.googlesource.com/platform/external/sqlite/
+     (You must apply the patch at the end of this file, and add the `dist'
+      directory to ``--with-ndk-path''.)
+  libselinux   - https://android.googlesource.com/platform/external/libselinux
+     (You must apply the patches at the end of the file, and obtain
+      the following three dependencies.)
+  libpackagelistparser
+    
https://android.googlesource.com/platform/system/core/+/refs/heads/nougat-mr1-dev/libpackagelistparser/
+  libpcre      - https://android.googlesource.com/platform/external/pcre
+  libcrypto    - https://android.googlesource.com/platform/external/boringssl
+     (You must apply the patch at the end of this file when building for
+      ARM systems.)
+
+Many of these dependencies have been migrated over to the
+``Android.bp'' build system now used to build Android itself.
+However, the old ``Android.mk'' Makefiles are still present in older
+branches, and can be easily adapte to newer versions.
+
+In addition, some Emacs dependencies provide `ndk-build' support
+themselves:
+
+  libjansson   - https://github.com/akheron/jansson
+    (You must add LOCAL_EXPORT_INCLUDES := $(LOCAL_C_INCLUDES) before
+     its Android.mk includes $(BUILD_SHARED_LIBRARY), then copy
+     android/jansson_config.h to android/jansson_private_config.h.)
+
+Emacs developers have ported the following dependencies to ARM Android
+systems:
+
+  gnutls, gmp  - https://sourceforge.net/projects/android-ports-for-gnu-emacs
+    (Please see the section GNUTLS near the end of this file.)
+  libtiff      - https://sourceforge.net/projects/android-ports-for-gnu-emacs
+    (Extract and point ``--with-ndk-path'' to tiff-4.5.0-emacs.tar.gz.)
+  tree-sitter  - https://sourceforge.net/projects/android-ports-for-gnu-emacs
+    (Please see the section TREE-SITTER near the end of this file.)
+  harfbuzz     - https://sourceforge.net/projects/android-ports-for-gnu-emacs
+    (Please see the section HARFBUZZ near the end of this file.)
+
+And other developers have ported the following dependencies to Android
+systems:
+
+  ImageMagick, lcms2 - https://github.com/MolotovCherry/Android-ImageMagick7
+    (Please see the section IMAGEMAGICK near the end of this file.)
+
+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.
+
+
+GNUTLS
+
+Modified copies of GnuTLS and its dependencies (such as libgmp,
+libtasn1, p11-kit) which can be built with the ndk-build system can be
+found at https://sourceforge.net/projects/android-ports-for-gnu-emacs.
+
+They have only been tested on arm64 Android systems running Android
+5.0 or later, and armv7l systems running Android 13 or later, so your
+mileage may vary, especially if you are trying to build Emacs for
+another kind of machine.
+
+To build Emacs with GnuTLS, you must unpack each of the following tar
+archives in that site:
+
+  gmp-6.2.1-emacs.tgz
+  gnutls-3.7.8-emacs.tar.gz
+  libtasn1-4.19.0-emacs.tar.gz
+  p11-kit-0.24.1-emacs.tar.gz
+  nettle-3.8-emacs.tar.gz
+
+and add the resulting folders to ``--with-ndk-path''.  Note that you
+should not try to build these packages separately using any
+`configure' script or Makefiles inside.
+
+
+TREE-SITTER
+
+A copy of tree-sitter modified to build with the ndk-build system can
+also be found that URL.  To build Emacs with tree-sitter, you must
+unpack the following tar archive in that site:
+
+  tree-sitter-0.20.7-emacs.tar.gz
+
+and add the resulting folder to ``--with-ndk-build''.
+
+
+HARFBUZZ
+
+A copy of HarfBuzz modified to build with the ndk-build system can
+also be found at that URL.  To build Emacs with HarfBuzz, you must
+unpack the following tar archive in that site:
+
+  harfbuzz-7.1.0-emacs.tar.gz
+
+and add the resulting folder to ``--with-ndk-build''.
+
+
+IMAGEMAGICK
+
+There is a third party port of ImageMagick to Android.  Unfortunately,
+the port also uses its own patched versions of libpng, libjpeg,
+libtiff and libwebp, which conflict with those used by Emacs.  Its
+Makefiles were also written for MS Windows, so you must also apply the
+patch at the end of this file.
+
+
+
+PATCH FOR LIBXML2
+
+This patch must be applied to the Android.mk in Google's version of
+libxml2 before it can be built for Emacs.  In addition, you must also
+revert the commit `edb5870767fed8712a9b77ef34097209b61ab2db'.
+
+diff --git a/Android.mk b/Android.mk
+index 07c7b372..24f67e49 100644
+--- a/Android.mk
++++ b/Android.mk
+@@ -80,6 +80,7 @@ LOCAL_SHARED_LIBRARIES := libicuuc
+ LOCAL_MODULE:= libxml2
+ LOCAL_CLANG := true
+ LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
++LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)
+ include $(BUILD_SHARED_LIBRARY)
+ 
+ # For the host
+@@ -94,3 +95,5 @@ LOCAL_MODULE := libxml2
+ LOCAL_CLANG := true
+ LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+ include $(BUILD_HOST_STATIC_LIBRARY)
++
++$(call import-module,libicuuc)
+
+PATCH FOR ICU
+
+This patch must be applied to icu4j/Android.mk in Google's version of
+icu before it can be built for Emacs.
+
+diff --git a/icu4j/Android.mk b/icu4j/Android.mk
+index d1ab3d5..69eff81 100644
+--- a/icu4j/Android.mk
++++ b/icu4j/Android.mk
+@@ -69,7 +69,7 @@ include $(BUILD_STATIC_JAVA_LIBRARY)
+ # Path to the ICU4C data files in the Android device file system:
+ icu4c_data := /system/usr/icu
+ icu4j_config_root := $(LOCAL_PATH)/main/classes/core/src
+-include external/icu/icu4j/adjust_icudt_path.mk
++include $(LOCAL_PATH)/adjust_icudt_path.mk
+ 
+ include $(CLEAR_VARS)
+ LOCAL_SRC_FILES := $(icu4j_src_files)
+
+diff --git a/icu4c/source/common/Android.mk b/icu4c/source/common/Android.mk
+index 8e5f757..44bb130 100644
+--- a/icu4c/source/common/Android.mk
++++ b/icu4c/source/common/Android.mk
+@@ -231,7 +231,7 @@ include $(CLEAR_VARS)
+ LOCAL_SRC_FILES += $(src_files)
+ LOCAL_C_INCLUDES += $(c_includes) $(optional_android_logging_includes)
+ LOCAL_CFLAGS += $(local_cflags) -DPIC -fPIC
+-LOCAL_SHARED_LIBRARIES += libdl $(optional_android_logging_libraries)
++LOCAL_SHARED_LIBRARIES += libdl libstdc++ 
$(optional_android_logging_libraries)
+ LOCAL_MODULE_TAGS := optional
+ LOCAL_MODULE := libicuuc
+ LOCAL_RTTI_FLAG := -frtti
+
+PATCH FOR SQLITE3
+
+diff --git a/dist/Android.mk b/dist/Android.mk
+index bf277d2..36734d9 100644
+--- a/dist/Android.mk
++++ b/dist/Android.mk
+@@ -141,6 +141,7 @@ include $(BUILD_HOST_EXECUTABLE)
+ include $(CLEAR_VARS)
+ LOCAL_SRC_FILES := $(common_src_files)
+ LOCAL_CFLAGS += $(minimal_sqlite_flags)
++LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)
+ LOCAL_MODULE:= libsqlite_static_minimal
+ LOCAL_SDK_VERSION := 23
+ include $(BUILD_STATIC_LIBRARY)
+
+diff --git a/dist/sqlite3.c b/dist/sqlite3.c
+index b0536a4..8fa1ee9 100644
+--- a/dist/sqlite3.c
++++ b/dist/sqlite3.c
+@@ -26474,7 +26474,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
+ */
+ #if !defined(HAVE_POSIX_FALLOCATE) \
+       && (_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L)
+-# define HAVE_POSIX_FALLOCATE 1
++/* # define HAVE_POSIX_FALLOCATE 1 */
+ #endif
+ 
+ /*
+
+PATCH FOR WEBP
+
+diff --git a/Android.mk b/Android.mk
+index c7bcb0f5..d4da1704 100644
+--- a/Android.mk
++++ b/Android.mk
+@@ -28,9 +28,10 @@ ifneq ($(findstring armeabi-v7a, $(TARGET_ARCH_ABI)),)
+   # Setting LOCAL_ARM_NEON will enable -mfpu=neon which may cause illegal
+   # instructions to be generated for armv7a code. Instead target the neon code
+   # specifically.
+-  NEON := c.neon
+-  USE_CPUFEATURES := yes
+-  WEBP_CFLAGS += -DHAVE_CPU_FEATURES_H
++  # NEON := c.neon
++  # USE_CPUFEATURES := yes
++  # WEBP_CFLAGS += -DHAVE_CPU_FEATURES_H
++  NEON := c
+ else
+   NEON := c
+ endif
+
+PATCHES FOR SELINUX
+
+diff --git a/Android.mk b/Android.mk
+index 659232e..1e64fd6 100644
+--- a/Android.mk
++++ b/Android.mk
+@@ -116,3 +116,7 @@ LOCAL_STATIC_LIBRARIES := libselinux
+ LOCAL_WHOLE_STATIC_LIBRARIES := libpcre
+ LOCAL_C_INCLUDES := external/pcre
+ include $(BUILD_HOST_EXECUTABLE)
++
++$(call import-module,libpcre)
++$(call import-module,libpackagelistparser)
++$(call import-module,libcrypto)
+
+diff --git a/src/android.c b/src/android.c
+index 5206a9f..b351ffc 100644
+--- a/src/android.c
++++ b/src/android.c
+@@ -21,8 +21,7 @@
+ #include <selinux/label.h>
+ #include <selinux/avc.h>
+ #include <openssl/sha.h>
+-#include <private/android_filesystem_config.h>
+-#include <log/log.h>
++#include <android/log.h>
+ #include "policy.h"
+ #include "callbacks.h"
+ #include "selinux_internal.h"
+@@ -686,6 +685,7 @@ static int seapp_context_lookup(enum seapp_kind kind,
+               seinfo = parsedseinfo;
+       }
+ 
++#if 0
+       userid = uid / AID_USER;
+       isOwner = (userid == 0);
+       appid = uid % AID_USER;
+@@ -702,9 +702,13 @@ static int seapp_context_lookup(enum seapp_kind kind,
+               username = "_app";
+               appid -= AID_APP;
+       } else {
++#endif
+               username = "_isolated";
++              appid = 0;
++#if 0
+               appid -= AID_ISOLATED_START;
+       }
++#endif
+ 
+       if (appid >= CAT_MAPPING_MAX_ID || userid >= CAT_MAPPING_MAX_ID)
+               goto err;
+@@ -1662,8 +1666,10 @@ int selinux_log_callback(int type, const char *fmt, ...)
+ 
+     va_start(ap, fmt);
+     if (vasprintf(&strp, fmt, ap) != -1) {
++#if 0
+         LOG_PRI(priority, "SELinux", "%s", strp);
+         LOG_EVENT_STRING(AUDITD_LOG_TAG, strp);
++#endif
+         free(strp);
+     }
+     va_end(ap);
+
+PATCH FOR BORINGSSL
+
+diff --git a/Android.mk b/Android.mk
+index 3e3ef2a..277d4a9 100644
+--- a/Android.mk
++++ b/Android.mk
+@@ -27,7 +27,9 @@ LOCAL_MODULE := libcrypto
+ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/src/include
+ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk 
$(LOCAL_PATH)/crypto-sources.mk
+ LOCAL_CFLAGS += -fvisibility=hidden -DBORINGSSL_SHARED_LIBRARY 
-DBORINGSSL_IMPLEMENTATION -DOPENSSL_SMALL -Wno-unused-parameter
++LOCAL_CFLAGS_arm = -DOPENSSL_STATIC_ARMCAP -DOPENSSL_NO_ASM
+ LOCAL_SDK_VERSION := 9
++LOCAL_LDFLAGS = --no-undefined
+ # sha256-armv4.S does not compile with clang.
+ LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
+ LOCAL_CLANG_ASFLAGS_arm64 += -march=armv8-a+crypto
+diff --git a/sources.mk b/sources.mk
+index e82f3d5..be3a3c4 100644
+--- a/sources.mk
++++ b/sources.mk
+@@ -337,20 +337,20 @@ linux_aarch64_sources := \
+   linux-aarch64/crypto/sha/sha256-armv8.S\
+   linux-aarch64/crypto/sha/sha512-armv8.S\
+ 
+-linux_arm_sources := \
+-  linux-arm/crypto/aes/aes-armv4.S\
+-  linux-arm/crypto/aes/aesv8-armx32.S\
+-  linux-arm/crypto/aes/bsaes-armv7.S\
+-  linux-arm/crypto/bn/armv4-mont.S\
+-  linux-arm/crypto/modes/ghash-armv4.S\
+-  linux-arm/crypto/modes/ghashv8-armx32.S\
+-  linux-arm/crypto/sha/sha1-armv4-large.S\
+-  linux-arm/crypto/sha/sha256-armv4.S\
+-  linux-arm/crypto/sha/sha512-armv4.S\
+-  src/crypto/chacha/chacha_vec_arm.S\
+-  src/crypto/cpu-arm-asm.S\
+-  src/crypto/curve25519/asm/x25519-asm-arm.S\
+-  src/crypto/poly1305/poly1305_arm_asm.S\
++# linux_arm_sources := \
++#   linux-arm/crypto/aes/aes-armv4.S\
++#   linux-arm/crypto/aes/aesv8-armx32.S\
++#   linux-arm/crypto/aes/bsaes-armv7.S\
++#   linux-arm/crypto/bn/armv4-mont.S\
++#   linux-arm/crypto/modes/ghash-armv4.S\
++#   linux-arm/crypto/modes/ghashv8-armx32.S\
++#   linux-arm/crypto/sha/sha1-armv4-large.S\
++#   linux-arm/crypto/sha/sha256-armv4.S\
++#   linux-arm/crypto/sha/sha512-armv4.S\
++#   src/crypto/chacha/chacha_vec_arm.S\
++#   src/crypto/cpu-arm-asm.S\
++#   src/crypto/curve25519/asm/x25519-asm-arm.S\
++#   src/crypto/poly1305/poly1305_arm_asm.S\
+ 
+ linux_x86_sources := \
+   linux-x86/crypto/aes/aes-586.S\
+
+PATCH FOR IMAGEMAGICK
+
+diff --git a/Android.mk b/Android.mk
+index 5ab6699..4441417 100644
+--- a/Android.mk
++++ b/Android.mk
+@@ -52,6 +52,20 @@ LZMA_LIB_PATH                   := $(LOCAL_PATH)/xz-5.2.4
+ BZLIB_LIB_PATH                  := $(LOCAL_PATH)/bzip-1.0.8
+ LCMS_LIB_PATH                   := $(LOCAL_PATH)/liblcms2-2.9
+ 
++LIBBZ2_ENABLED        := true
++LIBFFTW_ENABLED       := true
++LIBFREETYPE2_ENABLED  := true
++LIBJPEG_TURBO_ENABLED := true
++LIBLZMA_ENABLED       := true
++LIBOPENJPEG_ENABLED   := true
++LIBPNG_ENABLED        := true
++LIBTIFF_ENABLED       := true
++LIBWEBP_ENABLED       := true
++LIBXML2_ENABLED       := true
++LIBZLIB_ENABLED       := true
++LIBLCMS2_ENABLED      := true
++BUILD_MAGICKWAND      := true
++
+ #-------------------------------------------------------------
+ # Include all modules
+ #-------------------------------------------------------------
+@@ -68,6 +82,9 @@ include $(MAKE_PATH)/libjpeg-turbo.mk
+ # libopenjpeg
+ include $(MAKE_PATH)/libopenjpeg.mk
+ 
++# libwebp
++include $(MAKE_PATH)/libwebp.mk
++
+ # libtiff
+ include $(MAKE_PATH)/libtiff.mk
+ 
+@@ -77,9 +94,6 @@ include $(MAKE_PATH)/libpng.mk
+ # libfreetype2
+ include $(MAKE_PATH)/libfreetype2.mk
+ 
+-# libwebp
+-include $(MAKE_PATH)/libwebp.mk
+-
+ # libfftw
+ include $(MAKE_PATH)/libfftw.mk
+ 
+diff --git a/libjpeg-turbo-2.0.2/jconfig.h b/libjpeg-turbo-2.0.2/jconfig.h
+index 47d14c9..5c6f8ee 100644
+--- a/libjpeg-turbo-2.0.2/jconfig.h
++++ b/libjpeg-turbo-2.0.2/jconfig.h
+@@ -1,57 +1,43 @@
+-/* autogenerated jconfig.h based on Android.mk var JCONFIG_FLAGS */ 
++/* autogenerated jconfig.h based on Android.mk var JCONFIG_FLAGS */
+ #ifndef JPEG_LIB_VERSION
+ #define JPEG_LIB_VERSION 62
+ #endif
+-
+ #ifndef LIBJPEG_TURBO_VERSION
+ #define LIBJPEG_TURBO_VERSION 2.0.2
+ #endif
+-
+ #ifndef LIBJPEG_TURBO_VERSION_NUMBER
+ #define LIBJPEG_TURBO_VERSION_NUMBER 202
+ #endif
+-
+ #ifndef C_ARITH_CODING_SUPPORTED
+ #define C_ARITH_CODING_SUPPORTED
+ #endif
+-
+ #ifndef D_ARITH_CODING_SUPPORTED
+ #define D_ARITH_CODING_SUPPORTED
+ #endif
+-
+ #ifndef MEM_SRCDST_SUPPORTED
+ #define MEM_SRCDST_SUPPORTED
+ #endif
+-
+ #ifndef WITH_SIMD
+ #define WITH_SIMD
+ #endif
+-
+ #ifndef BITS_IN_JSAMPLE
+ #define BITS_IN_JSAMPLE 8
+ #endif
+-
+ #ifndef HAVE_LOCALE_H
+ #define HAVE_LOCALE_H
+ #endif
+-
+ #ifndef HAVE_STDDEF_H
+ #define HAVE_STDDEF_H
+ #endif
+-
+ #ifndef HAVE_STDLIB_H
+ #define HAVE_STDLIB_H
+ #endif
+-
+ #ifndef NEED_SYS_TYPES_H
+ #define NEED_SYS_TYPES_H
+ #endif
+-
+ #ifndef HAVE_UNSIGNED_CHAR
+ #define HAVE_UNSIGNED_CHAR
+ #endif
+-
+ #ifndef HAVE_UNSIGNED_SHORT
+ #define HAVE_UNSIGNED_SHORT
+ #endif
+-
+diff --git a/libxml2-2.9.9/encoding.c b/libxml2-2.9.9/encoding.c
+index a3aaf10..60f165b 100644
+--- a/libxml2-2.9.9/encoding.c
++++ b/libxml2-2.9.9/encoding.c
+@@ -2394,7 +2394,6 @@ xmlCharEncOutput(xmlOutputBufferPtr output, int init)
+ {
+     int ret;
+     size_t written;
+-    size_t writtentot = 0;
+     size_t toconv;
+     int c_in;
+     int c_out;
+@@ -2451,7 +2450,6 @@ retry:
+                             xmlBufContent(in), &c_in);
+     xmlBufShrink(in, c_in);
+     xmlBufAddLen(out, c_out);
+-    writtentot += c_out;
+     if (ret == -1) {
+         if (c_out > 0) {
+             /* Can be a limitation of iconv or uconv */
+@@ -2536,7 +2534,6 @@ retry:
+           }
+ 
+             xmlBufAddLen(out, c_out);
+-            writtentot += c_out;
+             goto retry;
+       }
+     }
+@@ -2567,9 +2564,7 @@ xmlCharEncOutFunc(xmlCharEncodingHandler *handler, 
xmlBufferPtr out,
+                   xmlBufferPtr in) {
+     int ret;
+     int written;
+-    int writtentot = 0;
+     int toconv;
+-    int output = 0;
+ 
+     if (handler == NULL) return(-1);
+     if (out == NULL) return(-1);
+@@ -2612,7 +2607,6 @@ retry:
+                             in->content, &toconv);
+     xmlBufferShrink(in, toconv);
+     out->use += written;
+-    writtentot += written;
+     out->content[out->use] = 0;
+     if (ret == -1) {
+         if (written > 0) {
+@@ -2622,8 +2616,6 @@ retry:
+         ret = -3;
+     }
+ 
+-    if (ret >= 0) output += ret;
+-
+     /*
+      * Attempt to handle error cases
+      */
+@@ -2700,7 +2692,6 @@ retry:
+           }
+ 
+             out->use += written;
+-            writtentot += written;
+             out->content[out->use] = 0;
+             goto retry;
+       }
+diff --git a/libxml2-2.9.9/xpath.c b/libxml2-2.9.9/xpath.c
+index 5e3bb9f..505ec82 100644
+--- a/libxml2-2.9.9/xpath.c
++++ b/libxml2-2.9.9/xpath.c
+@@ -10547,7 +10547,7 @@ xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
+ 
+ static xmlChar *
+ xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
+-    int len = 0, l;
++    int l;
+     int c;
+     const xmlChar *cur;
+     xmlChar *ret;
+@@ -10567,7 +10567,6 @@ xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
+           (c == '_') || (c == ':') ||
+           (IS_COMBINING(c)) ||
+           (IS_EXTENDER(c)))) {
+-      len += l;
+       NEXTL(l);
+       c = CUR_CHAR(l);
+     }
+diff --git a/make/libicu4c.mk b/make/libicu4c.mk
+index 21ec121..8b77865 100644
+--- a/make/libicu4c.mk
++++ b/make/libicu4c.mk
+@@ -250,7 +250,7 @@ LOCAL_MODULE    := libicuuc
+ LOCAL_SRC_FILES := $(src_files)
+ 
+ # when built in android, they require uconfig_local (because of android 
project), but we don't need this
+-$(shell > $(ICU_COMMON_PATH)/unicode/uconfig_local.h echo /* Autogenerated 
stub file to make libicuuc build happy */) \
++$(shell > $(ICU_COMMON_PATH)/unicode/uconfig_local.h echo /\* Autogenerated 
stub file to make libicuuc build happy \*/) \
+ 
+ ifeq ($(LIBXML2_ENABLED),true)
+     include $(BUILD_STATIC_LIBRARY)
+diff --git a/make/libjpeg-turbo.mk b/make/libjpeg-turbo.mk
+index d39dd41..fdebcf3 100644
+--- a/make/libjpeg-turbo.mk
++++ b/make/libjpeg-turbo.mk
+@@ -230,30 +230,30 @@ JCONFIG_FLAGS += \
+     HAVE_UNSIGNED_SHORT
+ 
+ JCONFIGINT_FLAGS += \
+-    BUILD="20190814" \
+-    PACKAGE_NAME="libjpeg-turbo" \
+-    VERSION="2.0.2"
++    BUILD=\"20190814\" \
++    PACKAGE_NAME=\"libjpeg-turbo\" \
++    VERSION=\"2.0.2\"
+ 
+ # originally defined in jconfigint.h, but the substitution has problems with 
spaces
+ LOCAL_CFLAGS := \
+     -DINLINE="inline __attribute__((always_inline))"
+ 
+ # create definition file jconfig.h, needed in order to build
+-$(shell echo /* autogenerated jconfig.h based on Android.mk var JCONFIG_FLAGS 
*/ > $(JPEG_LIB_PATH)/jconfig.h)
++$(shell echo \/\* autogenerated jconfig.h based on Android.mk var 
JCONFIG_FLAGS \*\/ > $(JPEG_LIB_PATH)/jconfig.h)
+ $(foreach name,$(JCONFIG_FLAGS), \
+     $(if $(findstring =,$(name)), \
+-        $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo #ifndef $(firstword $(subst 
=, ,$(name)))) \
++        $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo \#ifndef $(firstword 
$(subst =, ,$(name)))) \
+     , \
+-        $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo #ifndef $(name)) \
++        $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo \#ifndef $(name)) \
+     ) \
+-    $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo #define $(subst =, ,$(name))) \
+-    $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo #endif) \
++    $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo \#define $(subst =, ,$(name))) \
++    $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo \#endif) \
+     $(shell >> $(JPEG_LIB_PATH)/jconfig.h echo.) \
+ )
+ 
+ # create definition file jconfigint.h, needed in order to build
+-$(shell >$(JPEG_LIB_PATH)/jconfigint.h echo /* autogenerated jconfigint.h 
based on Android.mk vars JCONFIGINT_FLAGS */)
+-$(foreach name,$(JCONFIGINT_FLAGS),$(shell >>$(JPEG_LIB_PATH)/jconfigint.h 
echo #define $(subst =, ,$(name))))
++$(shell >$(JPEG_LIB_PATH)/jconfigint.h echo /\* autogenerated jconfigint.h 
based on Android.mk vars JCONFIGINT_FLAGS \*/)
++$(foreach name,$(JCONFIGINT_FLAGS),$(shell >>$(JPEG_LIB_PATH)/jconfigint.h 
echo \#define $(subst =, ,$(name))))
+ 
+ ifeq ($(LIBJPEG_TURBO_ENABLED),true)
+     include $(BUILD_STATIC_LIBRARY)
+diff --git a/make/liblcms2.mk b/make/liblcms2.mk
+index e1fd3b9..29ca791 100644
+--- a/make/liblcms2.mk
++++ b/make/liblcms2.mk
+@@ -10,6 +10,10 @@ LOCAL_C_INCLUDES := \
+     $(LCMS_LIB_PATH)/include \
+     $(LCMS_LIB_PATH)/src
+ 
++LOCAL_EXPORT_C_INCLUDES := \
++    $(LCMS_LIB_PATH) \
++    $(LCMS_LIB_PATH)/include \
++    $(LCMS_LIB_PATH)/src
+ 
+ LOCAL_CFLAGS := \
+     -DHAVE_FUNC_ATTRIBUTE_VISIBILITY=1 \
+diff --git a/make/libmagick++-7.mk b/make/libmagick++-7.mk
+index 5352ccb..929396d 100644
+--- a/make/libmagick++-7.mk
++++ b/make/libmagick++-7.mk
+@@ -12,7 +12,7 @@ LOCAL_C_INCLUDES  :=  \
+ 
+ ifneq ($(STATIC_BUILD),true)
+     LOCAL_LDFLAGS += -fexceptions
+-    LOCAL_LDLIBS    := -L$(SYSROOT)/usr/lib -llog -lz
++    LOCAL_LDLIBS    := -llog -lz
+ endif
+ 
+ LOCAL_SRC_FILES := \
+diff --git a/make/libmagickcore-7.mk b/make/libmagickcore-7.mk
+index 81293b2..d51fced 100644
+--- a/make/libmagickcore-7.mk
++++ b/make/libmagickcore-7.mk
+@@ -25,6 +25,7 @@ else ifeq ($(TARGET_ARCH_ABI),x86_64)
+     
+ endif
+ 
++LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)
+ 
+ LOCAL_C_INCLUDES  += \
+     $(IMAGE_MAGICK) \
+@@ -45,10 +46,9 @@ LOCAL_C_INCLUDES  += \
+     $(BZLIB_LIB_PATH) \
+     $(LCMS_LIB_PATH)/include
+ 
+-
+ ifneq ($(STATIC_BUILD),true)
+ # ignored in static library builds
+-    LOCAL_LDLIBS    := -L$(SYSROOT)/usr/lib -llog -lz
++    LOCAL_LDLIBS    := -llog -lz
+ endif
+ 
+ 
+diff --git a/make/libmagickwand-7.mk b/make/libmagickwand-7.mk
+index 7be2fb6..0bbcca5 100644
+--- a/make/libmagickwand-7.mk
++++ b/make/libmagickwand-7.mk
+@@ -14,7 +14,7 @@ LOCAL_C_INCLUDES  :=  \
+ 
+ # always ignored in static builds
+ ifneq ($(STATIC_BUILD),true)
+-    LOCAL_LDLIBS    := -L$(SYSROOT)/usr/lib -llog -lz
++    LOCAL_LDLIBS    := -llog -lz
+ endif
+ 
+ LOCAL_SRC_FILES := \
+@@ -54,6 +54,29 @@ ifeq ($(OPENCL_BUILD),true)
+     LOCAL_SHARED_LIBRARIES += libopencl
+ endif
+ 
++LOCAL_SHARED_LIBRARIES += libstdc++
++
++ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
++    LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)/configs/arm64
++    LOCAL_C_INCLUDES += $(IMAGE_MAGICK)/configs/arm64
++else ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)  
++    LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)/configs/arm
++    LOCAL_C_INCLUDES += $(IMAGE_MAGICK)/configs/arm
++else ifeq ($(TARGET_ARCH_ABI),x86)
++    LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)/configs/x86
++    LOCAL_C_INCLUDES += $(IMAGE_MAGICK)/configs/x86
++else ifeq ($(TARGET_ARCH_ABI),x86_64)
++    LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)/configs/x86-64
++    LOCAL_C_INCLUDES += $(IMAGE_MAGICK)/configs/x86-64
++    
++    ifneq ($(STATIC_BUILD),true)
++        LOCAL_LDFLAGS += -latomic
++    endif
++    
++endif
++
++LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)
++
+ ifeq ($(BUILD_MAGICKWAND),true)
+     ifeq ($(STATIC_BUILD),true)
+         LOCAL_STATIC_LIBRARIES := \
+diff --git a/make/libpng.mk b/make/libpng.mk
+index 24fb8ac..dda05fd 100644
+--- a/make/libpng.mk
++++ b/make/libpng.mk
+@@ -30,6 +30,7 @@ ifeq ($(TARGET_ARCH_ABI), arm64-v8a)
+ endif # TARGET_ARCH_ABI == arm64-v8a
+ 
+ 
++LOCAL_EXPORT_C_INCLUDES := $(PNG_LIB_PATH)
+ LOCAL_C_INCLUDES := $(PNG_LIB_PATH)
+ 
+ LOCAL_SRC_FILES += \
+diff --git a/make/libtiff.mk b/make/libtiff.mk
+index ca43f25..2b17508 100644
+--- a/make/libtiff.mk
++++ b/make/libtiff.mk
+@@ -12,6 +12,9 @@ LOCAL_C_INCLUDES :=  \
+     $(LZMA_LIB_PATH)/liblzma/api \
+     $(WEBP_LIB_PATH)/src
+ 
++LOCAL_EXPORT_C_INCLUDES :=  \
++    $(TIFF_LIB_PATH)
++
+ ifeq ($(LIBLZMA_ENABLED),true)
+     LOCAL_CFLAGS += -DLZMA_SUPPORT=1
+ endif
+diff --git a/make/magick.mk b/make/magick.mk
+index 3ba4b1d..5471608 100644
+--- a/make/magick.mk
++++ b/make/magick.mk
+@@ -18,7 +18,7 @@ LOCAL_C_INCLUDES  :=  \
+     $(FREETYPE_LIB_PATH)/include
+ 
+ 
+-LOCAL_LDLIBS    := -L$(SYSROOT)/usr/lib -llog -lz
++LOCAL_LDLIBS    := -llog -lz
+ LOCAL_SRC_FILES := \
+     $(IMAGE_MAGICK)/utilities/magick.c \
+ 
+
+
+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/>.
diff --git a/java/Makefile.in b/java/Makefile.in
new file mode 100644
index 00000000000..804d4669c7a
--- /dev/null
+++ b/java/Makefile.in
@@ -0,0 +1,337 @@
+### @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/>.
+
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+srcdir = @srcdir@
+builddir = @builddir@
+version = @version@
+
+# Don't install movemail if mailutils are to be used.
+emacs_use_mailutils = @emacs_use_mailutils@
+
+# This is the host lib-src and lib, not the cross compiler's lib-src.
+libsrc = ../lib-src
+EXEEXT = @EXEEXT@
+
+-include ${top_builddir}/src/verbose.mk
+
+SHELL = @SHELL@
+JAVAC = @JAVAC@
+AAPT = @AAPT@
+D8 = @D8@
+ZIPALIGN = @ZIPALIGN@
+JARSIGNER = @JARSIGNER@
+APKSIGNER = @APKSIGNER@
+JARSIGNER_FLAGS =
+ANDROID_JAR = @ANDROID_JAR@
+ANDROID_ABI = @ANDROID_ABI@
+ANDROID_SDK_18_OR_EARLIER = @ANDROID_SDK_18_OR_EARLIER@
+ANDROID_SDK_8_OR_EARLIER = @ANDROID_SDK_8_OR_EARLIER@
+WARN_JAVAFLAGS = @WARN_JAVAFLAGS@
+JAVAFLAGS = $(WARN_JAVAFLAGS) -classpath "$(ANDROID_JAR):$(srcdir)"
+FIND_DELETE = @FIND_DELETE@
+
+# Android 4.3 and earlier require Emacs to be signed with a different
+# digital signature algorithm.
+
+ifneq (,$(ANDROID_SDK_18_OR_EARLIER))
+JARSIGNER_FLAGS = -sigalg MD5withRSA -digestalg SHA1
+else
+JARSIGNER_FLAGS =
+endif
+
+# When building Emacs for Android 2.2, assets must not be compressed.
+# Otherwise, the asset manager fails to extract files larger than 1
+# MB.
+
+ifneq (,$(ANDROID_SDK_8_OR_EARLIER))
+AAPT_ASSET_ARGS = -0 ""
+else
+AAPT_ASSET_ARGS =
+endif
+
+SIGN_EMACS = -keystore $(srcdir)/emacs.keystore -storepass     \
+       emacs1 $(JARSIGNER_FLAGS)
+SIGN_EMACS_V2 = sign --v2-signing-enabled --ks                 \
+       $(srcdir)/emacs.keystore -debuggable-apk-permitted      \
+       --ks-pass pass:emacs1
+
+JAVA_FILES := $(wildcard $(srcdir)/org/gnu/emacs/*.java)
+RESOURCE_FILES := $(foreach file,$(wildcard $(srcdir)/res/*),  \
+                   $(wildcard $(file)/*))
+
+# R.java is a file generated by the `aapt' utility containing
+# constants that can then be used to locate ``resource identifiers''.
+# It is not a regular file and should not be compiled as Java source
+# code.  Instead, it is automatically included by the Java compiler.
+RESOURCE_FILE := $(srcdir)/org/gnu/emacs/R.java
+
+# CLASS_FILES is what should actually be built and included in the
+# resulting Emacs executable.  The Java compiler might generate more
+# than one class file for each source file, so this only serves as a
+# list of dependencies for Make.
+CLASS_FILES := $(foreach file,$(JAVA_FILES),$(basename $(file)).class)
+
+# Remove RESOURCE_FILE from JAVA_FILES, if it is already present.
+JAVA_FILES := $(filter-out $(RESOURCE_FILE),$(JAVA_FILES))
+
+# Compute the name for the Emacs application package.  This should be:
+# emacs-<version>-<min-sdk>-<abi>.apk
+
+ANDROID_MIN_SDK := @ANDROID_MIN_SDK@
+APK_NAME := emacs-$(version)-$(ANDROID_MIN_SDK)-$(ANDROID_ABI).apk
+
+# How this stuff works.
+
+# emacs.apk depends on emacs.apk-in, which is simply a ZIP archive
+# containing the following files:
+#  lib/$(ANDROID_ABI)/libemacs.so
+#  lib/$(ANDROID_ABI)/libandroid-emacs.so
+#  lib/$(ANDROID_ABI)/libctags.so
+#  lib/$(ANDROID_ABI)/libetags.so
+#  lib/$(ANDROID_ABI)/libhexl.so
+#  lib/$(ANDROID_ABI)/libmovemail.so
+#  lib/$(ANDROID_ABI)/librcs2log.so
+#  lib/$(ANDROID_ABI)/libebrowse.so
+#  assets/info/
+#  assets/etc/
+#  assets/lisp/
+
+.PHONY: emacs.apk-in all
+all: $(APK_NAME)
+
+# Binaries to cross-compile.
+CROSS_SRC_BINS := $(top_builddir)/cross/src/android-emacs
+CROSS_LIBSRC_BINS := $(top_builddir)/cross/lib-src/ctags       \
+                    $(top_builddir)/cross/lib-src/hexl         \
+                    $(top_builddir)/cross/lib-src/ebrowse      \
+                    $(top_builddir)/cross/lib-src/emacsclient  \
+                    $(top_builddir)/cross/lib-src/etags
+CROSS_LIBSRC_BINS_MOVEMAIL := $(top_builddir)/cross/lib-src/movemail
+CROSS_EXEC_BINS := $(top_builddir)/exec/exec1 $(top_builddir)/exec/loader
+CROSS_BINS = $(CROSS_SRC_BINS) $(CROSS_LIBSRC_BINS) $(CROSS_EXEC_BINS)
+
+ifneq ($(emacs_use_mailutils),yes)
+CROSS_LIBSRC_BINS := $(CROSS_LIBSRC_BINS) $(CROSS_LIBSRC_BINS_MOVEMAIL)
+endif
+
+# Libraries to cross-compile.
+CROSS_LIBS = $(top_builddir)/cross/src/libemacs.so
+
+# Make sure gnulib is built first!
+# If not, then the recursive invocations of make below will try to
+# build gnulib at the same time.
+CROSS_ARCHIVES = $(top_builddir)/cross/lib/libgnu.a
+
+# Third party libraries to compile.
+-include $(top_builddir)/cross/ndk-build/ndk-build.mk
+
+.PHONY: $(CROSS_BINS) $(CROSS_LIBS) $(CROSS_ARCHIVES)
+
+# There should only be a single invocation of $(MAKE) -C
+# $(top_srcdir)/cross for each directory under $(top_srcdir)/cross.
+$(CROSS_SRC_BINS) $(CROSS_LIBS) &: $(CROSS_ARCHIVES)
+       $(MAKE) -C $(top_builddir)/cross $(foreach file,        \
+                                          $(CROSS_SRC_BINS)    \
+                                          $(CROSS_LIBS),       \
+                                          src/$(notdir $(file)))
+
+$(CROSS_LIBSRC_BINS) &: $(CROSS_ARCHIVES)
+       $(MAKE) -C $(top_builddir)/cross $(foreach file,                \
+                                          $(CROSS_LIBSRC_BINS),        \
+                                          lib-src/$(notdir $(file)))
+
+$(CROSS_ARCHIVES):
+       $(MAKE) -C $(top_builddir)/cross lib/libgnu.a
+
+# These two binaries are helpers used to execute binaries on Android
+# 10 and later.
+
+$(CROSS_EXEC_BINS) &:
+       $(MAKE) -C $(top_builddir)/exec $(notdir $(CROSS_EXEC_BINS))
+
+# This is needed to generate the ``.directory-tree'' file used by the
+# Android emulations of readdir and faccessat.
+
+$(libsrc)/asset-directory-tool:
+       $(MAKE) -C $(libsrc) $(notdir $@)
+
+# install_tmp is a directory used to generate emacs.apk-in.
+# That is then packaged into $(APK_NAME).
+# There is no need to depend on NDK_BUILD_SHARED as libemacs.so
+# does already.
+
+.PHONY: install_temp install_temp/assets/directory-tree
+install_temp: $(CROSS_BINS) $(CROSS_LIBS) $(RESOURCE_FILES)
+       $(AM_V_GEN)
+# Make the working directory for this stuff
+       $(AM_V_SILENT) rm -rf install_temp
+       $(AM_V_SILENT) mkdir -p install_temp/lib/$(ANDROID_ABI)
+       $(AM_V_SILENT) mkdir -p install_temp/assets/etc
+       $(AM_V_SILENT) mkdir -p install_temp/assets/lisp
+       $(AM_V_SILENT) mkdir -p install_temp/assets/info
+# Install architecture independents to assets/etc and assets/lisp
+       $(AM_V_SILENT) cp -r $(top_srcdir)/lisp install_temp/assets
+       $(AM_V_SILENT) cp -r $(top_srcdir)/etc install_temp/assets
+       $(AM_V_SILENT) cp -r $(top_srcdir)/info install_temp/assets
+# Replace etc/DOC generated by compiling Emacs for the build machine
+# with etc/DOC from the cross-compiled Emacs.
+       $(AM_V_SILENT) test -f $(top_builddir)/cross/etc/DOC    \
+         && cp -r $(top_builddir)/cross/etc/DOC                \
+            install_temp/assets/etc
+# Remove undesirable files from those directories.
+       $(AM_V_SILENT)                                          \
+       for subdir in `find install_temp -type d -print`; do    \
+         chmod a+rx $${subdir} ;                               \
+         rm -rf $${subdir}/.gitignore ;                        \
+         rm -rf $${subdir}/.DS_Store ;                         \
+         rm -rf $${subdir}/#* ;                                \
+         rm -rf $${subdir}/.#* ;                               \
+         rm -rf $${subdir}/*~ ;                                \
+         rm -rf $${subdir}/*.orig ;                            \
+         rm -rf $${subdir}/ChangeLog* ;                        \
+         rm -rf $${subdir}/[mM]akefile*[.-]in ;                \
+         rm -rf $${subdir}/Makefile;                           \
+       done
+# Generate the directory tree for those directories.
+# Install architecture dependents to lib/$(ANDROID_ABI).  This
+# perculiar naming scheme is required to make Android preserve these
+# binaries upon installation.
+       $(AM_V_SILENT)                                                          
\
+       for file in $(CROSS_BINS); do                                           
\
+         if [ -x $$file ]; then                                                
\
+           filename=`basename $$file`;                                         
\
+           cp -f $$file install_temp/lib/$(ANDROID_ABI)/lib$${filename}.so;    
\
+         fi                                                                    
\
+       done
+       $(AM_V_SILENT)                                          \
+       for file in $(CROSS_LIBS); do                           \
+         if [ -x $$file ]; then                                \
+           cp -f $$file install_temp/lib/$(ANDROID_ABI);       \
+         fi                                                    \
+       done
+ifneq ($(NDK_BUILD_SHARED),)
+       $(AM_V_SILENT) cp -f $(NDK_BUILD_SHARED) \
+         install_temp/lib/$(ANDROID_ABI)
+endif
+
+install_temp/assets/directory-tree: $(libsrc)/asset-directory-tool \
+  install_temp install_temp/assets/version                        \
+  install_temp/assets/build_info
+       $(AM_V_GEN) $(libsrc)/asset-directory-tool install_temp/assets \
+         install_temp/assets/directory-tree
+
+install_temp/assets/version: install_temp
+       $(AM_V_GEN) { (cd $(top_srcdir)                                 \
+                       && git rev-parse HEAD || echo "Unknown")        \
+                       && (git rev-parse --abbrev-ref HEAD             \
+                           || echo "Unknown") } 2> /dev/null > $@
+
+install_temp/assets/build_info: install_temp
+       $(AM_V_GEN) { hostname; date +%s; } > $@
+
+emacs.apk-in: install_temp install_temp/assets/directory-tree  \
+  AndroidManifest.xml install_temp/assets/version             \
+  install_temp/assets/build_info
+# Package everything.  Specifying the assets on this command line is
+# necessary for AAssetManager_getNextFileName to work on old versions
+# of Android.  Make sure not to generate R.java, as it's already been
+# generated.
+       $(AM_V_AAPT) $(AAPT) p -I "$(ANDROID_JAR)" -F $@   \
+         -f -M AndroidManifest.xml $(AAPT_ASSET_ARGS)     \
+         -A install_temp/assets                           \
+         -S $(top_srcdir)/java/res -J install_temp
+       $(AM_V_SILENT) pushd install_temp &> /dev/null; \
+         $(AAPT) add ../$@ `find lib -type f`;         \
+       popd &> /dev/null
+       $(AM_V_SILENT) rm -rf install_temp
+
+# Makefile itself.
+.PRECIOUS: $(top_srcdir)/config.status Makefile
+$(top_srcdir)/config.status: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4
+       $(MAKE) -C $(dir $@) $(notdir $@)
+Makefile: $(top_srcdir)/config.status $(top_srcdir)/java/Makefile.in
+       $(MAKE) -C .. java/$@
+
+# AndroidManifest.xml:
+AndroidManifest.xml: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4 \
+  $(srcdir)/AndroidManifest.xml.in
+       pushd ..; ./config.status java/AndroidManifest.xml; popd
+
+# R.java:
+$(RESOURCE_FILE): $(RESOURCE_FILES)
+       $(AM_V_GEN) $(AAPT) p -I "$(ANDROID_JAR)" -f    \
+         -J $(dir $@) -M AndroidManifest.xml           \
+         -S $(top_srcdir)/java/res
+
+# Make all class files depend on R.java being built.
+$(CLASS_FILES): $(RESOURCE_FILE)
+
+.SUFFIXES: .java .class
+$(CLASS_FILES) &: $(JAVA_FILES)
+       $(AM_V_JAVAC) $(JAVAC) $(JAVAFLAGS) $(JAVA_FILES)
+       $(AM_V_SILENT) touch $(CLASS_FILES)
+
+# N.B. that find must be called all over again in case javac generated
+# nested classes.
+
+classes.dex: $(CLASS_FILES)
+       $(AM_V_D8) $(D8) --classpath $(ANDROID_JAR)             \
+         $(subst $$,\$$,$(shell find $(srcdir) -type f         \
+           -name *.class)) --output $(builddir)
+
+# When emacs.keystore expires, regenerate it with:
+#
+#  keytool -genkey -v -keystore emacs.keystore -alias "Emacs keystore" \
+#   -keyalg RSA -sigalg  SHA1withRSA -keysize 2048 -validity 100000
+
+.PHONY: clean maintainer-clean
+
+$(APK_NAME): classes.dex emacs.apk-in $(srcdir)/emacs.keystore
+       $(AM_V_GEN)
+       $(AM_V_SILENT) cp -f emacs.apk-in $@.unaligned
+       $(AM_V_SILENT) $(AAPT) add $@.unaligned classes.dex
+       $(AM_V_SILENT) $(JARSIGNER) $(SIGN_EMACS) $@.unaligned "Emacs keystore"
+       $(AM_V_SILENT) $(ZIPALIGN) -f 4 $@.unaligned $@
+# Signing must happen after alignment!
+       $(AM_V_SILENT) $(APKSIGNER) $(SIGN_EMACS_V2) $@
+       $(AM_V_SILENT) rm -f $@.unaligned *.idsig
+
+# TAGS generation.
+
+ETAGS = $(top_builddir)/lib-src/etags
+
+$(ETAGS): FORCE
+       $(MAKE) -C ../lib-src $(notdir $@)
+
+tagsfiles = $(JAVA_FILES) $(RESOURCE_FILE)
+
+.PHONY: tags FORCE
+tags: TAGS
+TAGS: $(ETAGS) $(tagsfiles)
+       $(AM_V_GEN) $(ETAGS) $(tagsfiles)
+
+clean:
+       rm -f *.apk emacs.apk-in *.dex *.unaligned *.class *.idsig
+       rm -rf install-temp $(RESOURCE_FILE) TAGS
+       find . -name '*.class' $(FIND_DELETE)
+
+maintainer-clean distclean bootstrap-clean: clean
+       rm -f Makefile ndk-build.mk
diff --git a/java/README b/java/README
new file mode 100644
index 00000000000..a6adb805b9e
--- /dev/null
+++ b/java/README
@@ -0,0 +1,1049 @@
+This directory holds the Java sources of the port of GNU Emacs to
+Android-like systems, along with files needed to create an application
+package out of them.  If you need to build this port, please read the
+file INSTALL in this directory.
+
+The ``org/gnu/emacs'' subdirectory contains the Java sources under the
+``org.gnu.emacs'' package identifier.
+
+``AndroidManifest.xml'' contains a manifest describing the Java
+sources to the system.
+
+The ``res'' directory contains resources, mainly the Emacs icon and
+several ``boolean resources'' which are used as a form of conditional
+evaluation for manifest entries.
+
+`emacs.keystore' is the signing key used to build Emacs.  It is kept
+here, and we encourage all people redistributing Emacs to use this
+key.  It holds no security value, and otherwise it will be impossible
+to install different builds of Emacs on top of each other.
+
+Please keep the Java code indented with tabs and formatted according
+to the rules for C code in the GNU coding standards.  Always use
+C-style comments.
+
+======================================================================
+
+OVERVIEW OF JAVA
+
+Emacs developers do not know Java, and there is no reason they should
+have to.  Thus, the code in this directory is confined to what is
+strictly necessary to support Emacs, and only uses a subset of Java
+written in a way that is easily understandable to C programmers.
+
+Java is required because the entire Android runtime is based around
+Java, and there is no way to write an Android program which runs
+without Java.
+
+This text exists to prime other Emacs developers, already familar with
+C, on the basic architecture of the Android port, and to teach them
+how to read and write the Java code found in this directory.
+
+Java is an object oriented language with automatic memory management
+compiled down to bytecode, which is then subject to interpretation by
+a Java virtual machine.
+
+What that means, is that:
+
+struct emacs_window
+{
+  int some_fields;
+  int of_emacs_window;
+};
+
+static void
+do_something_with_emacs_window (struct emacs_window *a, int n)
+{
+  a->some_fields = a->of_emacs_window + n;
+}
+
+would be written:
+
+public class EmacsWindow
+{
+  public int someFields;
+  public int ofEmacsWindow;
+
+  public void
+  doSomething (int n)
+  {
+    someFields = ofEmacsWindow + n;
+  }
+}
+
+and instead of doing:
+
+do_something_with_emacs_window (my_window, 1);
+
+you say:
+
+myWindow.doSomething (1);
+
+In addition to functions associated with an object of a given class
+(such as EmacsWindow), Java also has two other kinds of functions.
+
+The first are so-called ``static'' functions (the static means
+something entirely different from what it does in C.)
+
+A static function, while still having to be defined within a class,
+can be called without any object.  Instead of the object, you write
+the name of the Java class within which it is defined. For example,
+the following C code:
+
+int
+multiply_a_with_b_and_then_add_c (int a, int b, int c)
+{
+  return a * b + c;
+}
+
+would be:
+
+public class EmacsSomething
+{
+  public static int
+  multiplyAWithBAndThenAddC (int a, int b, int c)
+  {
+    return a * b + c;
+  }
+};
+
+Then, instead of calling:
+
+int foo;
+
+foo = multiply_a_with_b_then_add_c (1, 2, 3);
+
+you say:
+
+int foo;
+
+foo = EmacsSomething.multiplyAWithBAndThenAddC (1, 2, 3);
+
+In Java, ``static'' does not mean that the function is only used
+within its compilation unit!  Instead, the ``private'' qualifier is
+used to mean more or less the same thing:
+
+static void
+this_procedure_is_only_used_within_this_file (void)
+{
+  do_something ();
+}
+
+becomes
+
+public class EmacsSomething
+{
+  private static void
+  thisProcedureIsOnlyUsedWithinThisClass ()
+  {
+
+  }
+}
+
+the other kind are called ``constructors''.  They are functions that
+must be called to allocate memory to hold a class:
+
+public class EmacsFoo
+{
+  int bar;
+
+  public
+  EmacsFoo (int tokenA, int tokenB)
+  {
+    bar = tokenA + tokenB;
+  }
+}
+
+now, the following statement:
+
+EmacsFoo foo;
+
+foo = new EmacsFoo (1, 2);
+
+becomes more or less equivalent to the following C code:
+
+struct emacs_foo
+{
+  int bar;
+};
+
+struct emacs_foo *
+make_emacs_foo (int token_a, int token_b)
+{
+  struct emacs_foo *foo;
+
+  foo = xmalloc (sizeof *foo);
+  foo->bar = token_a + token_b;
+
+  return foo;
+}
+
+/* ... */
+
+struct emacs_foo *foo;
+
+foo = make_emacs_foo (1, 2);
+
+A class may have any number of constructors, or no constructors at
+all, in which case the compiler inserts an empty constructor.
+
+
+
+Sometimes, you will see Java code that looks like this:
+
+    allFiles = filesDirectory.listFiles (new FileFilter () {
+       @Override
+       public boolean
+       accept (File file)
+       {
+         return (!file.isDirectory ()
+                 && file.getName ().endsWith (".pdmp"));
+       }
+      });
+
+This is Java's version of GCC's nested function extension.  The major
+difference is that the nested function may still be called even after
+it goes out of scope, and always retains a reference to the class and
+local variables around where it was called.
+
+Being an object-oriented language, Java also allows defining that a
+class ``extends'' another class.  The following C code:
+
+struct a
+{
+  long thirty_two;
+};
+
+struct b
+{
+  struct a a;
+  long long sixty_four;
+};
+
+extern void do_something (struct a *);
+
+void
+my_function (struct b *b)
+{
+  do_something (&b->a);
+}
+
+is roughly equivalent to the following Java code, split into two
+files:
+
+  A.java
+
+public class A
+{
+  int thirtyTwo;
+
+  public void
+  doSomething ()
+  {
+    etcEtcEtc ();
+  }
+};
+
+  B.java
+
+public class B extends A
+{
+  long sixty_four;
+
+  public static void
+  myFunction (B b)
+  {
+    b.doSomething ();
+  }
+}
+
+the Java runtime has transformed the call to ``b.doSomething'' to
+``((A) b).doSomething''.
+
+However, Java also allows overriding this behavior, by specifying the
+@Override keyword:
+
+public class B extends A
+{
+  long sixty_four;
+
+  @Override
+  public void
+  doSomething ()
+  {
+    Something.doSomethingTwo ();
+    super.doSomething ();
+  }
+}
+
+now, any call to ``doSomething'' on a ``B'' created using ``new B ()''
+will end up calling ``Something.doSomethingTwo'', before calling back
+to ``A.doSomething''.  This override also applies in reverse; that is
+to say, even if you write:
+
+  ((A) b).doSomething ();
+
+B's version of doSomething will still be called, if ``b'' was created
+using ``new B ()''.
+
+This mechanism is used extensively throughout the Java language and
+Android windowing APIs.
+
+Elsewhere, you will encounter Java code that defines arrays:
+
+public class EmacsFrobinicator
+{
+  public static void
+  emacsFrobinicate (int something)
+  {
+    int[] primesFromSomething;
+
+    primesFromSomething = new int[numberOfPrimes];
+    /* ... */
+  }
+}
+
+Java arrays are similar to C arrays in that they can not grow.  But
+they are very much unlike C arrays in that they are always references
+(as opposed to decaying into pointers in only some situations), and
+contain information about their length.
+
+If another function named ``frobinicate1'' takes an array as an
+argument, then it need not take the length of the array.
+
+Instead, it may simply iterate over the array like so:
+
+int i, k;
+
+for (i = 0; i < array.length; ++i)
+  {
+    k = array[i];
+
+    Whatever.doSomethingWithK (k);
+  }
+
+The syntax used to define arrays is also slightly different.  As
+arrays are always references, there is no way for you to tell the
+runtime to allocate an array of size N in a structure (class.)
+
+Instead, if you need an array of that size, you must declare a field
+with the type of the array, and allocate the array inside the class's
+constructor, like so:
+
+public class EmacsArrayContainer
+{
+  public int[] myArray;
+
+  public
+  EmacsArrayContainer ()
+  {
+    myArray = new array[10];
+  }
+}
+
+while in C, you could just have written:
+
+struct emacs_array_container
+{
+  int my_array[10];
+};
+
+or, possibly even better,
+
+typedef int emacs_array_container[10];
+
+Alas, Java has no equivalent of `typedef'.
+
+Like in C, Java string literals are delimited by double quotes.
+Unlike C, however, strings are not NULL-terminated arrays of
+characters, but a distinct type named ``String''.  They store their
+own length, characters in Java's 16-bit ``char'' type, and are capable
+of holding NULL bytes.
+
+Instead of writing:
+
+wchar_t character;
+extern char *s;
+size_t s;
+
+  for (/* determine n, s in a loop.  */)
+    s += mbstowc (&character, s, n);
+
+or:
+
+const char *byte;
+
+for (byte = my_string; *byte; ++byte)
+  /* do something with *byte.  */;
+
+or perhaps even:
+
+size_t length, i;
+char foo;
+
+length = strlen (my_string);
+
+for (i = 0; i < length; ++i)
+  foo = my_string[i];
+
+you write:
+
+char foo;
+int i;
+
+for (i = 0; i < myString.length (); ++i)
+  foo = myString.charAt (0);
+
+Java also has stricter rules on what can be used as a truth value in a
+conditional.  While in C, any non-zero value is true, Java requires
+that every truth value be of the boolean type ``boolean''.
+
+What this means is that instead of simply writing:
+
+  if (foo || bar)
+
+where foo can either be 1 or 0, and bar can either be NULL or a
+pointer to something, you must explicitly write:
+
+  if (foo != 0 || bar != null)
+
+in Java.
+
+JAVA NATIVE INTERFACE
+
+Java also provides an interface for C code to interface with Java.
+
+C functions exported from a shared library become static Java
+functions within a class, like so:
+
+public class EmacsNative
+{
+  /* Obtain the fingerprint of this build of Emacs.  The fingerprint
+     can be used to determine the dump file name.  */
+  public static native String getFingerprint ();
+
+  /* Set certain parameters before initializing Emacs.
+
+     assetManager must be the asset manager associated with the
+     context that is loading Emacs.  It is saved and remains for the
+     remainder the lifetime of the Emacs process.
+
+     filesDir must be the package's data storage location for the
+     current Android user.
+
+     libDir must be the package's data storage location for native
+     libraries.         It is used as PATH.
+
+     cacheDir must be the package's cache directory.  It is used as
+     the `temporary-file-directory'.
+
+     pixelDensityX and pixelDensityY are the DPI values that will be
+     used by Emacs.
+
+     classPath must be the classpath of this app_process process, or
+     NULL.
+
+     emacsService must be the EmacsService singleton, or NULL. */
+  public static native void setEmacsParams (AssetManager assetManager,
+                                           String filesDir,
+                                           String libDir,
+                                           String cacheDir,
+                                           float pixelDensityX,
+                                           float pixelDensityY,
+                                           String classPath,
+                                           EmacsService emacsService);
+}
+
+Where the corresponding C functions are located in android.c, and
+loaded by the special invocation:
+
+  static
+  {
+    System.loadLibrary ("emacs");
+  };
+
+where ``static'' defines a section of code which will be run upon the
+object (containing class) being loaded.  This is like:
+
+  __attribute__((constructor))
+
+on systems where shared object constructors are supported.
+
+See http://docs.oracle.com/en/java/javase/19/docs/specs/jni/intro.html
+for more details.
+
+
+
+OVERVIEW OF ANDROID
+
+When the Android system starts an application, it does not actually
+call the application's ``main'' function.  It may not even start the
+application's process if one is already running.
+
+Instead, Android is organized around components.  When the user opens
+the ``Emacs'' icon, the Android system looks up and starts the
+component associated with the ``Emacs'' icon.  In this case, the
+component is called an activity, and is declared in
+the AndroidManifest.xml in this directory:
+
+    <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" />
+       <category android:name="android.intent.category.DEFAULT" />
+       <category android:name="android.intent.category.LAUNCHER" />
+      </intent-filter>
+    </activity>
+
+This tells Android to start the activity defined in ``EmacsActivity''
+(defined in org/gnu/emacs/EmacsActivity.java), a class extending the
+Android class ``Activity''.
+
+To do so, the Android system creates an instance of ``EmacsActivity''
+and the window system window associated with it, and eventually calls:
+
+  Activity activity;
+
+  activity.onCreate (...);
+
+But which ``onCreate'' is really called?
+It is actually the ``onCreate'' defined in EmacsActivity.java, as
+it overrides the ``onCreate'' defined in Android's own Activity class:
+
+  @Override
+  public void
+  onCreate (Bundle savedInstanceState)
+  {
+    FrameLayout.LayoutParams params;
+    Intent intent;
+
+Then, this is what happens step-by-step within the ``onCreate''
+function:
+
+    /* See if Emacs should be started with -Q. */
+    intent = getIntent ();
+    EmacsService.needDashQ
+      = intent.getBooleanExtra ("org.gnu.emacs.START_DASH_Q",
+                               false);
+
+Here, Emacs obtains the intent (a request to start a component) which
+was used to start Emacs, and sets a special flag if it contains a
+request for Emacs to start with the ``-Q'' command-line argument.
+
+    /* Set the theme to one without a title bar.  */
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+      setTheme (android.R.style.Theme_DeviceDefault_NoActionBar);
+    else
+      setTheme (android.R.style.Theme_NoTitleBar);
+
+Next, Emacs sets an appropriate theme for the activity's associated
+window decorations.
+
+    params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT,
+                                          LayoutParams.MATCH_PARENT);
+
+    /* Make the frame layout.  */
+    layout = new FrameLayout (this);
+    layout.setLayoutParams (params);
+
+    /* Set it as the content view.  */
+    setContentView (layout);
+
+Then, Emacs creates a ``FrameLayout'', a widget that holds a single
+other widget, and makes it the activity's ``content view''.
+
+The activity itself is a ``FrameLayout'', so the ``layout parameters''
+here apply to the FrameLayout itself, and not its children.
+
+    /* Maybe start the Emacs service if necessary.  */
+    EmacsService.startEmacsService (this);
+
+And after that, Emacs calls the static function ``startEmacsService'',
+defined in the class ``EmacsService''. This starts the Emacs service
+component if necessary.
+
+    /* Add this activity to the list of available activities.  */
+    EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this);
+
+    super.onCreate (savedInstanceState);
+
+Finally, Emacs registers that this activity is now ready to receive
+top-level frames (windows) created from Lisp.
+
+Activities come and go, but Emacs has to stay running in the mean
+time.  Thus, Emacs also defines a ``service'', which is a long-running
+component that the Android system allows to run in the background.
+
+Let us go back and review the definition of ``startEmacsService'':
+
+  public static void
+  startEmacsService (Context context)
+  {
+    if (EmacsService.SERVICE == null)
+      {
+       if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+         /* Start the Emacs service now.  */
+         context.startService (new Intent (context,
+                                           EmacsService.class));
+       else
+         /* Display the permanant notification and start Emacs as a
+            foreground service.  */
+         context.startForegroundService (new Intent (context,
+                                                     EmacsService.class));
+      }
+  }
+
+If ``EmacsService.SERVICE'' does not yet exist, what this does is to
+tell the ``context'' (the equivalent of an Xlib Display *) to start a
+service defined by the class ``EmacsService''. Eventually, this
+results in ``EmacsService.onCreate'' being called:
+
+  @Override
+  public void
+  onCreate ()
+  {
+    AssetManager manager;
+    Context app_context;
+    String filesDir, libDir, cacheDir, classPath;
+    double pixelDensityX;
+    double pixelDensityY;
+
+Here is what this function does, step-by-step:
+
+    SERVICE = this;
+
+First, it sets the special static variable ``SERVICE'' to ``this'',
+which is a pointer to the ``EmacsService' object that was created.
+
+    handler = new Handler (Looper.getMainLooper ());
+
+Next, it creates a ``Handler'' object for the ``main looper''.
+This is a helper structure which allows executing code on the Android
+user interface thread.
+
+    manager = getAssets ();
+    app_context = getApplicationContext ();
+    metrics = getResources ().getDisplayMetrics ();
+    pixelDensityX = metrics.xdpi;
+    pixelDensityY = metrics.ydpi;
+
+Finally, it obtains:
+
+  - the asset manager, which is used to retrieve assets packaged
+    into the Emacs application package.
+
+  - the application context, used to obtain application specific
+    information.
+
+  - the display metrics, and from them, the X and Y densities in dots
+    per inch.
+
+Then, inside a ``try'' block:
+
+    try
+      {
+       /* Configure Emacs with the asset manager and other necessary
+          parameters.  */
+       filesDir = app_context.getFilesDir ().getCanonicalPath ();
+       libDir = getLibraryDirectory ();
+       cacheDir = app_context.getCacheDir ().getCanonicalPath ();
+
+It obtains the names of the Emacs home, shared library, and temporary
+file directories.
+
+       /* Now provide this application's apk file, so a recursive
+          invocation of app_process (through android-emacs) can
+          find EmacsNoninteractive.  */
+       classPath = getApkFile ();
+
+The name of the Emacs application package.
+
+       Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir
+              + ", libDir = " + libDir + ", and classPath = " + classPath);
+
+Prints a debug message to the Android system log with this
+information.
+
+       EmacsNative.setEmacsParams (manager, filesDir, libDir,
+                                   cacheDir, (float) pixelDensityX,
+                                   (float) pixelDensityY,
+                                   classPath, this);
+
+And calls the native function ``setEmacsParams'' (defined in
+android.c) to configure Emacs with this information.
+
+       /* Start the thread that runs Emacs.  */
+       thread = new EmacsThread (this, needDashQ);
+       thread.start ();
+
+Then, it allocates an ``EmacsThread'' object, and starts that thread.
+Inside that thread is where Emacs's C code runs.
+
+      }
+    catch (IOException exception)
+      {
+       EmacsNative.emacsAbort ();
+       return;
+
+And here is the purpose of the ``try'' block.  Functions related to
+file names in Java will signal errors of various types upon failure.
+
+This ``catch'' block means that the Java virtual machine will abort
+execution of the contents of the ``try'' block as soon as an error of
+type ``IOException'' is encountered, and begin executing the contents
+of the ``catch'' block.
+
+Any failure of that type here is a crash, and
+``EmacsNative.emacsAbort'' is called to quickly abort the process to
+get a useful backtrace.
+      }
+  }
+
+Now, let us look at the definition of the class ``EmacsThread'', found
+in org/gnu/emacs/EmacsThread.java:
+
+public class EmacsThread extends Thread
+{
+  /* Whether or not Emacs should be started -Q.         */
+  private boolean startDashQ;
+
+  public
+  EmacsThread (EmacsService service, boolean startDashQ)
+  {
+    super ("Emacs main thread");
+    this.startDashQ = startDashQ;
+  }
+
+  @Override
+  public void
+  run ()
+  {
+    String args[];
+
+    if (!startDashQ)
+      args = new String[] { "libandroid-emacs.so", };
+    else
+      args = new String[] { "libandroid-emacs.so", "-Q", };
+
+    /* Run the native code now.         */
+    EmacsNative.initEmacs (args, EmacsApplication.dumpFileName);
+  }
+};
+
+The class itself defines a single field, ``startDashQ'', a constructor
+with an unused argument of the type ``EmacsService'' (which is useful
+while debugging) and a flag ``startDashQ'', and a single function
+``run'', overriding the same function in the class ``Thread''.
+
+When ``thread.start'' is called, the Java virtual machine creates a
+new thread, and then calls the function ``run'' within that thread.
+
+This function then computes a suitable argument vector, and calls
+``EmacsNative.initEmacs'' (defined in android.c), which then calls a
+modified version of the regular Emacs ``main'' function.
+
+At that point, Emacs initialization proceeds as usual:
+Vinitial_window_system is set, loadup.el calls `normal-top-level',
+which calls `command-line', and finally
+`window-system-initialization', which initializes the `android'
+terminal interface as usual.
+
+What happens here is the same as on other platforms.  Now, here is
+what happens when the initial frame is created: Fx_create_frame calls
+`android_create_frame_window' to create a top level window:
+
+static void
+android_create_frame_window (struct frame *f)
+{
+  struct android_set_window_attributes attributes;
+  enum android_window_value_mask attribute_mask;
+
+  attributes.background_pixel = FRAME_BACKGROUND_PIXEL (f);
+  attribute_mask = ANDROID_CW_BACK_PIXEL;
+
+  block_input ();
+  FRAME_ANDROID_WINDOW (f)
+    = android_create_window (FRAME_DISPLAY_INFO (f)->root_window,
+                            f->left_pos,
+                            f->top_pos,
+                            FRAME_PIXEL_WIDTH (f),
+                            FRAME_PIXEL_HEIGHT (f),
+                            attribute_mask, &attributes);
+  unblock_input ();
+}
+
+This calls the function `android_create_window' with some arguments
+whose meanings are identical to the arguments to `XCreateWindow'.
+
+Here is the definition of `android_create_window', in android.c:
+
+android_window
+android_create_window (android_window parent, int x, int y,
+                      int width, int height,
+                      enum android_window_value_mask value_mask,
+                      struct android_set_window_attributes *attrs)
+{
+  static jclass class;
+  static jmethodID constructor;
+  jobject object, parent_object, old;
+  android_window window;
+  android_handle prev_max_handle;
+  bool override_redirect;
+
+What does it do? First, some context:
+
+At any time, there can be at most 65535 Java objects referred to by
+the rest of Emacs through the Java native interface.  Each such object
+is assigned a ``handle'' (similar to an XID on X) and given a unique
+type.  The function `android_resolve_handle' returns the JNI `jobject'
+associated with a given handle.
+
+  parent_object = android_resolve_handle (parent, ANDROID_HANDLE_WINDOW);
+
+Here, it is being used to look up the `jobject' associated with the
+`parent' handle.
+
+  prev_max_handle = max_handle;
+  window = android_alloc_id ();
+
+Next, `max_handle' is saved, and a new handle is allocated for
+`window'.
+
+  if (!window)
+    error ("Out of window handles!");
+
+An error is signalled if Emacs runs out of available handles.
+
+  if (!class)
+    {
+      class = (*android_java_env)->FindClass (android_java_env,
+                                             "org/gnu/emacs/EmacsWindow");
+      assert (class != NULL);
+
+Then, if this initialization has not yet been completed, Emacs
+proceeds to find the Java class named ``EmacsWindow''.
+
+      constructor
+       = (*android_java_env)->GetMethodID (android_java_env, class, "<init>",
+                                           "(SLorg/gnu/emacs/EmacsWindow;"
+                                           "IIIIZ)V");
+      assert (constructor != NULL);
+
+And it tries to look up the constructor, which should take seven
+arguments:
+
+  S                                    - a short.  (the handle ID)
+  Lorg/gnu/Emacs/EmacsWindow;          - an instance of the EmacsWindow
+                                         class.  (the parent)
+  IIII                                 - four ints.  (the window geometry.)
+  Z                                    - a boolean.  (whether or not the
+                                         window is override-redirect; see
+                                         XChangeWindowAttributes.)
+
+      old = class;
+      class = (*android_java_env)->NewGlobalRef (android_java_env, class);
+      (*android_java_env)->ExceptionClear (android_java_env);
+      ANDROID_DELETE_LOCAL_REF (old);
+
+Next, it saves a global reference to the class and deletes the local
+reference.  Global references will never be deallocated by the Java
+virtual machine as long as they still exist.
+
+      if (!class)
+       memory_full (0);
+    }
+
+  /* N.B. that ANDROID_CW_OVERRIDE_REDIRECT can only be set at window
+     creation time.  */
+  override_redirect = ((value_mask
+                       & ANDROID_CW_OVERRIDE_REDIRECT)
+                      && attrs->override_redirect);
+
+  object = (*android_java_env)->NewObject (android_java_env, class,
+                                          constructor, (jshort) window,
+                                          parent_object, (jint) x, (jint) y,
+                                          (jint) width, (jint) height,
+                                          (jboolean) override_redirect);
+
+Then, it creates an instance of the ``EmacsWindow'' class with the
+appropriate arguments and previously determined constructor.
+
+  if (!object)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+
+      max_handle = prev_max_handle;
+      memory_full (0);
+
+If creating the object fails, Emacs clears the ``pending exception''
+and signals that it is out of memory.
+    }
+
+  android_handles[window].type = ANDROID_HANDLE_WINDOW;
+  android_handles[window].handle
+    = (*android_java_env)->NewGlobalRef (android_java_env,
+                                        object);
+  (*android_java_env)->ExceptionClear (android_java_env);
+  ANDROID_DELETE_LOCAL_REF (object);
+
+Otherwise, it associates a new global reference to the object with the
+handle, and deletes the local reference returned from the JNI
+NewObject function.
+
+  if (!android_handles[window].handle)
+    memory_full (0);
+
+If allocating the global reference fails, Emacs signals that it is out
+of memory.
+
+  android_change_window_attributes (window, value_mask, attrs);
+  return window;
+
+Otherwise, it applies the specified window attributes and returns the
+handle of the new window.
+}
+
+
+
+DRAWABLES, CURSORS AND HANDLES
+
+Each widget created by Emacs corresponds to a single ``window'', which
+has its own backing store.  This arrangement is quite similar to X.
+
+C code does not directly refer to the EmacsView widgets that implement
+the UI logic behind windows.  Instead, its handles refer to
+EmacsWindow structures, which contain the state necessary to interact
+with the widgets in an orderly and synchronized manner.
+
+Like X, both pixmaps and windows are drawable resources, and the same
+graphics operations can be applied to both.  Thus, a separate
+EmacsPixmap structure is used to wrap around Android Bitmap resources,
+and the Java-level graphics operation functions are capable of
+operating on them both.
+
+Finally, graphics contexts are maintained on both the C and Java
+levels; the C state recorded in `struct android_gc' is kept in sync
+with the Java state in the GContext handle's corresponding EmacsGC
+structure, and cursors are used through handles that refer to
+EmacsCursor structures that hold system PointerIcons.
+
+In all cases, the interfaces provided are identical to X.
+
+
+
+EVENT LOOP
+
+In a typical Android application, the event loop is managed by the
+operating system, and callbacks (implemented through overriding
+separate functions in widgets) are run by the event loop wherever
+necessary.  The thread which runs the event loop is also the only
+thread capable of creating and manipulating widgets and activities,
+and is referred to as the ``UI thread''.
+
+These callbacks are used by Emacs to write representations of X-like
+events to a separate event queue, which are then read from Emacs's own
+event loop running in a separate thread.  This is accomplished through
+replacing `select' by a function which waits for the event queue to be
+occupied, in addition to any file descriptors that `select' would
+normally wait for.
+
+Conversely, Emacs's event loop sometimes needs to send events to the
+UI thread.  These events are implemented as tiny fragments of code,
+which are run as they are received by the main thread.
+
+A typical example is `displayToast', which is implemented in
+EmacsService.java:
+
+  public void
+  displayToast (final String string)
+  {
+    runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         Toast toast;
+
+         toast = Toast.makeText (getApplicationContext (),
+                                 string, Toast.LENGTH_SHORT);
+         toast.show ();
+       }
+      });
+  }
+
+Here, the variable `string' is used by a nested function.  This nested
+function contains a copy of that variable, and is run on the main
+thread using the function `runOnUiThread', in order to display a short
+status message on the display.
+
+When Emacs needs to wait for the nested function to finish, it uses a
+mechanism implemented in `syncRunnable'.  This mechanism first calls a
+deadlock avoidance mechanism, then runs a nested function on the UI
+thread, which is expected to signal itself as a condition variable
+upon completion.  It is typically used to allocate resources that can
+only be allocated from the UI thread, or to obtain non-thread-safe
+information.  The following function is an example; it returns a new
+EmacsView widget corresponding to the provided window:
+
+  public EmacsView
+  getEmacsView (final EmacsWindow window, final int visibility,
+               final boolean isFocusedByDefault)
+  {
+    Runnable runnable;
+    final EmacsHolder<EmacsView> view;
+
+    view = new EmacsHolder<EmacsView> ();
+
+    runnable = new Runnable () {
+       public void
+       run ()
+       {
+         synchronized (this)
+           {
+             view.thing = new EmacsView (window);
+             view.thing.setVisibility (visibility);
+
+             /* The following function is only present on Android 26
+                or later.  */
+             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+               view.thing.setFocusedByDefault (isFocusedByDefault);
+
+             notify ();
+           }
+       }
+      };
+
+    syncRunnable (runnable);
+    return view.thing;
+  }
+
+As no value can be directly returned from the nested function, a
+separate container object is used to hold the result after the
+function finishes execution.  Note the type name inside the angle
+brackets: this type is substituted into the class definition as it is
+used; a definition such as:
+
+public class Foo<T>
+{
+  T bar;
+};
+
+can not be used alone:
+
+  Foo holder; /* Error! */
+
+but must have a type specified:
+
+  Foo<Object> holder;
+
+in which case the effective definition is:
+
+public class Foo
+{
+  Object bar;
+};
+
diff --git a/java/debug.sh b/java/debug.sh
new file mode 100755
index 00000000000..d6e439bec90
--- /dev/null
+++ b/java/debug.sh
@@ -0,0 +1,368 @@
+#!/bin/bash
+### Run Emacs under GDB or JDB on Android.
+
+## 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/>.
+
+set -m
+oldpwd=`pwd`
+cd `dirname $0`
+
+devices=`adb devices | grep device | awk -- '/device\y/ { print $1 }' -`
+device=
+progname=$0
+package=org.gnu.emacs
+activity=org.gnu.emacs.EmacsActivity
+gdb_port=5039
+jdb_port=64013
+jdb=no
+attach_existing=no
+gdbserver=
+gdb=gdb
+
+while [ $# -gt 0 ]; do
+    case "$1" in
+       ## This option specifies the serial number of a device to use.
+       "--device" )
+           device="$2"
+           if [ -z device ]; then
+               echo "You must specify an argument to --device"
+               exit 1
+           fi
+           shift
+           ;;
+       "--help" )
+           echo "Usage: $progname [options] -- [gdb options]"
+           echo ""
+           echo "  --device DEVICE     run Emacs on the specified device"
+           echo "  --port PORT         run the GDB server on a specific port"
+           echo "  --jdb-port PORT     run the JDB server on a specific port"
+           echo "  --jdb               run JDB instead of GDB"
+           echo "  --gdb               use specified GDB binary"
+           echo "  --attach-existing   attach to an existing process"
+           echo "  --gdbserver BINARY  upload and use the specified gdbserver 
binary"
+           echo "  --help              print this message"
+           echo ""
+           echo "Available devices:"
+           for device in $devices; do
+               echo "  " $device
+           done
+           echo ""
+           exit 0
+           ;;
+       "--jdb" )
+           jdb=yes
+           ;;
+       "--gdb" )
+           shift
+           gdb=$1
+           ;;
+       "--gdbserver" )
+           shift
+           gdbserver=$1
+           ;;
+       "--port" )
+           shift
+           gdb_port=$1
+           ;;
+       "--jdb-port" )
+           shift
+           jdb_port=$1
+           ;;
+       "--attach-existing" )
+           attach_existing=yes
+           ;;
+       "--" )
+           shift
+           gdbargs=$@
+           break;
+           ;;
+       * )
+           echo "$progname: Unrecognized argument $1"
+           exit 1
+           ;;
+    esac
+    shift
+done
+
+if [ -z "$devices" ]; then
+    echo "No devices are available."
+    exit 1
+fi
+
+if [ -z $device ]; then
+    device=$devices
+fi
+
+if [ `wc -w <<< "$devices"` -gt 1 ] && [ -z device ]; then
+    echo "Multiple devices are available.  Please pick one using"
+    echo "--device and try again."
+fi
+
+echo "Looking for $package on device $device"
+
+# Find the application data directory
+app_data_dir=`adb -s $device shell run-as $package sh -c 'pwd 2> /dev/null'`
+
+if [ -z $app_data_dir ]; then
+   echo "The data directory for the package $package was not found."
+   echo "Is it installed?"
+fi
+
+echo "Found application data directory at" "$app_data_dir"
+
+# Generate an awk script to extract PIDs from Android ps output.  It
+# is enough to run `ps' as the package user on newer versions of
+# Android, but that doesn't work on Android 2.3.
+cat << EOF > tmp.awk
+BEGIN {
+  pid = 0;
+  pid_column = 2;
+}
+
+{
+  # Remove any trailing carriage return from the input line.
+  gsub ("\r", "", \$NF)
+
+  # If this is line 1, figure out which column contains the PID.
+  if (NR == 1)
+    {
+      for (n = 1; n <= NF; ++n)
+       {
+         if (\$n == "PID")
+           pid_column=n;
+       }
+    }
+  else if (\$NF == "$package")
+   print \$pid_column
+}
+EOF
+
+# Make sure that file disappears once this script exits.
+trap "rm -f $(pwd)/tmp.awk" 0
+
+# First, run ps to fetch the list of process IDs.
+package_pids=`adb -s $device shell ps`
+
+# Next, extract the list of PIDs currently running.
+package_pids=`awk -f tmp.awk <<< $package_pids`
+
+if [ "$attach_existing" != "yes" ]; then
+    # Finally, kill each existing process.
+    for pid in $package_pids; do
+       echo "Killing existing process $pid..."
+       adb -s $device shell run-as $package kill -9 $pid &> /dev/null
+    done
+
+    # Now run the main activity.  This must be done as the adb user and
+    # not as the package user.
+    echo "Starting activity $activity and attaching debugger"
+
+    # Exit if the activity could not be started.
+    adb -s $device shell am start -D -n "$package/$activity"
+    if [ ! $? ]; then
+       exit 1;
+    fi
+
+    # Sleep for a bit.  Otherwise, the process may not have started
+    # yet.
+    sleep 1
+
+    # Now look for processes matching the package again.
+    package_pids=`adb -s $device shell ps`
+
+    # Next, remove lines matching "ps" itself.
+    package_pids=`awk -f tmp.awk <<< $package_pids`
+fi
+
+pid=$package_pids
+num_pids=`wc -w <<< "$package_pids"`
+
+if [ $num_pids -gt 1 ]; then
+    echo "More than one process was started:"
+    echo ""
+    adb -s $device shell run-as $package ps | awk -- "{
+      if (!match (\$0, /ps/) && match (\$0, /$package/))
+        print \$0
+    }"
+    echo ""
+    printf "Which one do you want to attach to? "
+    read pid
+elif [ -z $package_pids ]; then
+    echo "No processes were found to attach to."
+    exit 1
+fi
+
+# If either --jdb was specified or debug.sh is not connecting to an
+# existing process, then store a suitable JDB invocation in
+# jdb_command.  GDB will then run JDB to unblock the application from
+# the wait dialog after startup.
+
+if [ "$jdb" = "yes" ] || [ "$attach_existing" != yes ]; then
+    adb -s $device forward --remove-all
+    adb -s $device forward "tcp:$jdb_port" "jdwp:$pid"
+
+    if [ ! $? ]; then
+       echo "Failed to forward jdwp:$pid to $jdb_port!"
+       echo "Perhaps you need to specify a different port with --port?"
+       exit 1;
+    fi
+
+    jdb_command="jdb -connect \
+                com.sun.jdi.SocketAttach:hostname=localhost,port=$jdb_port"
+
+    if [ $jdb = "yes" ]; then
+       # Just start JDB and then exit
+       $jdb_command
+       exit 1
+    fi
+fi
+
+if [ -n "$jdb_command" ]; then
+    echo "Starting JDB to unblock application."
+
+    # Start JDB to unblock the application.
+    coproc JDB { $jdb_command; }
+
+    # Tell JDB to first suspend all threads.
+    echo "suspend" >&${JDB[1]}
+
+    # Tell JDB to print a magic string once the program is
+    # initialized.
+    echo "print \"__verify_jdb_has_started__\"" >&${JDB[1]}
+
+    # Now wait for JDB to give the string back.
+    line=
+    while :; do
+       read -u ${JDB[0]} line
+       if [ ! $? ]; then
+           echo "Failed to read JDB output"
+           exit 1
+       fi
+
+       case "$line" in
+           *__verify_jdb_has_started__*)
+               # Android only polls for a Java debugger every 200ms, so
+               # the debugger must be connected for at least that long.
+               echo "Pausing 1 second for the program to continue."
+               sleep 1
+               break
+               ;;
+       esac
+    done
+
+    # Note that JDB does not exit until GDB is fully attached!
+fi
+
+# See if gdbserver has to be uploaded
+gdbserver_cmd=
+is_root=
+if [ -z "$gdbserver" ]; then
+    gdbserver_bin=/system/bin/gdbserver64
+else
+    gdbserver_bin=/data/local/tmp/gdbserver
+    gdbserver_cat="cat $gdbserver_bin | run-as $package sh -c \
+                  \"tee gdbserver > /dev/null\""
+
+    # Upload the specified gdbserver binary to the device.
+    adb -s $device push "$gdbserver" "$gdbserver_bin"
+
+    if (adb -s $device shell ls /system/bin | grep -G tee); then
+       # Copy it to the user directory.
+       adb -s $device shell "$gdbserver_cat"
+       adb -s $device shell "run-as $package chmod 777 gdbserver"
+       gdbserver_cmd="./gdbserver"
+    else
+       # Hopefully this is an old version of Android which allows
+       # execution from /data/local/tmp.  Its `chmod' doesn't support
+       # `+x' either.
+       adb -s $device shell "chmod 777 $gdbserver_bin"
+       gdbserver_cmd="$gdbserver_bin"
+
+       # If the user is root, then there is no need to open any kind
+       # of TCP socket.
+       if (adb -s $device shell id | grep -G root); then
+           gdbserver=
+           is_root=yes
+       fi
+    fi
+fi
+
+# Now start gdbserver on the device asynchronously.
+
+echo "Attaching gdbserver to $pid on $device..."
+exec 5<> /tmp/file-descriptor-stamp
+rm -f /tmp/file-descriptor-stamp
+
+if [ -z "$gdbserver" ]; then
+    if [ "$is_root" = "yes" ]; then
+       adb -s $device shell $gdbserver_bin --multi \
+           "0.0.0.0:7564" --attach $pid >&5 &
+       gdb_socket="tcp:7564"
+    else
+       adb -s $device shell $gdbserver_bin --multi \
+           "0.0.0.0:7564" --attach $pid >&5 &
+       gdb_socket="tcp:7564"
+    fi
+else
+    # Normally the program cannot access $gdbserver_bin when it is
+    # placed in /data/local/tmp.
+    adb -s $device shell run-as $package $gdbserver_cmd --multi \
+       "+debug.$package.socket" --attach $pid >&5 &
+    gdb_socket="localfilesystem:$app_data_dir/debug.$package.socket"
+fi
+
+# In order to allow adb to forward to the gdbserver socket, make the
+# app data directory a+x.
+adb -s $device shell run-as $package chmod a+x $app_data_dir
+
+# Wait until gdbserver successfully runs.
+line=
+while read -u 5 line; do
+    case "$line" in
+       *Attached* )
+           break;
+           ;;
+       *error* | *Error* | failed )
+           echo "GDB error:" $line
+           exit 1
+           ;;
+       * )
+           ;;
+    esac
+done
+
+# Now that GDB is attached, tell the Java debugger to resume execution
+# and then exit.
+
+if [ -n "$jdb_command" ]; then
+    echo "resume" >&${JDB[1]}
+    echo "exit" >&${JDB[1]}
+fi
+
+# Forward the gdb server port here.
+adb -s $device forward "tcp:$gdb_port" $gdb_socket
+if [ ! $? ]; then
+    echo "Failed to forward $app_data_dir/debug.$package.socket"
+    echo "to $gdb_port!  Perhaps you need to specify a different port"
+    echo "with --port?"
+    exit 1;
+fi
+
+# Finally, start gdb with any extra arguments needed.
+cd "$oldpwd"
+$gdb --eval-command "target remote localhost:$gdb_port" $gdbargs
diff --git a/java/emacs.keystore b/java/emacs.keystore
new file mode 100644
index 00000000000..76d80b6db65
Binary files /dev/null and b/java/emacs.keystore differ
diff --git a/java/org/gnu/emacs/EmacsActivity.java 
b/java/org/gnu/emacs/EmacsActivity.java
new file mode 100644
index 00000000000..4ddf51fbb20
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsActivity.java
@@ -0,0 +1,481 @@
+/* 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 java.lang.IllegalStateException;
+import java.util.List;
+import java.util.ArrayList;
+
+import android.app.Activity;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+
+import android.os.Build;
+import android.os.Bundle;
+
+import android.util.Log;
+
+import android.net.Uri;
+
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.view.WindowInsetsController;
+
+import android.widget.FrameLayout;
+
+public class EmacsActivity extends Activity
+  implements EmacsWindowAttachmentManager.WindowConsumer,
+  ViewTreeObserver.OnGlobalLayoutListener
+{
+  public static final String TAG = "EmacsActivity";
+
+  /* ID for URIs from a granted document tree.  */
+  public static final int ACCEPT_DOCUMENT_TREE = 1;
+
+  /* The currently attached EmacsWindow, or null if none.  */
+  private EmacsWindow window;
+
+  /* The frame layout associated with the activity.  */
+  private FrameLayout layout;
+
+  /* List of activities with focus.  */
+  public static final List<EmacsActivity> focusedActivities;
+
+  /* The last activity to have been focused.  */
+  public static EmacsActivity lastFocusedActivity;
+
+  /* The currently focused window.  */
+  public static EmacsWindow focusedWindow;
+
+  /* Whether or not this activity is paused.  */
+  private boolean isPaused;
+
+  /* Whether or not this activity is fullscreen.  */
+  private boolean isFullscreen;
+
+  /* The last context menu to be closed.  */
+  private static Menu lastClosedMenu;
+
+  static
+  {
+    focusedActivities = new ArrayList<EmacsActivity> ();
+  };
+
+  public static void
+  invalidateFocus1 (EmacsWindow window)
+  {
+    if (window.view.isFocused ())
+      focusedWindow = window;
+
+    for (EmacsWindow child : window.children)
+      invalidateFocus1 (child);
+  }
+
+  public static void
+  invalidateFocus ()
+  {
+    EmacsWindow oldFocus;
+
+    /* Walk through each focused activity and assign the window focus
+       to the bottom-most focused window within.  Record the old focus
+       as well.  */
+    oldFocus = focusedWindow;
+    focusedWindow = null;
+
+    for (EmacsActivity activity : focusedActivities)
+      {
+       if (activity.window != null)
+         invalidateFocus1 (activity.window);
+      }
+
+    /* Send focus in- and out- events to the previous and current
+       focus.  */
+
+    if (oldFocus != null)
+      EmacsNative.sendFocusOut (oldFocus.handle,
+                               System.currentTimeMillis ());
+
+    if (focusedWindow != null)
+      EmacsNative.sendFocusIn (focusedWindow.handle,
+                              System.currentTimeMillis ());
+  }
+
+  @Override
+  public final void
+  detachWindow ()
+  {
+    syncFullscreenWith (null);
+
+    if (window == null)
+      Log.w (TAG, "detachWindow called, but there is no window");
+    else
+      {
+       /* Clear the window's pointer to this activity and remove the
+          window's view.  */
+       window.setConsumer (null);
+
+       /* The window can't be iconified any longer.  */
+       window.noticeDeiconified ();
+       layout.removeView (window.view);
+       window = null;
+
+       invalidateFocus ();
+      }
+  }
+
+  @Override
+  public final void
+  attachWindow (EmacsWindow child)
+  {
+    Log.d (TAG, "attachWindow: " + child);
+
+    if (window != null)
+      throw new IllegalStateException ("trying to attach window when one"
+                                      + " already exists");
+
+    syncFullscreenWith (child);
+
+    /* Record and attach the view.  */
+
+    window = child;
+    layout.addView (window.view);
+    child.setConsumer (this);
+
+    /* If the window isn't no-focus-on-map, focus its view.  */
+    if (!child.getDontFocusOnMap ())
+      window.view.requestFocus ();
+
+    /* If the activity is iconified, send that to the window.  */
+    if (isPaused)
+      window.noticeIconified ();
+
+    /* Invalidate the focus.  */
+    invalidateFocus ();
+  }
+
+  @Override
+  public final void
+  destroy ()
+  {
+    finish ();
+  }
+
+  @Override
+  public final EmacsWindow
+  getAttachedWindow ()
+  {
+    return window;
+  }
+
+  @Override
+  public final void
+  onCreate (Bundle savedInstanceState)
+  {
+    FrameLayout.LayoutParams params;
+    Intent intent;
+    View decorView;
+    ViewTreeObserver observer;
+    int matchParent;
+
+    /* See if Emacs should be started with any extra arguments, such
+       as `--quick'.  */
+    intent = getIntent ();
+    EmacsService.extraStartupArgument
+      = intent.getStringExtra ("org.gnu.emacs.STARTUP_ARGUMENT");
+
+    matchParent = FrameLayout.LayoutParams.MATCH_PARENT;
+    params
+      = new FrameLayout.LayoutParams (matchParent,
+                                     matchParent);
+
+    /* Make the frame layout.  */
+    layout = new FrameLayout (this);
+    layout.setLayoutParams (params);
+
+    /* Set it as the content view.  */
+    setContentView (layout);
+
+    /* Maybe start the Emacs service if necessary.  */
+    EmacsService.startEmacsService (this);
+
+    /* Add this activity to the list of available activities.  */
+    EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this);
+
+    /* Start observing global layout changes between Jelly Bean and Q.
+       This is required to restore the fullscreen state whenever the
+       on screen keyboard is displayed, as there is otherwise no way
+       to determine when the on screen keyboard becomes visible.  */
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
+       && Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
+      {
+       decorView = getWindow ().getDecorView ();
+       observer = decorView.getViewTreeObserver ();
+       observer.addOnGlobalLayoutListener (this);
+      }
+
+    super.onCreate (savedInstanceState);
+  }
+
+  @Override
+  public final void
+  onGlobalLayout ()
+  {
+    syncFullscreenWith (window);
+  }
+
+  @Override
+  public final void
+  onDestroy ()
+  {
+    EmacsWindowAttachmentManager manager;
+    boolean isMultitask;
+
+    manager = EmacsWindowAttachmentManager.MANAGER;
+
+    /* The activity will die shortly hereafter.  If there is a window
+       attached, close it now.  */
+    Log.d (TAG, "onDestroy " + this);
+    isMultitask = this instanceof EmacsMultitaskActivity;
+    manager.removeWindowConsumer (this, isMultitask || isFinishing ());
+    focusedActivities.remove (this);
+    invalidateFocus ();
+
+    /* Remove this activity from the static field, lest it leak.  */
+    if (lastFocusedActivity == this)
+      lastFocusedActivity = null;
+
+    super.onDestroy ();
+  }
+
+  @Override
+  public final void
+  onWindowFocusChanged (boolean isFocused)
+  {
+    Log.d (TAG, ("onWindowFocusChanged: "
+                + (isFocused ? "YES" : "NO")));
+
+    if (isFocused && !focusedActivities.contains (this))
+      {
+       focusedActivities.add (this);
+       lastFocusedActivity = this;
+
+       /* Update the window insets as the focus change may have
+          changed the window insets as well, and the system does not
+          automatically restore visibility flags.  */
+
+       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
+           && Build.VERSION.SDK_INT < Build.VERSION_CODES.R
+           && isFullscreen)
+         syncFullscreenWith (window);
+      }
+    else
+      focusedActivities.remove (this);
+
+    invalidateFocus ();
+  }
+
+  @Override
+  public final void
+  onPause ()
+  {
+    isPaused = true;
+
+    EmacsWindowAttachmentManager.MANAGER.noticeIconified (this);
+    super.onPause ();
+  }
+
+  @Override
+  public final void
+  onResume ()
+  {
+    isPaused = false;
+
+    EmacsWindowAttachmentManager.MANAGER.noticeDeiconified (this);
+    super.onResume ();
+  }
+
+  @Override
+  public final void
+  onContextMenuClosed (Menu menu)
+  {
+    int serial;
+
+    Log.d (TAG, "onContextMenuClosed: " + menu);
+
+    /* See the comment inside onMenuItemClick.  */
+
+    if (((EmacsContextMenu.wasSubmenuSelected == -2)
+        || (EmacsContextMenu.wasSubmenuSelected >= 0
+            && ((System.currentTimeMillis ()
+                 - EmacsContextMenu.wasSubmenuSelected)
+                <= 300)))
+       || menu == lastClosedMenu)
+      {
+       EmacsContextMenu.wasSubmenuSelected = -1;
+       lastClosedMenu = menu;
+       return;
+      }
+
+    /* lastClosedMenu is set because Android apparently calls this
+       function twice.  */
+
+    lastClosedMenu = null;
+
+    /* Send a context menu event given that no menu item has already
+       been selected.  */
+    if (!EmacsContextMenu.itemAlreadySelected)
+      {
+       serial = EmacsContextMenu.lastMenuEventSerial;
+       EmacsNative.sendContextMenu ((short) 0, 0,
+                                    serial);
+      }
+
+    super.onContextMenuClosed (menu);
+  }
+
+  @SuppressWarnings ("deprecation")
+  public final void
+  syncFullscreenWith (EmacsWindow emacsWindow)
+  {
+    WindowInsetsController controller;
+    Window window;
+    int behavior, flags;
+    View view;
+
+    if (emacsWindow != null)
+      isFullscreen = emacsWindow.fullscreen;
+    else
+      isFullscreen = false;
+
+    /* On Android 11 or later, use the window insets controller to
+       control whether or not the view is fullscreen.  */
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
+      {
+       window = getWindow ();
+
+       /* If there is no attached window, return immediately.  */
+       if (window == null)
+         return;
+
+       behavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+       controller = window.getInsetsController ();
+       controller.setSystemBarsBehavior (behavior);
+
+       if (isFullscreen)
+         controller.hide (WindowInsets.Type.statusBars ()
+                          | WindowInsets.Type.navigationBars ());
+       else
+         controller.show (WindowInsets.Type.statusBars ()
+                          | WindowInsets.Type.navigationBars ());
+
+       return;
+      }
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
+      {
+       /* On Android 4.1 or later, use `setSystemUiVisibility'.  */
+
+       window = getWindow ();
+
+       if (window == null)
+         return;
+
+       view = window.getDecorView ();
+
+       if (isFullscreen)
+         {
+           flags = 0;
+           flags |= View.SYSTEM_UI_FLAG_FULLSCREEN;
+
+           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
+             {
+               /* These flags means that Emacs will be full screen as
+                  long as the state flag is set.  */
+               flags |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+               flags |= View.SYSTEM_UI_FLAG_IMMERSIVE;
+               flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+             }
+
+           /* Apply the given flags.  */
+           view.setSystemUiVisibility (flags);
+         }
+       else
+         view.setSystemUiVisibility (View.SYSTEM_UI_FLAG_VISIBLE);
+      }
+  }
+
+  @Override
+  public final void
+  onAttachedToWindow ()
+  {
+    super.onAttachedToWindow ();
+
+    /* Update the window insets.  */
+    syncFullscreenWith (window);
+  }
+
+
+
+  @Override
+  public final void
+  onActivityResult (int requestCode, int resultCode, Intent data)
+  {
+    ContentResolver resolver;
+    Uri uri;
+    int flags;
+
+    switch (requestCode)
+      {
+      case ACCEPT_DOCUMENT_TREE:
+
+       /* A document granted through
+          EmacsService.requestDirectoryAccess.  */
+
+       if (resultCode == RESULT_OK)
+         {
+           resolver = getContentResolver ();
+           uri = data.getData ();
+           flags = (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+           try
+             {
+               if (uri != null)
+                 resolver.takePersistableUriPermission (uri, flags);
+             }
+           catch (Exception exception)
+             {
+               /* Permission to access URI might've been revoked in
+                  between selecting the file and this callback being
+                  invoked.  Don't crash in such cases.  */
+             }
+         }
+
+       break;
+      }
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsApplication.java 
b/java/org/gnu/emacs/EmacsApplication.java
new file mode 100644
index 00000000000..8afa5bcedb4
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsApplication.java
@@ -0,0 +1,92 @@
+/* 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 java.io.File;
+import java.io.FileFilter;
+
+import android.content.Context;
+
+import android.app.Application;
+import android.util.Log;
+
+public final class EmacsApplication extends Application
+{
+  private static final String TAG = "EmacsApplication";
+
+  /* The name of the dump file to use.  */
+  public static String dumpFileName;
+
+  public static void
+  findDumpFile (Context context)
+  {
+    File filesDirectory;
+    File[] allFiles;
+    String wantedDumpFile;
+    int i;
+
+    wantedDumpFile = ("emacs-" + EmacsNative.getFingerprint ()
+                     + ".pdmp");
+
+    /* Obtain a list of all files ending with ``.pdmp''.  Then, look
+       for a file named ``emacs-<fingerprint>.pdmp'' and delete the
+       rest.  */
+    filesDirectory = context.getFilesDir ();
+
+    allFiles = filesDirectory.listFiles (new FileFilter () {
+       @Override
+       public boolean
+       accept (File file)
+       {
+         return (!file.isDirectory ()
+                 && file.getName ().endsWith (".pdmp"));
+       }
+      });
+
+    if (allFiles == null)
+      return;
+
+    /* Now try to find the right dump file.  */
+    for (i = 0; i < allFiles.length; ++i)
+      {
+       if (allFiles[i].getName ().equals (wantedDumpFile))
+         dumpFileName = allFiles[i].getAbsolutePath ();
+       else
+         /* Delete this outdated dump file.  */
+         allFiles[i].delete ();
+      }
+  }
+
+  @Override
+  public void
+  onCreate ()
+  {
+    /* Block signals which don't interest the current thread and its
+       descendants created by the system.  The original signal mask
+       will be restored for the Emacs thread in `initEmacs'.  */
+    EmacsNative.setupSystemThread ();
+
+    /* Locate a suitable dump file.  */
+    findDumpFile (this);
+
+    /* Start the rest of the application.  */
+    super.onCreate ();
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsClipboard.java 
b/java/org/gnu/emacs/EmacsClipboard.java
new file mode 100644
index 00000000000..5cd48af6e3a
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsClipboard.java
@@ -0,0 +1,47 @@
+/* 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 ();
+
+  public abstract byte[][] getClipboardTargets ();
+  public abstract long[] getClipboardData (byte[] target);
+
+  /* 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/EmacsContextMenu.java 
b/java/org/gnu/emacs/EmacsContextMenu.java
new file mode 100644
index 00000000000..46eddeeda3d
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsContextMenu.java
@@ -0,0 +1,393 @@
+/* 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 java.util.List;
+import java.util.ArrayList;
+
+import android.content.Context;
+import android.content.Intent;
+
+import android.os.Build;
+
+import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.SubMenu;
+
+import android.util.Log;
+
+/* Context menu implementation.  This object is built from JNI and
+   describes a menu hiearchy.  Then, `inflate' can turn it into an
+   Android menu, which can be turned into a popup (or other kind of)
+   menu.  */
+
+public final class EmacsContextMenu
+{
+  private static final String TAG = "EmacsContextMenu";
+
+  /* Whether or not an item was selected.  */
+  public static boolean itemAlreadySelected;
+
+  /* Whether or not a submenu was selected.
+     Value is -1 if no; value is -2 if yes, and a context menu
+     close event will definitely be sent.  Any other value is
+     the timestamp when the submenu was selected.  */
+  public static long wasSubmenuSelected;
+
+  /* The serial ID of the last context menu to be displayed.  */
+  public static int lastMenuEventSerial;
+
+  /* The last group ID used for a menu item.  */
+  public int lastGroupId;
+
+  private static final class Item implements MenuItem.OnMenuItemClickListener
+  {
+    public int itemID;
+    public String itemName, tooltip;
+    public EmacsContextMenu subMenu;
+    public boolean isEnabled, isCheckable, isChecked;
+    public EmacsView inflatedView;
+    public boolean isRadio;
+
+    @Override
+    public boolean
+    onMenuItemClick (MenuItem item)
+    {
+      Log.d (TAG, "onMenuItemClick: " + itemName + " (" + itemID + ")");
+
+      if (subMenu != null)
+       {
+         /* Android 6.0 and earlier don't support nested submenus
+            properly, so display the submenu popup by hand.  */
+
+         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
+           {
+             Log.d (TAG, "onMenuItemClick: displaying submenu " + subMenu);
+
+             /* Still set wasSubmenuSelected -- if not set, the
+                dismissal of this context menu will result in a
+                context menu event being sent.  */
+             wasSubmenuSelected = -2;
+
+             /* Running a popup menu from inside a click handler
+                doesn't work, so make sure it is displayed
+                outside.  */
+
+             inflatedView.post (new Runnable () {
+                 @Override
+                 public void
+                 run ()
+                 {
+                   inflatedView.popupMenu (subMenu, 0, 0, true);
+                 }
+               });
+
+             return true;
+           }
+
+         /* After opening a submenu within a submenu, Android will
+            send onContextMenuClosed for a ContextMenuBuilder.  This
+            will normally confuse Emacs into thinking that the
+            context menu has been dismissed.  Wrong!
+
+            Setting this flag makes EmacsActivity to only handle
+            SubMenuBuilder being closed, which always means the menu
+            has actually been dismissed.
+
+            However, these extraneous events aren't sent on devices
+            where submenus display without dismissing their parents.
+            Thus, only ignore the close event if it happens within
+            300 milliseconds of the submenu being selected.  */
+         wasSubmenuSelected = System.currentTimeMillis ();
+         return false;
+       }
+
+      /* Send a context menu event.  */
+      EmacsNative.sendContextMenu ((short) 0, itemID,
+                                  lastMenuEventSerial);
+
+      /* Say that an item has already been selected.  */
+      itemAlreadySelected = true;
+      return true;
+    }
+  };
+
+  /* List of menu items contained in this menu.  */
+  public List<Item> menuItems;
+
+  /* The parent context menu, or NULL if none.  */
+  private EmacsContextMenu parent;
+
+  /* The title of this context menu, or NULL if none.  */
+  private String title;
+
+
+
+  /* Create a context menu with no items inside and the title TITLE,
+     which may be NULL.  */
+
+  public static EmacsContextMenu
+  createContextMenu (String title)
+  {
+    EmacsContextMenu menu;
+
+    menu = new EmacsContextMenu ();
+    menu.title = title;
+    menu.menuItems = new ArrayList<Item> ();
+
+    return menu;
+  }
+
+  /* Add a normal menu item to the context menu with the id ITEMID and
+     the name ITEMNAME.  Enable it if ISENABLED, else keep it
+     disabled.
+
+     If this is not a submenu and ISCHECKABLE is set, make the item
+     checkable.  Likewise, if ISCHECKED is set, make the item
+     checked.
+
+     If TOOLTIP is non-NULL, set the menu item tooltip to TOOLTIP.
+
+     If ISRADIO, then display the check mark as a radio button.  */
+
+  public void
+  addItem (int itemID, String itemName, boolean isEnabled,
+          boolean isCheckable, boolean isChecked,
+          String tooltip, boolean isRadio)
+  {
+    Item item;
+
+    item = new Item ();
+    item.itemID = itemID;
+    item.itemName = itemName;
+    item.isEnabled = isEnabled;
+    item.isCheckable = isCheckable;
+    item.isChecked = isChecked;
+    item.tooltip = tooltip;
+    item.isRadio = isRadio;
+
+    menuItems.add (item);
+  }
+
+  /* Create a disabled menu item with the name ITEMNAME.  */
+
+  public void
+  addPane (String itemName)
+  {
+    Item item;
+
+    item = new Item ();
+    item.itemName = itemName;
+
+    menuItems.add (item);
+  }
+
+  /* Add a submenu to the context menu with the specified title and
+     item name.  */
+
+  public EmacsContextMenu
+  addSubmenu (String itemName, String tooltip)
+  {
+    EmacsContextMenu submenu;
+    Item item;
+
+    item = new Item ();
+    item.itemID = 0;
+    item.itemName = itemName;
+    item.tooltip = tooltip;
+    item.subMenu = createContextMenu (itemName);
+    item.subMenu.parent = this;
+
+    menuItems.add (item);
+    return item.subMenu;
+  }
+
+  /* Add the contents of this menu to MENU.  Assume MENU will be
+     displayed in INFLATEDVIEW.  */
+
+  private void
+  inflateMenuItems (Menu menu, EmacsView inflatedView)
+  {
+    Intent intent;
+    MenuItem menuItem;
+    SubMenu submenu;
+
+    for (Item item : menuItems)
+      {
+       if (item.subMenu != null)
+         {
+           /* This is a submenu.  On versions of Android which
+              support doing so, create the submenu and add the
+              contents of the menu to it.
+
+              Note that Android 4.0 and later technically supports
+              having multiple layers of nested submenus, but if they
+              are used, onContextMenuClosed becomes unreliable.  */
+
+           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+             {
+               submenu = menu.addSubMenu (item.itemName);
+               item.subMenu.inflateMenuItems (submenu, inflatedView);
+
+               /* This is still needed to set wasSubmenuSelected.  */
+               menuItem = submenu.getItem ();
+             }
+           else
+             menuItem = menu.add (item.itemName);
+
+           item.inflatedView = inflatedView;
+           menuItem.setOnMenuItemClickListener (item);
+         }
+       else
+         {
+           if (item.isRadio)
+             menuItem = menu.add (++lastGroupId, Menu.NONE, Menu.NONE,
+                                  item.itemName);
+           else
+             menuItem = menu.add (item.itemName);
+           menuItem.setOnMenuItemClickListener (item);
+
+           /* If the item ID is zero, then disable the item.  */
+           if (item.itemID == 0 || !item.isEnabled)
+             menuItem.setEnabled (false);
+
+           /* Now make the menu item display a checkmark as
+              appropriate.  */
+
+           if (item.isCheckable)
+             menuItem.setCheckable (true);
+
+           if (item.isChecked)
+             menuItem.setChecked (true);
+
+           /* Define an exclusively checkable group if the item is a
+              radio button.  */
+
+           if (item.isRadio)
+             menu.setGroupCheckable (lastGroupId, true, true);
+
+           /* If the tooltip text is set and the system is new enough
+              to support menu item tooltips, set it on the item.  */
+
+           if (item.tooltip != null
+               && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+             menuItem.setTooltipText (item.tooltip);
+         }
+      }
+  }
+
+  /* Enter the items in this context menu to MENU.
+     Assume that MENU will be displayed in VIEW; this may lead to
+     popupMenu being called on VIEW if a submenu is selected.
+
+     If MENU is a ContextMenu, set its header title to the one
+     contained in this object.  */
+
+  public void
+  expandTo (Menu menu, EmacsView view)
+  {
+    inflateMenuItems (menu, view);
+
+    /* See if menu is a ContextMenu and a title is set.  */
+    if (title == null || !(menu instanceof ContextMenu))
+      return;
+
+    /* Set its title to this.title.  */
+    ((ContextMenu) menu).setHeaderTitle (title);
+  }
+
+  /* Return the parent or NULL.  */
+
+  public EmacsContextMenu
+  parent ()
+  {
+    return this.parent;
+  }
+
+  /* Like display, but does the actual work and runs in the main
+     thread.  */
+
+  private boolean
+  display1 (EmacsWindow window, int xPosition, int yPosition)
+  {
+    /* Set this flag to false.  It is used to decide whether or not to
+       send 0 in response to the context menu being closed.  */
+    itemAlreadySelected = false;
+
+    /* No submenu has been selected yet.  */
+    wasSubmenuSelected = -1;
+
+    return window.view.popupMenu (this, xPosition, yPosition,
+                                 false);
+  }
+
+  /* Display this context menu on WINDOW, at xPosition and yPosition.
+     SERIAL is a number that will be returned in any menu event
+     generated to identify this context menu.  */
+
+  public boolean
+  display (final EmacsWindow window, final int xPosition,
+          final int yPosition, final int serial)
+  {
+    Runnable runnable;
+    final EmacsHolder<Boolean> rc;
+
+    rc = new EmacsHolder<Boolean> ();
+    rc.thing = false;
+
+    runnable = new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         synchronized (this)
+           {
+             lastMenuEventSerial = serial;
+             rc.thing = display1 (window, xPosition, yPosition);
+             notify ();
+           }
+       }
+      };
+
+    EmacsService.syncRunnable (runnable);
+    return rc.thing;
+  }
+
+  /* Dismiss this context menu.  WINDOW is the window where the
+     context menu is being displayed.  */
+
+  public void
+  dismiss (final EmacsWindow window)
+  {
+    Runnable runnable;
+
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         window.view.cancelPopupMenu ();
+         itemAlreadySelected = false;
+       }
+      });
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsCursor.java 
b/java/org/gnu/emacs/EmacsCursor.java
new file mode 100644
index 00000000000..c14c6f2a11b
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsCursor.java
@@ -0,0 +1,47 @@
+/* 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.view.PointerIcon;
+import android.os.Build;
+
+/* Cursor wrapper.  Note that pointer icons are not supported prior to
+   Android 24.  */
+
+public final class EmacsCursor extends EmacsHandleObject
+{
+  /* The pointer icon associated with this cursor.  */
+  public final PointerIcon icon;
+
+  public
+  EmacsCursor (short handle, int glyph)
+  {
+    super (handle);
+
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
+      {
+       icon = null;
+       return;
+      }
+
+    icon = PointerIcon.getSystemIcon (EmacsService.SERVICE,
+                                     glyph);
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsDialog.java 
b/java/org/gnu/emacs/EmacsDialog.java
new file mode 100644
index 00000000000..e4ed2271741
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDialog.java
@@ -0,0 +1,427 @@
+/* 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 java.util.List;
+import java.util.ArrayList;
+
+import android.app.AlertDialog;
+
+import android.content.Context;
+import android.content.DialogInterface;
+
+import android.content.res.Resources.NotFoundException;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
+
+import android.os.Build;
+
+import android.provider.Settings;
+
+import android.util.Log;
+
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.FrameLayout;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+
+/* Toolkit dialog implementation.  This object is built from JNI and
+   describes a single alert dialog.  Then, `inflate' turns it into
+   AlertDialog.  */
+
+public final class EmacsDialog implements DialogInterface.OnDismissListener
+{
+  private static final String TAG = "EmacsDialog";
+
+  /* List of buttons in this dialog.  */
+  private List<EmacsButton> buttons;
+
+  /* Dialog title.  */
+  private String title;
+
+  /* Dialog text.  */
+  private String text;
+
+  /* Whether or not a selection has already been made.  */
+  private boolean wasButtonClicked;
+
+  /* Dialog to dismiss after click.  */
+  private AlertDialog dismissDialog;
+
+  /* The menu serial associated with this dialog box.  */
+  private int menuEventSerial;
+
+  private final class EmacsButton implements View.OnClickListener,
+                                 DialogInterface.OnClickListener
+  {
+    /* Name of this button.  */
+    public String name;
+
+    /* ID of this button.  */
+    public int id;
+
+    /* Whether or not the button is enabled.  */
+    public boolean enabled;
+
+    @Override
+    public void
+    onClick (View view)
+    {
+      Log.d (TAG, "onClicked " + this);
+
+      wasButtonClicked = true;
+      EmacsNative.sendContextMenu ((short) 0, id, menuEventSerial);
+      dismissDialog.dismiss ();
+    }
+
+    @Override
+    public void
+    onClick (DialogInterface dialog, int which)
+    {
+      Log.d (TAG, "onClicked " + this);
+
+      wasButtonClicked = true;
+      EmacsNative.sendContextMenu ((short) 0, id, menuEventSerial);
+    }
+  };
+
+  /* Create a popup dialog with the title TITLE and the text TEXT.
+     TITLE may be NULL.  MENUEVENTSERIAL is a number which will
+     identify this popup dialog inside events it sends.  */
+
+  public static EmacsDialog
+  createDialog (String title, String text, int menuEventSerial)
+  {
+    EmacsDialog dialog;
+
+    dialog = new EmacsDialog ();
+    dialog.buttons = new ArrayList<EmacsButton> ();
+    dialog.title = title;
+    dialog.text = text;
+    dialog.menuEventSerial = menuEventSerial;
+
+    return dialog;
+  }
+
+  /* Add a button named NAME, with the identifier ID.  If DISABLE,
+     disable the button.  */
+
+  public void
+  addButton (String name, int id, boolean disable)
+  {
+    EmacsButton button;
+
+    button = new EmacsButton ();
+    button.name = name;
+    button.id = id;
+    button.enabled = !disable;
+    buttons.add (button);
+  }
+
+  /* Turn this dialog into an AlertDialog for the specified
+     CONTEXT.
+
+     Upon a button being selected, the dialog will send an
+     ANDROID_CONTEXT_MENU event with the id of that button.
+
+     Upon the dialog being dismissed, an ANDROID_CONTEXT_MENU event
+     will be sent with an id of 0.  */
+
+  public AlertDialog
+  toAlertDialog (Context context)
+  {
+    AlertDialog dialog;
+    int size, styleId, flag;
+    int[] attrs;
+    EmacsButton button;
+    EmacsDialogButtonLayout layout;
+    Button buttonView;
+    ViewGroup.LayoutParams layoutParams;
+    Theme theme;
+    TypedArray attributes;
+    Window window;
+
+    size = buttons.size ();
+    styleId = -1;
+
+    if (size <= 3)
+      {
+       dialog = new AlertDialog.Builder (context).create ();
+       dialog.setMessage (text);
+       dialog.setCancelable (true);
+       dialog.setOnDismissListener (this);
+
+       if (title != null)
+         dialog.setTitle (title);
+
+       /* There are less than 4 buttons.  Add the buttons the way
+          Android intends them to be added.  */
+
+       if (size >= 1)
+         {
+           button = buttons.get (0);
+           dialog.setButton (DialogInterface.BUTTON_POSITIVE,
+                             button.name, button);
+         }
+
+       if (size >= 2)
+         {
+           button = buttons.get (1);
+           dialog.setButton (DialogInterface.BUTTON_NEGATIVE,
+                             button.name, button);
+         }
+
+       if (size >= 3)
+         {
+           button = buttons.get (2);
+           dialog.setButton (DialogInterface.BUTTON_NEUTRAL,
+                             button.name, button);
+         }
+      }
+    else
+      {
+       /* There are more than 3 buttons.  Add them all to a special
+          container widget that handles wrapping.  First, create the
+          layout.  */
+
+       layout = new EmacsDialogButtonLayout (context);
+       layoutParams
+         = new FrameLayout.LayoutParams (ViewGroup.LayoutParams.MATCH_PARENT,
+                                         ViewGroup.LayoutParams.WRAP_CONTENT);
+       layout.setLayoutParams (layoutParams);
+
+       /* Add that layout to the dialog's custom view.
+
+          android.R.id.custom is documented to work.  But looking it
+          up returns NULL, so setView must be used instead.  */
+
+       dialog = new AlertDialog.Builder (context).setView (layout).create ();
+       dialog.setMessage (text);
+       dialog.setCancelable (true);
+       dialog.setOnDismissListener (this);
+
+       if (title != null)
+         dialog.setTitle (title);
+
+       /* Now that the dialog has been created, set the style of each
+          custom button to match the usual dialog buttons found on
+          Android 5 and later.  */
+
+       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+         {
+           /* Obtain the Theme associated with the dialog.  */
+           theme = dialog.getContext ().getTheme ();
+
+           /* Resolve the dialog button style.  */
+           attrs
+             = new int [] { android.R.attr.buttonBarNeutralButtonStyle, };
+
+           try
+             {
+               attributes = theme.obtainStyledAttributes (attrs);
+
+               /* Look for the style ID.  Default to -1 if it could
+                  not be found.  */
+               styleId = attributes.getResourceId (0, -1);
+
+               /* Now clean up the TypedAttributes object.  */
+               attributes.recycle ();
+             }
+           catch (NotFoundException e)
+             {
+               /* Nothing to do here.  */
+             }
+         }
+
+       /* Create each button and add it to the layout.  Set the style
+          if necessary.  */
+
+       for (EmacsButton emacsButton : buttons)
+         {
+           if (styleId == -1)
+             /* No specific style... */
+             buttonView = new Button (context);
+           else
+             /* Use the given styleId.  */
+             buttonView = new Button (context, null, 0, styleId);
+
+           /* Set the text and on click handler.  */
+           buttonView.setText (emacsButton.name);
+           buttonView.setOnClickListener (emacsButton);
+           buttonView.setEnabled (emacsButton.enabled);
+           layout.addView (buttonView);
+         }
+      }
+
+    return dialog;
+  }
+
+  /* Internal helper for display run on the main thread.  */
+
+  @SuppressWarnings("deprecation")
+  private boolean
+  display1 ()
+  {
+    Context context;
+    int size, type;
+    Button buttonView;
+    EmacsButton button;
+    AlertDialog dialog;
+    Window window;
+
+    if (EmacsActivity.focusedActivities.isEmpty ())
+      {
+       /* If focusedActivities is empty then this dialog may have
+          been displayed immediately after another popup dialog was
+          dismissed.  Or Emacs might legitimately be in the
+          background, possibly displaying this popup in response to
+          an Emacsclient request.  Try the service context if it will
+          work, then any focused EmacsOpenActivity, and finally the
+          last EmacsActivity to be focused.  */
+
+       Log.d (TAG, "display1: no focused activities...");
+       Log.d (TAG, ("display1: EmacsOpenActivity.currentActivity: "
+                    + EmacsOpenActivity.currentActivity));
+
+       if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M
+           || Settings.canDrawOverlays (EmacsService.SERVICE))
+         context = EmacsService.SERVICE;
+       else if (EmacsOpenActivity.currentActivity != null)
+         context = EmacsOpenActivity.currentActivity;
+       else
+         context = EmacsActivity.lastFocusedActivity;
+
+       if (context == null)
+         return false;
+      }
+    else
+      /* Display using the activity context when Emacs is in the
+        foreground, as this allows the dialog to be dismissed more
+        consistently.  */
+      context = EmacsActivity.focusedActivities.get (0);
+
+    Log.d (TAG, "display1: using context " + context);
+
+    dialog = dismissDialog = toAlertDialog (context);
+
+    try
+      {
+       if (context == EmacsService.SERVICE)
+         {
+           /* Apply the system alert window type to make sure this
+              dialog can be displayed.  */
+
+           window = dialog.getWindow ();
+           type = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
+                   ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
+                   : WindowManager.LayoutParams.TYPE_PHONE);
+           window.setType (type);
+         }
+
+       dismissDialog.show ();
+      }
+    catch (Exception exception)
+      {
+       /* This can happen when the system decides Emacs is not in the
+          foreground any longer.  */
+       return false;
+      }
+
+    /* If there are less than four buttons, then they must be
+       individually enabled or disabled after the dialog is
+       displayed.  */
+    size = buttons.size ();
+
+    if (size <= 3)
+      {
+       if (size >= 1)
+         {
+           button = buttons.get (0);
+           buttonView
+             = dialog.getButton (DialogInterface.BUTTON_POSITIVE);
+           buttonView.setEnabled (button.enabled);
+         }
+
+       if (size >= 2)
+         {
+           button = buttons.get (1);
+           buttonView
+             = dialog.getButton (DialogInterface.BUTTON_NEGATIVE);
+           buttonView.setEnabled (button.enabled);
+         }
+
+       if (size >= 3)
+         {
+           button = buttons.get (2);
+           buttonView
+             = dialog.getButton (DialogInterface.BUTTON_NEUTRAL);
+           buttonView.setEnabled (button.enabled);
+         }
+      }
+
+    return true;
+  }
+
+  /* Display this dialog for a suitable activity.
+     Value is false if the dialog could not be displayed,
+     and true otherwise.  */
+
+  public boolean
+  display ()
+  {
+    Runnable runnable;
+    final EmacsHolder<Boolean> rc;
+
+    rc = new EmacsHolder<Boolean> ();
+    runnable = new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         synchronized (this)
+           {
+             rc.thing = display1 ();
+             notify ();
+           }
+       }
+      };
+
+    EmacsService.syncRunnable (runnable);
+    return rc.thing;
+  }
+
+
+
+  @Override
+  public void
+  onDismiss (DialogInterface dialog)
+  {
+    Log.d (TAG, "onDismiss: " + this);
+
+    if (wasButtonClicked)
+      return;
+
+    EmacsNative.sendContextMenu ((short) 0, 0, menuEventSerial);
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsDialogButtonLayout.java 
b/java/org/gnu/emacs/EmacsDialogButtonLayout.java
new file mode 100644
index 00000000000..fd8d63d81d3
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDialogButtonLayout.java
@@ -0,0 +1,152 @@
+/* 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.Context;
+
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+
+
+
+/* This ``view group'' implements a container widget for multiple
+   buttons of the type found in pop-up dialogs.  It is used when
+   displaying a dialog box that contains more than three buttons, as
+   the default dialog box widget is not capable of holding more than
+   that many.  */
+
+
+
+public final class EmacsDialogButtonLayout extends ViewGroup
+{
+  public
+  EmacsDialogButtonLayout (Context context)
+  {
+    super (context);
+  }
+
+  @Override
+  protected void
+  onMeasure (int widthMeasureSpec, int heightMeasureSpec)
+  {
+    int width, count, i, x, y, height, spec, tempSpec;
+    View view;
+
+    /* Obtain the width of this widget and create the measure
+       specification used to measure children.  */
+
+    width = MeasureSpec.getSize (widthMeasureSpec);
+    spec = MeasureSpec.makeMeasureSpec (0, MeasureSpec.UNSPECIFIED);
+    tempSpec
+      = MeasureSpec.makeMeasureSpec (width, MeasureSpec.AT_MOST);
+    x = y = height = 0;
+
+    /* Run through each widget.  */
+
+    count = getChildCount ();
+
+    for (i = 0; i < count; ++i)
+      {
+       view = getChildAt (i);
+
+       /* Measure this view.  */
+       view.measure (spec, spec);
+
+       if (width - x < view.getMeasuredWidth ())
+         {
+           /* Move onto the next line, unless this line is empty.  */
+
+           if (x != 0)
+             {
+               y += height;
+               height = x = 0;
+             }
+
+           if (view.getMeasuredWidth () > width)
+             /* Measure the view again, this time forcing it to be at
+                most width wide, if it is not already.  */
+             view.measure (tempSpec, spec);
+         }
+
+       height = Math.max (height, view.getMeasuredHeight ());
+       x += view.getMeasuredWidth ();
+      }
+
+    /* Now set the measured size of this widget.  */
+    setMeasuredDimension (width, y + height);
+  }
+
+  @Override
+  protected void
+  onLayout (boolean changed, int left, int top, int right,
+           int bottom)
+  {
+    int width, count, i, x, y, height, spec, tempSpec;
+    View view;
+
+    /* Obtain the width of this widget and create the measure
+       specification used to measure children.  */
+
+    width = getMeasuredWidth ();
+    spec = MeasureSpec.makeMeasureSpec (0, MeasureSpec.UNSPECIFIED);
+    tempSpec
+      = MeasureSpec.makeMeasureSpec (width, MeasureSpec.AT_MOST);
+    x = y = height = 0;
+
+    /* Run through each widget.  */
+
+    count = getChildCount ();
+
+    for (i = 0; i < count; ++i)
+      {
+       view = getChildAt (i);
+
+       /* Measure this view.  */
+       view.measure (spec, spec);
+
+       if (width - x < view.getMeasuredWidth ())
+         {
+           /* Move onto the next line, unless this line is empty.  */
+
+           if (x != 0)
+             {
+               y += height;
+               height = x = 0;
+             }
+
+           if (view.getMeasuredWidth () > width)
+             /* Measure the view again, this time forcing it to be at
+                most width wide, if it is not already.  */
+             view.measure (tempSpec, spec);
+         }
+
+       /* Now assign this view its position.  */
+       view.layout (x, y, x + view.getMeasuredWidth (),
+                    y + view.getMeasuredHeight ());
+
+       /* And move on to the next widget.  */
+       height = Math.max (height, view.getMeasuredHeight ());
+       x += view.getMeasuredWidth ();
+      }
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsDirectoryEntry.java 
b/java/org/gnu/emacs/EmacsDirectoryEntry.java
new file mode 100644
index 00000000000..75c52e48002
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDirectoryEntry.java
@@ -0,0 +1,33 @@
+/* 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;
+
+/* Structure holding a single ``directory entry'' from a document
+   provider.  */
+
+public final class EmacsDirectoryEntry
+{
+  /* The type of this directory entry.  0 means a regular file and 1
+     means a directory.  */
+  public int d_type;
+
+  /* The display name of the file represented.  */
+  public String d_name;
+};
diff --git a/java/org/gnu/emacs/EmacsDocumentsProvider.java 
b/java/org/gnu/emacs/EmacsDocumentsProvider.java
new file mode 100644
index 00000000000..96dc2bc6e14
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDocumentsProvider.java
@@ -0,0 +1,578 @@
+/* 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.Context;
+
+import android.database.Cursor;
+import android.database.MatrixCursor;
+
+import android.os.Build;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Root;
+import static android.provider.DocumentsContract.buildChildDocumentsUri;
+import android.provider.DocumentsProvider;
+
+import android.webkit.MimeTypeMap;
+
+import android.net.Uri;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/* ``Documents provider''.  This allows Emacs's home directory to be
+   modified by other programs holding permissions to manage system
+   storage, which is useful to (for example) correct misconfigurations
+   which prevent Emacs from starting up.
+
+   This functionality is only available on Android 19 and later.  */
+
+public final class EmacsDocumentsProvider extends DocumentsProvider
+{
+  /* Home directory.  This is the directory whose contents are
+     initially returned to requesting applications.  */
+  private File baseDir;
+
+  /* The default projection for requests for the root directory.  */
+  private static final String[] DEFAULT_ROOT_PROJECTION;
+
+  /* The default projection for requests for a file.  */
+  private static final String[] DEFAULT_DOCUMENT_PROJECTION;
+
+  static
+  {
+    DEFAULT_ROOT_PROJECTION = new String[] {
+      Root.COLUMN_ROOT_ID,
+      Root.COLUMN_MIME_TYPES,
+      Root.COLUMN_FLAGS,
+      Root.COLUMN_ICON,
+      Root.COLUMN_TITLE,
+      Root.COLUMN_SUMMARY,
+      Root.COLUMN_DOCUMENT_ID,
+      Root.COLUMN_AVAILABLE_BYTES,
+    };
+
+    DEFAULT_DOCUMENT_PROJECTION = new String[] {
+      Document.COLUMN_DOCUMENT_ID,
+      Document.COLUMN_MIME_TYPE,
+      Document.COLUMN_DISPLAY_NAME,
+      Document.COLUMN_LAST_MODIFIED,
+      Document.COLUMN_FLAGS,
+      Document.COLUMN_SIZE,
+    };
+  }
+
+  @Override
+  public boolean
+  onCreate ()
+  {
+    /* Set the base directory to Emacs's files directory.  */
+    baseDir = getContext ().getFilesDir ();
+    return true;
+  }
+
+  @Override
+  public Cursor
+  queryRoots (String[] projection)
+  {
+    MatrixCursor result;
+    MatrixCursor.RowBuilder row;
+
+    /* If the requestor asked for nothing at all, then it wants some
+       data by default.  */
+
+    if (projection == null)
+      projection = DEFAULT_ROOT_PROJECTION;
+
+    result = new MatrixCursor (projection);
+    row = result.newRow ();
+
+    /* Now create and add a row for each file in the base
+       directory.  */
+    row.add (Root.COLUMN_ROOT_ID, baseDir.getAbsolutePath ());
+    row.add (Root.COLUMN_SUMMARY, "Emacs home directory");
+
+    /* Add the appropriate flags.  */
+
+    row.add (Root.COLUMN_FLAGS, (Root.FLAG_SUPPORTS_CREATE
+                                | Root.FLAG_SUPPORTS_IS_CHILD));
+    row.add (Root.COLUMN_ICON, R.drawable.emacs);
+    row.add (Root.FLAG_LOCAL_ONLY);
+    row.add (Root.COLUMN_TITLE, "Emacs");
+    row.add (Root.COLUMN_DOCUMENT_ID, baseDir.getAbsolutePath ());
+
+    return result;
+  }
+
+  private Uri
+  getNotificationUri (File file)
+  {
+    Uri updatedUri;
+
+    updatedUri
+      = buildChildDocumentsUri ("org.gnu.emacs",
+                               file.getAbsolutePath ());
+
+    return updatedUri;
+  }
+
+  /* Inform the system that FILE's contents (or FILE itself) has
+     changed.  */
+
+  private void
+  notifyChange (File file)
+  {
+    Uri updatedUri;
+    Context context;
+
+    context = getContext ();
+    updatedUri
+      = buildChildDocumentsUri ("org.gnu.emacs",
+                               file.getAbsolutePath ());
+    context.getContentResolver ().notifyChange (updatedUri, null);
+  }
+
+  /* Inform the system that FILE's contents (or FILE itself) has
+     changed.  FILE is a string describing containing the file name of
+     a directory as opposed to a File.  */
+
+  private void
+  notifyChangeByName (String file)
+  {
+    Uri updatedUri;
+    Context context;
+
+    context = getContext ();
+    updatedUri
+      = buildChildDocumentsUri ("org.gnu.emacs", file);
+    context.getContentResolver ().notifyChange (updatedUri, null);
+  }
+
+  /* Return the MIME type of a file FILE.  */
+
+  private String
+  getMimeType (File file)
+  {
+    String name, extension, mime;
+    int extensionSeparator;
+    MimeTypeMap singleton;
+
+    if (file.isDirectory ())
+      return Document.MIME_TYPE_DIR;
+
+    /* Abuse WebView stuff to get the file's MIME type.  */
+    name = file.getName ();
+    extensionSeparator = name.lastIndexOf ('.');
+
+    if (extensionSeparator > 0)
+      {
+       singleton = MimeTypeMap.getSingleton ();
+       extension = name.substring (extensionSeparator + 1);
+       mime = singleton.getMimeTypeFromExtension (extension);
+
+       if (mime != null)
+         return mime;
+      }
+
+    return "application/octet-stream";
+  }
+
+  /* Append the specified FILE to the query result RESULT.
+     Handle both directories and ordinary files.  */
+
+  private void
+  queryDocument1 (MatrixCursor result, File file)
+  {
+    MatrixCursor.RowBuilder row;
+    String fileName, displayName, mimeType;
+    int flags;
+
+    row = result.newRow ();
+    flags = 0;
+
+    /* fileName is a string that the system will ask for some time in
+       the future.  Here, it is just the absolute name of the file.  */
+    fileName = file.getAbsolutePath ();
+
+    /* If file is a directory, add the right flags for that.  */
+
+    if (file.isDirectory ())
+      {
+       if (file.canWrite ())
+         {
+           flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
+           flags |= Document.FLAG_SUPPORTS_DELETE;
+
+           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+             flags |= Document.FLAG_SUPPORTS_RENAME;
+
+           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+             flags |= Document.FLAG_SUPPORTS_MOVE;
+         }
+      }
+    else if (file.canWrite ())
+      {
+       /* Apply the correct flags for a writable file.  */
+       flags |= Document.FLAG_SUPPORTS_WRITE;
+       flags |= Document.FLAG_SUPPORTS_DELETE;
+
+       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+         flags |= Document.FLAG_SUPPORTS_RENAME;
+
+       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+         {
+           flags |= Document.FLAG_SUPPORTS_REMOVE;
+           flags |= Document.FLAG_SUPPORTS_MOVE;
+         }
+      }
+
+    displayName = file.getName ();
+    mimeType = getMimeType (file);
+
+    row.add (Document.COLUMN_DOCUMENT_ID, fileName);
+    row.add (Document.COLUMN_DISPLAY_NAME, displayName);
+    row.add (Document.COLUMN_SIZE, file.length ());
+    row.add (Document.COLUMN_MIME_TYPE, mimeType);
+    row.add (Document.COLUMN_LAST_MODIFIED, file.lastModified ());
+    row.add (Document.COLUMN_FLAGS, flags);
+  }
+
+  @Override
+  public Cursor
+  queryDocument (String documentId, String[] projection)
+    throws FileNotFoundException
+  {
+    MatrixCursor result;
+    File file;
+    Context context;
+
+    file = new File (documentId);
+    context = getContext ();
+
+    if (projection == null)
+      projection = DEFAULT_DOCUMENT_PROJECTION;
+
+    result = new MatrixCursor (projection);
+    queryDocument1 (result, file);
+
+    /* Now allow interested applications to detect changes.  */
+    result.setNotificationUri (context.getContentResolver (),
+                              getNotificationUri (file));
+
+    return result;
+  }
+
+  @Override
+  public Cursor
+  queryChildDocuments (String parentDocumentId, String[] projection,
+                      String sortOrder) throws FileNotFoundException
+  {
+    MatrixCursor result;
+    File directory;
+    File[] files;
+    Context context;
+
+    if (projection == null)
+      projection = DEFAULT_DOCUMENT_PROJECTION;
+
+    result = new MatrixCursor (projection);
+
+    /* Try to open the file corresponding to the location being
+       requested.  */
+    directory = new File (parentDocumentId);
+
+    /* Look up each child.  */
+    files = directory.listFiles ();
+
+    if (files != null)
+      {
+       /* Now add each child.  */
+       for (File child : files)
+         queryDocument1 (result, child);
+      }
+
+    context = getContext ();
+
+    /* Now allow interested applications to detect changes.  */
+    result.setNotificationUri (context.getContentResolver (),
+                              getNotificationUri (directory));
+
+    return result;
+  }
+
+  @Override
+  public ParcelFileDescriptor
+  openDocument (String documentId, String mode,
+               CancellationSignal signal) throws FileNotFoundException
+  {
+    return ParcelFileDescriptor.open (new File (documentId),
+                                     ParcelFileDescriptor.parseMode (mode));
+  }
+
+  @Override
+  public String
+  createDocument (String documentId, String mimeType,
+                 String displayName) throws FileNotFoundException
+  {
+    File file, parentFile;
+    boolean rc;
+
+    file = new File (documentId, displayName);
+
+    try
+      {
+       rc = false;
+
+       if (Document.MIME_TYPE_DIR.equals (mimeType))
+         {
+           file.mkdirs ();
+
+           if (file.isDirectory ())
+             rc = true;
+         }
+       else
+         {
+           file.createNewFile ();
+
+           if (file.isFile ()
+               && file.setWritable (true)
+               && file.setReadable (true))
+             rc = true;
+         }
+
+       if (!rc)
+         throw new FileNotFoundException ("rc != 1");
+      }
+    catch (IOException e)
+      {
+       throw new FileNotFoundException (e.toString ());
+      }
+
+    parentFile = file.getParentFile ();
+
+    if (parentFile != null)
+      notifyChange (parentFile);
+
+    return file.getAbsolutePath ();
+  }
+
+  private void
+  deleteDocument1 (File child)
+  {
+    File[] children;
+
+    /* Don't delete symlinks recursively.
+
+       Calling readlink or stat is problematic due to file name
+       encoding problems, so try to delete the file first, and only
+       try to delete files recursively afterword.  */
+
+    if (child.delete ())
+      return;
+
+    children = child.listFiles ();
+
+    if (children != null)
+      {
+       for (File file : children)
+         deleteDocument1 (file);
+      }
+
+    child.delete ();
+  }
+
+  @Override
+  public void
+  deleteDocument (String documentId)
+    throws FileNotFoundException
+  {
+    File file, parent;
+    File[] children;
+
+    /* Java makes recursively deleting a file hard.  File name
+       encoding issues also prevent easily calling into C...  */
+
+    file = new File (documentId);
+    parent = file.getParentFile ();
+
+    if (parent == null)
+      throw new RuntimeException ("trying to delete file without"
+                                 + " parent!");
+
+    if (file.delete ())
+      {
+       /* Tell the system about the change.  */
+       notifyChange (parent);
+       return;
+      }
+
+    children = file.listFiles ();
+
+    if (children != null)
+      {
+       for (File child : children)
+         deleteDocument1 (child);
+      }
+
+    if (file.delete ())
+      /* Tell the system about the change.  */
+      notifyChange (parent);
+  }
+
+  @Override
+  public void
+  removeDocument (String documentId, String parentDocumentId)
+    throws FileNotFoundException
+  {
+    deleteDocument (documentId);
+  }
+
+  @Override
+  public String
+  getDocumentType (String documentId)
+  {
+    return getMimeType (new File (documentId));
+  }
+
+  @Override
+  public String
+  renameDocument (String documentId, String displayName)
+    throws FileNotFoundException
+  {
+    File file, newName;
+    File parent;
+
+    file = new File (documentId);
+    parent = file.getParentFile ();
+    newName = new File (parent, displayName);
+
+    if (parent == null)
+      throw new FileNotFoundException ("parent is null");
+
+    file = new File (documentId);
+
+    if (!file.renameTo (newName))
+      return null;
+
+    notifyChange (parent);
+    return newName.getAbsolutePath ();
+  }
+
+  @Override
+  public boolean
+  isChildDocument (String parentDocumentId, String documentId)
+  {
+    return documentId.startsWith (parentDocumentId);
+  }
+
+  @Override
+  public String
+  moveDocument (String sourceDocumentId,
+               String sourceParentDocumentId,
+               String targetParentDocumentId)
+    throws FileNotFoundException
+  {
+    File file, newName;
+    FileInputStream inputStream;
+    FileOutputStream outputStream;
+    byte buffer[];
+    int length;
+
+    file = new File (sourceDocumentId);
+
+    /* Now, create the file name of the parent document.  */
+    newName = new File (targetParentDocumentId,
+                       file.getName ());
+
+    /* Try to perform a simple rename, before falling back to
+       copying.  */
+
+    if (file.renameTo (newName))
+      {
+       notifyChangeByName (file.getParent ());
+       notifyChangeByName (targetParentDocumentId);
+       return newName.getAbsolutePath ();
+      }
+
+    /* If that doesn't work, create the new file and copy over the old
+       file's contents.  */
+
+    inputStream = null;
+    outputStream = null;
+
+    try
+      {
+       if (!newName.createNewFile ()
+           || !newName.setWritable (true)
+           || !newName.setReadable (true))
+         throw new FileNotFoundException ("failed to create new file");
+
+       /* Open the file in preparation for a copy.  */
+
+       inputStream = new FileInputStream (file);
+       outputStream = new FileOutputStream (newName);
+
+       /* Allocate the buffer used to hold data.  */
+
+       buffer = new byte[4096];
+
+       while ((length = inputStream.read (buffer)) > 0)
+         outputStream.write (buffer, 0, length);
+      }
+    catch (IOException e)
+      {
+       throw new FileNotFoundException ("IOException: " + e);
+      }
+    finally
+      {
+       try
+         {
+           if (inputStream != null)
+             inputStream.close ();
+         }
+       catch (IOException e)
+         {
+
+         }
+
+       try
+         {
+           if (outputStream != null)
+             outputStream.close ();
+         }
+       catch (IOException e)
+         {
+
+         }
+      }
+
+    file.delete ();
+    notifyChangeByName (file.getParent ());
+    notifyChangeByName (targetParentDocumentId);
+
+    return newName.getAbsolutePath ();
+  }
+}
diff --git a/java/org/gnu/emacs/EmacsDrawLine.java 
b/java/org/gnu/emacs/EmacsDrawLine.java
new file mode 100644
index 00000000000..d367ccff9c4
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDrawLine.java
@@ -0,0 +1,79 @@
+/* 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.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+public final class EmacsDrawLine
+{
+  public static void
+  perform (EmacsDrawable drawable, EmacsGC gc,
+          int x, int y, int x2, int y2)
+  {
+    Rect rect;
+    Canvas canvas;
+    Paint paint;
+    int x0, x1, y0, y1;
+
+    /* TODO implement stippling.  */
+    if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+      return;
+
+    /* Calculate the leftmost and rightmost points.  */
+
+    x0 = Math.min (x, x2 + 1);
+    x1 = Math.max (x, x2 + 1);
+    y0 = Math.min (y, y2 + 1);
+    y1 = Math.max (y, y2 + 1);
+
+    /* And the clip rectangle.  */
+
+    paint = gc.gcPaint;
+    rect = new Rect (x0, y0, x1, y1);
+    canvas = drawable.lockCanvas (gc);
+
+    if (canvas == null)
+      return;
+
+    paint.setStyle (Paint.Style.FILL);
+
+    /* Since drawLine has PostScript style behavior, adjust the
+       coordinates appropriately.
+
+       The left most pixel of a straight line is always partially
+       filled.  Patch it in manually.  */
+
+    if (gc.clip_mask == null)
+      {
+       canvas.drawLine ((float) x + 0.5f, (float) y + 0.5f,
+                        (float) x2 + 0.5f, (float) y2 + 0.5f,
+                        paint);
+
+       if (x2 > x)
+         canvas.drawRect (new Rect (x, y, x + 1, y + 1), paint);
+      }
+
+    /* DrawLine with clip mask not implemented; it is not used by
+       Emacs.  */
+    drawable.damageRect (rect);
+  }
+}
diff --git a/java/org/gnu/emacs/EmacsDrawPoint.java 
b/java/org/gnu/emacs/EmacsDrawPoint.java
new file mode 100644
index 00000000000..6a1cb744d60
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDrawPoint.java
@@ -0,0 +1,34 @@
+/* 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;
+
+public final class EmacsDrawPoint
+{
+  public static void
+  perform (EmacsDrawable drawable,
+          EmacsGC immutableGC, int x, int y)
+  {
+    /* Use EmacsFillRectangle instead of EmacsDrawRectangle, as the
+       latter actually draws a rectangle one pixel wider than
+       specified.  */
+    EmacsFillRectangle.perform (drawable, immutableGC,
+                               x, y, 1, 1);
+  }
+}
diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java 
b/java/org/gnu/emacs/EmacsDrawRectangle.java
new file mode 100644
index 00000000000..e1261b4a2d2
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDrawRectangle.java
@@ -0,0 +1,120 @@
+/* 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.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+
+import android.util.Log;
+
+public final class EmacsDrawRectangle
+{
+  public static void
+  perform (EmacsDrawable drawable, EmacsGC gc,
+          int x, int y, int width, int height)
+  {
+    Paint maskPaint, paint;
+    Canvas maskCanvas;
+    Bitmap maskBitmap;
+    Rect maskRect, dstRect;
+    Canvas canvas;
+    Bitmap clipBitmap;
+
+    /* TODO implement stippling.  */
+    if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+      return;
+
+    canvas = drawable.lockCanvas (gc);
+
+    if (canvas == null)
+      return;
+
+    paint = gc.gcPaint;
+    paint.setStyle (Paint.Style.STROKE);
+
+    if (gc.clip_mask == null)
+      /* Use canvas.drawRect with a RectF.  That seems to reliably
+        get PostScript behavior.  */
+      canvas.drawRect (new RectF (x + 0.5f, y + 0.5f,
+                                 x + width + 0.5f,
+                                 y + height + 0.5f),
+                      paint);
+    else
+      {
+       /* Drawing with a clip mask involves calculating the
+          intersection of the clip mask with the dst rect, and
+          extrapolating the corresponding part of the src rect.  */
+       clipBitmap = gc.clip_mask.bitmap;
+       dstRect = new Rect (x, y, x + width, y + height);
+       maskRect = new Rect (gc.clip_x_origin,
+                            gc.clip_y_origin,
+                            (gc.clip_x_origin
+                             + clipBitmap.getWidth ()),
+                            (gc.clip_y_origin
+                             + clipBitmap.getHeight ()));
+
+       if (!maskRect.setIntersect (dstRect, maskRect))
+         /* There is no intersection between the clip mask and the
+            dest rect.  */
+         return;
+
+       /* Finally, create a temporary bitmap that is the size of
+          maskRect.  */
+
+       maskBitmap
+         = Bitmap.createBitmap (maskRect.width (), maskRect.height (),
+                                Bitmap.Config.ARGB_8888);
+
+       /* Draw the mask onto the maskBitmap.  */
+       maskCanvas = new Canvas (maskBitmap);
+       maskRect.offset (-gc.clip_x_origin,
+                        -gc.clip_y_origin);
+       maskCanvas.drawBitmap (gc.clip_mask.bitmap,
+                              maskRect, new Rect (0, 0,
+                                                  maskRect.width (),
+                                                  maskRect.height ()),
+                              paint);
+       maskRect.offset (gc.clip_x_origin,
+                        gc.clip_y_origin);
+
+       /* Set the transfer mode to SRC_IN to preserve only the parts
+          of the source that overlap with the mask.  */
+       maskPaint = new Paint ();
+       maskPaint.setXfermode (EmacsGC.srcInAlu);
+       maskPaint.setStyle (Paint.Style.STROKE);
+
+       /* Draw the source.  */
+       maskCanvas.drawRect (maskRect, maskPaint);
+
+       /* Finally, draw the mask bitmap to the destination.  */
+       paint.setXfermode (null);
+       canvas.drawBitmap (maskBitmap, null, maskRect, paint);
+
+       /* Recycle this unused bitmap.  */
+       maskBitmap.recycle ();
+      }
+
+    drawable.damageRect (new Rect (x, y, x + width + 1,
+                                  y + height + 1));
+  }
+}
diff --git a/java/org/gnu/emacs/EmacsDrawable.java 
b/java/org/gnu/emacs/EmacsDrawable.java
new file mode 100644
index 00000000000..f2f8885e976
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDrawable.java
@@ -0,0 +1,32 @@
+/* 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.graphics.Rect;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+
+public interface EmacsDrawable
+{
+  public Canvas lockCanvas (EmacsGC gc);
+  public void damageRect (Rect damageRect);
+  public Bitmap getBitmap ();
+  public boolean isDestroyed ();
+};
diff --git a/java/org/gnu/emacs/EmacsFillPolygon.java 
b/java/org/gnu/emacs/EmacsFillPolygon.java
new file mode 100644
index 00000000000..4ae3882cab4
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsFillPolygon.java
@@ -0,0 +1,80 @@
+/* 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.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.RectF;
+
+public final class EmacsFillPolygon
+{
+  public static void
+  perform (EmacsDrawable drawable, EmacsGC gc, Point points[])
+  {
+    Canvas canvas;
+    Path path;
+    Paint paint;
+    Rect rect;
+    RectF rectF;
+    int i;
+
+    canvas = drawable.lockCanvas (gc);
+
+    if (canvas == null)
+      return;
+
+    paint = gc.gcPaint;
+
+    /* Build the path from the given array of points.  */
+    path = new Path ();
+
+    if (points.length >= 1)
+      {
+       path.moveTo (points[0].x, points[0].y);
+
+       for (i = 1; i < points.length; ++i)
+         path.lineTo (points[i].x, points[i].y);
+
+       path.close ();
+      }
+
+    /* Compute the damage rectangle.  */
+    rectF = new RectF (0, 0, 0, 0);
+    path.computeBounds (rectF, true);
+
+    rect = new Rect ((int) Math.floor (rectF.left),
+                    (int) Math.floor (rectF.top),
+                    (int) Math.ceil (rectF.right),
+                    (int) Math.ceil (rectF.bottom));
+
+    paint.setStyle (Paint.Style.FILL);
+
+    if (gc.clip_mask == null)
+      canvas.drawPath (path, paint);
+
+    drawable.damageRect (rect);
+
+    /* FillPolygon with clip mask not implemented; it is not used by
+       Emacs.  */
+  }
+}
diff --git a/java/org/gnu/emacs/EmacsFillRectangle.java 
b/java/org/gnu/emacs/EmacsFillRectangle.java
new file mode 100644
index 00000000000..461fd3c639c
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsFillRectangle.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;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+import android.util.Log;
+
+public final class EmacsFillRectangle
+{
+  public static void
+  perform (EmacsDrawable drawable, EmacsGC gc,
+          int x, int y, int width, int height)
+  {
+    Paint maskPaint, paint;
+    Canvas maskCanvas;
+    Bitmap maskBitmap;
+    Rect rect;
+    Rect maskRect, dstRect;
+    Canvas canvas;
+    Bitmap clipBitmap;
+
+    /* TODO implement stippling.  */
+    if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+      return;
+
+    canvas = drawable.lockCanvas (gc);
+
+    if (canvas == null)
+      return;
+
+    paint = gc.gcPaint;
+    rect = new Rect (x, y, x + width, y + height);
+
+    paint.setStyle (Paint.Style.FILL);
+
+    if (gc.clip_mask == null)
+      canvas.drawRect (rect, paint);
+    else
+      {
+       /* Drawing with a clip mask involves calculating the
+          intersection of the clip mask with the dst rect, and
+          extrapolating the corresponding part of the src rect.  */
+
+       clipBitmap = gc.clip_mask.bitmap;
+       dstRect = new Rect (x, y, x + width, y + height);
+       maskRect = new Rect (gc.clip_x_origin,
+                            gc.clip_y_origin,
+                            (gc.clip_x_origin
+                             + clipBitmap.getWidth ()),
+                            (gc.clip_y_origin
+                             + clipBitmap.getHeight ()));
+
+       if (!maskRect.setIntersect (dstRect, maskRect))
+         /* There is no intersection between the clip mask and the
+            dest rect.  */
+         return;
+
+       /* Finally, create a temporary bitmap that is the size of
+          maskRect.  */
+
+       maskBitmap
+         = Bitmap.createBitmap (maskRect.width (), maskRect.height (),
+                                Bitmap.Config.ARGB_8888);
+
+       /* Draw the mask onto the maskBitmap.  */
+       maskCanvas = new Canvas (maskBitmap);
+       maskRect.offset (-gc.clip_x_origin,
+                        -gc.clip_y_origin);
+       maskCanvas.drawBitmap (gc.clip_mask.bitmap,
+                              maskRect, new Rect (0, 0,
+                                                  maskRect.width (),
+                                                  maskRect.height ()),
+                              paint);
+       maskRect.offset (gc.clip_x_origin,
+                        gc.clip_y_origin);
+
+       /* Set the transfer mode to SRC_IN to preserve only the parts
+          of the source that overlap with the mask.  */
+       maskPaint = new Paint ();
+       maskPaint.setXfermode (EmacsGC.srcInAlu);
+
+       /* Draw the source.  */
+       maskCanvas.drawRect (maskRect, maskPaint);
+
+       /* Finally, draw the mask bitmap to the destination.  */
+       paint.setXfermode (null);
+       canvas.drawBitmap (maskBitmap, null, maskRect, paint);
+
+       /* Recycle this unused bitmap.  */
+       maskBitmap.recycle ();
+      }
+
+    drawable.damageRect (rect);
+  }
+}
diff --git a/java/org/gnu/emacs/EmacsFontDriver.java 
b/java/org/gnu/emacs/EmacsFontDriver.java
new file mode 100644
index 00000000000..ff52899a897
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsFontDriver.java
@@ -0,0 +1,173 @@
+/* Font backend 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 code is mostly unused.  See sfntfont-android.c for the code
+   that is actually used.  */
+
+public abstract class EmacsFontDriver
+{
+  /* Font weights.  */
+  public static final int THIN         = 0;
+  public static final int ULTRA_LIGHT  = 40;
+  public static final int LIGHT                = 50;
+  public static final int SEMI_LIGHT   = 55;
+  public static final int REGULAR      = 80;
+  public static final int MEDIUM       = 100;
+  public static final int SEMI_BOLD    = 180;
+  public static final int BOLD         = 200;
+  public static final int EXTRA_BOLD   = 205;
+  public static final int BLACK                = 210;
+  public static final int ULTRA_HEAVY  = 250;
+
+  /* Font slants.  */
+  public static final int REVERSE_OBLIQUE      = 0;
+  public static final int REVERSE_ITALIC       = 10;
+  public static final int NORMAL               = 100;
+  public static final int ITALIC               = 200;
+  public static final int OBLIQUE              = 210;
+
+  /* Font widths.  */
+  public static final int ULTRA_CONDENSED      = 50;
+  public static final int EXTRA_CONDENSED      = 63;
+  public static final int CONDENSED            = 75;
+  public static final int SEMI_CONDENSED       = 87;
+  public static final int UNSPECIFIED          = 100;
+  public static final int SEMI_EXPANDED                = 113;
+  public static final int EXPANDED             = 125;
+  public static final int EXTRA_EXPANDED       = 150;
+  public static final int ULTRA_EXPANDED       = 200;
+
+  /* Font spacings.  */
+  public static final int PROPORTIONAL = 0;
+  public static final int DUAL         = 90;
+  public static final int MONO         = 100;
+  public static final int CHARCELL     = 110;
+
+  public static class FontSpec
+  {
+    /* The fields below mean the same as they do in enum
+       font_property_index in font.h.  */
+
+    public String foundry;
+    public String family;
+    public String adstyle;
+    public String registry;
+    public Integer width;
+    public Integer weight;
+    public Integer slant;
+    public Integer size;
+    public Integer spacing;
+    public Integer avgwidth;
+    public Integer dpi;
+
+    @Override
+    public String
+    toString ()
+    {
+      return ("foundry: " + foundry
+             + " family: " + family
+             + " adstyle: " + adstyle
+             + " registry: " + registry
+             + " width: " + width
+             + " weight: " + weight
+             + " slant: " + slant
+             + " spacing: " + spacing
+             + " avgwidth: " + avgwidth
+             + " dpi: " + dpi);
+    }
+  };
+
+  public static class FontMetrics
+  {
+    public short lbearing;
+    public short rbearing;
+    public short width;
+    public short ascent;
+    public short descent;
+
+    @Override
+    public String
+    toString ()
+    {
+      return ("lbearing " + lbearing
+             + " rbearing " + rbearing
+             + " width " + width
+             + " ascent " + ascent
+             + " descent " + descent);
+    }
+  }
+
+  public static class FontEntity extends FontSpec
+  {
+    /* No extra fields here.  */
+  };
+
+  public abstract class FontObject extends FontSpec
+  {
+    public int minWidth;
+    public int maxWidth;
+    public int pixelSize;
+    public int height;
+    public int spaceWidth;
+    public int averageWidth;
+    public int ascent;
+    public int descent;
+    public int underlineThickness;
+    public int underlinePosition;
+    public int baselineOffset;
+    public int relativeCompose;
+    public int defaultAscent;
+    public int encodingCharset;
+    public int repertoryCharset;
+
+    public
+    FontObject ()
+    {
+      encodingCharset = -1;
+      repertoryCharset = -1;
+    }
+  };
+
+  /* These mean the same as they do in struct font_driver.  */
+  public abstract FontEntity[] list (FontSpec fontSpec);
+  public abstract FontEntity match (FontSpec fontSpec);
+  public abstract String[] listFamilies ();
+  public abstract FontObject openFont (FontEntity fontEntity, int pixelSize);
+  public abstract int hasChar (FontSpec font, char charCode);
+  public abstract void textExtents (FontObject font, int code[],
+                                   FontMetrics fontMetrics);
+  public abstract int encodeChar (FontObject fontObject, char charCode);
+  public abstract int draw (FontObject fontObject, EmacsGC gc,
+                           EmacsDrawable drawable, int[] chars,
+                           int x, int y, int backgroundWidth,
+                           boolean withBackground);
+
+  public static EmacsFontDriver
+  createFontDriver ()
+  {
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
+      return new EmacsSdk23FontDriver ();
+
+    return new EmacsSdk7FontDriver ();
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsGC.java b/java/org/gnu/emacs/EmacsGC.java
new file mode 100644
index 00000000000..a7467cb9bd0
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsGC.java
@@ -0,0 +1,121 @@
+/* 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.graphics.Rect;
+import android.graphics.Paint;
+
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Xfermode;
+
+/* X like graphics context structures.  Keep the enums in synch with
+   androidgui.h! */
+
+public final class EmacsGC extends EmacsHandleObject
+{
+  public static final int GC_COPY = 0;
+  public static final int GC_XOR  = 1;
+
+  public static final int GC_FILL_SOLID                        = 0;
+  public static final int GC_FILL_OPAQUE_STIPPLED      = 1;
+
+  public static final Xfermode xorAlu, srcInAlu;
+
+  public int function, fill_style;
+  public int foreground, background;
+  public int clip_x_origin, clip_y_origin;
+  public int ts_origin_x, ts_origin_y;
+  public Rect clip_rects[], real_clip_rects[];
+  public EmacsPixmap clip_mask, stipple;
+  public Paint gcPaint;
+
+  /* ID incremented every time the clipping rectangles of any GC
+     changes.  */
+  private static long clip_serial;
+
+  /* The value of clipRectID after the last time this GCs clip
+     rectangles changed.  0 if there are no clip rectangles.  */
+  public long clipRectID;
+
+  static
+  {
+    xorAlu = new PorterDuffXfermode (Mode.XOR);
+    srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
+  }
+
+  /* The following fields are only set on immutable GCs.  */
+
+  public
+  EmacsGC (short handle)
+  {
+    /* For historical reasons the C code has an extra layer of
+       indirection above this GC handle.  struct android_gc is the GC
+       used by Emacs code, while android_gcontext is the type of the
+       handle.  */
+    super (handle);
+
+    fill_style = GC_FILL_SOLID;
+    function = GC_COPY;
+    foreground = 0;
+    background = 0xffffff;
+    gcPaint = new Paint ();
+  }
+
+  /* Mark this GC as dirty.  Apply parameters to the paint and
+     recompute real_clip_rects.  */
+
+  public void
+  markDirty (boolean clipRectsChanged)
+  {
+    int i;
+
+    if (clipRectsChanged)
+      {
+       if ((ts_origin_x != 0 || ts_origin_y != 0)
+           && clip_rects != null)
+         {
+           real_clip_rects = new Rect[clip_rects.length];
+
+           for (i = 0; i < clip_rects.length; ++i)
+             {
+               real_clip_rects[i] = new Rect (clip_rects[i]);
+               real_clip_rects[i].offset (ts_origin_x, ts_origin_y);
+             }
+         }
+       else
+         real_clip_rects = clip_rects;
+
+       clipRectID = ++clip_serial;
+      }
+
+    gcPaint.setStrokeWidth (1f);
+    gcPaint.setColor (foreground | 0xff000000);
+    gcPaint.setXfermode (function == GC_XOR
+                        ? xorAlu : srcInAlu);
+  }
+
+  public void
+  resetXfermode ()
+  {
+    gcPaint.setXfermode (function == GC_XOR
+                        ? xorAlu : srcInAlu);
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsHandleObject.java 
b/java/org/gnu/emacs/EmacsHandleObject.java
new file mode 100644
index 00000000000..5b889895337
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsHandleObject.java
@@ -0,0 +1,59 @@
+/* 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 java.lang.IllegalStateException;
+
+/* This defines something that is a so-called ``handle''.  Handles
+   must be created by C code, and will remain existing until
+   destroyHandle is called.  C code then refers to the handle by a
+   number which maps into the Java object representing the handle.
+
+   All handle operations must be done from the Emacs thread.  */
+
+public abstract class EmacsHandleObject
+{
+  /* Whether or not this handle has been destroyed.  */
+  volatile boolean destroyed;
+
+  /* The handle associated with this object.  */
+  public short handle;
+
+  public
+  EmacsHandleObject (short handle)
+  {
+    this.handle = handle;
+  }
+
+  public void
+  destroyHandle () throws IllegalStateException
+  {
+    synchronized (this)
+      {
+       destroyed = true;
+      }
+  }
+
+  public boolean
+  isDestroyed ()
+  {
+    return destroyed;
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsHolder.java 
b/java/org/gnu/emacs/EmacsHolder.java
new file mode 100644
index 00000000000..6cd48ba57ce
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsHolder.java
@@ -0,0 +1,30 @@
+/* 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;
+
+
+
+/* This class serves as a simple reference to an object of type T.
+   Nothing could be found inside the standard library.  */
+
+public final class EmacsHolder<T>
+{
+  T thing;
+};
diff --git a/java/org/gnu/emacs/EmacsInputConnection.java 
b/java/org/gnu/emacs/EmacsInputConnection.java
new file mode 100644
index 00000000000..c3764a7b29f
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsInputConnection.java
@@ -0,0 +1,698 @@
+/* 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;
+import android.os.Bundle;
+import android.os.Handler;
+
+import android.view.KeyEvent;
+
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.SurroundingText;
+import android.view.inputmethod.TextAttribute;
+import android.view.inputmethod.TextSnapshot;
+
+import android.util.Log;
+
+/* Android input methods, take number six.  See textconv.c for more
+   details; this is more-or-less a thin wrapper around that file.  */
+
+public final class EmacsInputConnection implements InputConnection
+{
+  private static final String TAG = "EmacsInputConnection";
+
+  /* View associated with this input connection.  */
+  private EmacsView view;
+
+  /* The handle ID associated with that view's window.  */
+  private short windowHandle;
+
+  /* Number of batch edits currently underway.  Used to avoid
+     synchronizing with the Emacs thread after each
+     `endBatchEdit'.  */
+  private int batchEditCount;
+
+  /* Whether or not to synchronize and call `updateIC' with the
+     selection position after committing text.
+
+     This helps with on screen keyboard programs found in some vendor
+     versions of Android, which rely on immediate updates to the point
+     position after text is commited in order to place the cursor
+     within that text.  */
+
+  private static boolean syncAfterCommit;
+
+  /* Whether or not to return empty text with the offset set to zero
+     if a request arrives that has no flags set and has requested no
+     characters at all.
+
+     This is necessary with on screen keyboard programs found in some
+     vendor versions of Android which don't rely on the documented
+     meaning of `ExtractedText.startOffset', and instead take the
+     selection offset inside at face value.  */
+
+  private static boolean extractAbsoluteOffsets;
+
+  static
+  {
+    if (Build.MANUFACTURER.equalsIgnoreCase ("Huawei")
+       || Build.MANUFACTURER.equalsIgnoreCase ("Honor"))
+      extractAbsoluteOffsets = syncAfterCommit = true;
+
+    /* The Samsung and Vivo keyboards take `selectionStart' at face
+       value if some text is returned, and also searches for words
+       solely within that text.  However, when no text is returned, it
+       falls back to getTextAfterCursor and getTextBeforeCursor.  */
+    if (Build.MANUFACTURER.equalsIgnoreCase ("Samsung")
+       || Build.MANUFACTURER.equalsIgnoreCase ("Vivo"))
+      extractAbsoluteOffsets = true;
+  };
+
+
+  public
+  EmacsInputConnection (EmacsView view)
+  {
+    this.view = view;
+    this.windowHandle = view.window.handle;
+  }
+
+
+  /* The functions below are called by input methods whenever they
+     need to perform an edit.  */
+
+  @Override
+  public boolean
+  beginBatchEdit ()
+  {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "beginBatchEdit");
+
+    EmacsNative.beginBatchEdit (windowHandle);
+
+    /* Keep a record of the number of outstanding batch edits here as
+       well.  */
+    batchEditCount++;
+    return true;
+  }
+
+  @Override
+  public boolean
+  endBatchEdit ()
+  {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "endBatchEdit");
+
+    EmacsNative.endBatchEdit (windowHandle);
+
+    /* Subtract one from the UI thread record of the number of batch
+       edits currently under way.  */
+
+    if (batchEditCount > 0)
+      batchEditCount -= 1;
+
+    return batchEditCount > 0;
+  }
+
+  public boolean
+  commitCompletion (CompletionInfo info)
+  {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "commitCompletion: " + info);
+
+    EmacsNative.commitCompletion (windowHandle,
+                                 info.getText ().toString (),
+                                 info.getPosition ());
+    return true;
+  }
+
+  @Override
+  public boolean
+  commitCorrection (CorrectionInfo info)
+  {
+    /* The input method calls this function not to commit text, but to
+       indicate that a subsequent edit will consist of a correction.
+       Emacs has no use for this information.
+
+       Of course this completely contradicts the provided
+       documentation, but this is how Android actually behaves.  */
+    return false;
+  }
+
+  @Override
+  public boolean
+  commitText (CharSequence text, int newCursorPosition)
+  {
+    int[] selection;
+
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "commitText: " + text + " " + newCursorPosition);
+
+    EmacsNative.commitText (windowHandle, text.toString (),
+                           newCursorPosition);
+
+    if (syncAfterCommit)
+      {
+       /* Synchronize with the Emacs thread, obtain the new
+          selection, and report it immediately.  */
+
+       selection = EmacsNative.getSelection (windowHandle);
+
+       if (EmacsService.DEBUG_IC && selection != null)
+         Log.d (TAG, "commitText: new selection is " + selection[0]
+                + ", by " + selection[1]);
+
+       if (selection != null)
+         /* N.B. that the composing region is removed after text is
+            committed.  */
+         view.imManager.updateSelection (view, selection[0],
+                                         selection[1], -1, -1);
+      }
+
+    return true;
+  }
+
+  @Override
+  public boolean
+  commitText (CharSequence text, int newCursorPosition,
+             TextAttribute textAttribute)
+  {
+    return commitText (text, newCursorPosition);
+  }
+
+  @Override
+  public boolean
+  deleteSurroundingText (int leftLength, int rightLength)
+  {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, ("deleteSurroundingText: "
+                  + leftLength + " " + rightLength));
+
+    EmacsNative.deleteSurroundingText (windowHandle, leftLength,
+                                      rightLength);
+    return true;
+  }
+
+  @Override
+  public boolean
+  deleteSurroundingTextInCodePoints (int leftLength, int rightLength)
+  {
+    /* Emacs returns characters which cannot be represented in a Java
+       `char' as NULL characters, so code points always reflect
+       characters themselves.  */
+    return deleteSurroundingText (leftLength, rightLength);
+  }
+
+  @Override
+  public boolean
+  finishComposingText ()
+  {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "finishComposingText");
+
+    EmacsNative.finishComposingText (windowHandle);
+    return true;
+  }
+
+  @Override
+  public String
+  getSelectedText (int flags)
+  {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return null;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "getSelectedText: " + flags);
+
+    return EmacsNative.getSelectedText (windowHandle, flags);
+  }
+
+  @Override
+  public String
+  getTextAfterCursor (int length, int flags)
+  {
+    String string;
+
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return null;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "getTextAfterCursor: " + length + " " + flags);
+
+    string = EmacsNative.getTextAfterCursor (windowHandle, length,
+                                            flags);
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "   --> " + string);
+
+    return string;
+  }
+
+  @Override
+  public String
+  getTextBeforeCursor (int length, int flags)
+  {
+    String string;
+
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return null;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "getTextBeforeCursor: " + length + " " + flags);
+
+    string = EmacsNative.getTextBeforeCursor (windowHandle, length,
+                                             flags);
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "   --> " + string);
+
+    return string;
+  }
+
+  @Override
+  public boolean
+  setComposingText (CharSequence text, int newCursorPosition)
+  {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, ("setComposingText: "
+                  + text + " ## " + newCursorPosition));
+
+    EmacsNative.setComposingText (windowHandle, text.toString (),
+                                 newCursorPosition);
+    return true;
+  }
+
+  @Override
+  public boolean
+  setComposingText (CharSequence text, int newCursorPosition,
+                   TextAttribute textAttribute)
+  {
+    return setComposingText (text, newCursorPosition);
+  }
+
+  @Override
+  public boolean
+  setComposingRegion (int start, int end)
+  {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "setComposingRegion: " + start + " " + end);
+
+    EmacsNative.setComposingRegion (windowHandle, start, end);
+    return true;
+  }
+
+  @Override
+  public boolean
+  setComposingRegion (int start, int end, TextAttribute textAttribute)
+  {
+    return setComposingRegion (start, end);
+  }
+
+  @Override
+  public boolean
+  performEditorAction (int editorAction)
+  {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "performEditorAction: " + editorAction);
+
+    EmacsNative.performEditorAction (windowHandle, editorAction);
+    return true;
+  }
+
+  @Override
+  public boolean
+  performContextMenuAction (int contextMenuAction)
+  {
+    int action;
+
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "performContextMenuAction: " + contextMenuAction);
+
+    /* Translate the action in Java code.  That way, a great deal of
+       JNI boilerplate can be avoided.  */
+
+    switch (contextMenuAction)
+      {
+      case android.R.id.selectAll:
+       action = 0;
+       break;
+
+      case android.R.id.startSelectingText:
+       action = 1;
+       break;
+
+      case android.R.id.stopSelectingText:
+       action = 2;
+       break;
+
+      case android.R.id.cut:
+       action = 3;
+       break;
+
+      case android.R.id.copy:
+       action = 4;
+       break;
+
+      case android.R.id.paste:
+       action = 5;
+       break;
+
+      default:
+       return true;
+      }
+
+    EmacsNative.performContextMenuAction (windowHandle, action);
+    return true;
+  }
+
+  @Override
+  public ExtractedText
+  getExtractedText (ExtractedTextRequest request, int flags)
+  {
+    ExtractedText text;
+    int[] selection;
+
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return null;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "getExtractedText: " + request.hintMaxChars + ", "
+            + request.hintMaxLines + " " + flags);
+
+    /* If a request arrives with hintMaxChars, hintMaxLines and flags
+       set to 0, and the system is known to be buggy, return an empty
+       extracted text object with the absolute selection positions.  */
+
+    if (extractAbsoluteOffsets
+       && request.hintMaxChars == 0
+       && request.hintMaxLines == 0
+       && flags == 0)
+      {
+       /* Obtain the selection.  */
+       selection = EmacsNative.getSelection (windowHandle);
+       if (selection == null)
+         return null;
+
+       /* Create the workaround extracted text.  */
+       text = new ExtractedText ();
+       text.partialStartOffset = -1;
+       text.partialEndOffset = -1;
+       text.text = "";
+       text.selectionStart = selection[0];
+       text.selectionEnd = selection[1];
+      }
+    else
+      text = EmacsNative.getExtractedText (windowHandle, request,
+                                          flags);
+
+    if (text == null)
+      {
+       if (EmacsService.DEBUG_IC)
+         Log.d (TAG, "getExtractedText: text is NULL");
+
+       return null;
+      }
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "getExtractedText: " + text.text + " @"
+            + text.startOffset + ":" + text.selectionStart
+            + ", " + text.selectionEnd);
+
+    return text;
+  }
+
+  @Override
+  public boolean
+  setSelection (int start, int end)
+  {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "setSelection: " + start + " " + end);
+
+    EmacsNative.setSelection (windowHandle, start, end);
+    return true;
+  }
+
+  @Override
+  /* ACTION_MULTIPLE is apparently obsolete.  */
+  @SuppressWarnings ("deprecation")
+  public boolean
+  sendKeyEvent (KeyEvent key)
+  {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "sendKeyEvent: " + key);
+
+    /* Use the standard API if possible.  */
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+      view.imManager.dispatchKeyEventFromInputMethod (view, key);
+    else
+      {
+       /* Fall back to dispatching the event manually if not.  */
+
+       switch (key.getAction ())
+         {
+         case KeyEvent.ACTION_DOWN:
+           view.onKeyDown (key.getKeyCode (), key);
+           break;
+
+         case KeyEvent.ACTION_UP:
+           view.onKeyUp (key.getKeyCode (), key);
+           break;
+
+         case KeyEvent.ACTION_MULTIPLE:
+           view.onKeyMultiple (key.getKeyCode (),
+                               key.getRepeatCount (),
+                               key);
+           break;
+         }
+      }
+
+    return true;
+  }
+
+  @Override
+  public boolean
+  requestCursorUpdates (int cursorUpdateMode)
+  {
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return false;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, "requestCursorUpdates: " + cursorUpdateMode);
+
+    EmacsNative.requestCursorUpdates (windowHandle, cursorUpdateMode);
+    return true;
+  }
+
+  @Override
+  public boolean
+  requestCursorUpdates (int cursorUpdateMode, int filter)
+  {
+    if (filter != 0)
+      return false;
+
+    return requestCursorUpdates (cursorUpdateMode);
+  }
+
+  @Override
+  public SurroundingText
+  getSurroundingText (int beforeLength, int afterLength,
+                     int flags)
+  {
+    SurroundingText text;
+
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return null;
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, ("getSurroundingText: " + beforeLength + ", "
+                  + afterLength));
+
+    text = EmacsNative.getSurroundingText (windowHandle, beforeLength,
+                                          afterLength, flags);
+
+    if (EmacsService.DEBUG_IC && text != null)
+      Log.d (TAG, ("getSurroundingText: "
+                  + text.getSelectionStart ()
+                  + ","
+                  + text.getSelectionEnd ()
+                  + "+"
+                  + text.getOffset ()
+                  + ": "
+                  + text.getText ()));
+
+    return text;
+  }
+
+  @Override
+  public TextSnapshot
+  takeSnapshot ()
+  {
+    TextSnapshot snapshot;
+
+    /* Return if the input connection is out of date.  */
+    if (view.icSerial < view.icGeneration)
+      return null;
+
+    snapshot = EmacsNative.takeSnapshot (windowHandle);
+
+    if (EmacsService.DEBUG_IC)
+      Log.d (TAG, ("takeSnapshot: "
+                  + snapshot.getSurroundingText ().getText ()
+                  + " @ " + snapshot.getCompositionEnd ()
+                  + ", " + snapshot.getCompositionStart ()));
+
+    return snapshot;
+  }
+
+  @Override
+  public void
+  closeConnection ()
+  {
+    batchEditCount = 0;
+  }
+
+
+
+  public void
+  reset ()
+  {
+    batchEditCount = 0;
+  }
+
+
+  /* Override functions which are not implemented.  */
+
+  @Override
+  public Handler
+  getHandler ()
+  {
+    return null;
+  }
+
+  @Override
+  public boolean
+  commitContent (InputContentInfo inputContentInfo, int flags,
+                Bundle opts)
+  {
+    return false;
+  }
+
+  @Override
+  public boolean
+  setImeConsumesInput (boolean imeConsumesInput)
+  {
+    return false;
+  }
+
+  @Override
+  public boolean
+  clearMetaKeyStates (int states)
+  {
+    return false;
+  }
+
+  @Override
+  public boolean
+  reportFullscreenMode (boolean enabled)
+  {
+    return false;
+  }
+
+  @Override
+  public boolean
+  performSpellCheck ()
+  {
+    return false;
+  }
+
+  @Override
+  public boolean
+  performPrivateCommand (String action, Bundle data)
+  {
+    return false;
+  }
+
+  @Override
+  public int
+  getCursorCapsMode (int reqModes)
+  {
+    return 0;
+  }
+}
diff --git a/java/org/gnu/emacs/EmacsLauncherPreferencesActivity.java 
b/java/org/gnu/emacs/EmacsLauncherPreferencesActivity.java
new file mode 100644
index 00000000000..1e1e5d97631
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsLauncherPreferencesActivity.java
@@ -0,0 +1,31 @@
+/* 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;
+
+/* This class only exists because EmacsPreferencesActivity is already
+   defined as an activity, the system wants a new class in order to
+   define a new activity, and only activities can be enabled or
+   disabled per the API level of the host.  */
+
+public final class EmacsLauncherPreferencesActivity
+  extends EmacsPreferencesActivity
+{
+
+}
diff --git a/java/org/gnu/emacs/EmacsMultitaskActivity.java 
b/java/org/gnu/emacs/EmacsMultitaskActivity.java
new file mode 100644
index 00000000000..b1c48f03fba
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsMultitaskActivity.java
@@ -0,0 +1,29 @@
+/* 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;
+
+/* This class only exists because EmacsActivity is already defined as
+   an activity, and the system wants a new class in order to define a
+   new activity.  */
+
+public final class EmacsMultitaskActivity extends EmacsActivity
+{
+
+}
diff --git a/java/org/gnu/emacs/EmacsNative.java 
b/java/org/gnu/emacs/EmacsNative.java
new file mode 100644
index 00000000000..fae0ba98f86
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -0,0 +1,316 @@
+/* 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.res.AssetManager;
+
+import android.graphics.Bitmap;
+
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.SurroundingText;
+import android.view.inputmethod.TextSnapshot;
+
+public final class EmacsNative
+{
+  /* List of native libraries that must be loaded during class
+     initialization.  */
+  private static final String[] libraryDeps;
+
+
+  /* Like `dup' in C.  */
+  public static native int dup (int fd);
+
+  /* Obtain the fingerprint of this build of Emacs.  The fingerprint
+     can be used to determine the dump file name.  */
+  public static native String getFingerprint ();
+
+  /* Set certain parameters before initializing Emacs.
+
+     assetManager must be the asset manager associated with the
+     context that is loading Emacs.  It is saved and remains for the
+     remainder the lifetime of the Emacs process.
+
+     filesDir must be the package's data storage location for the
+     current Android user.
+
+     libDir must be the package's data storage location for native
+     libraries.  It is used as PATH.
+
+     cacheDir must be the package's cache directory.  It is used as
+     the `temporary-file-directory'.
+
+     pixelDensityX and pixelDensityY are the DPI values that will be
+     used by Emacs.
+
+     scaledDensity is the DPI value used to translate point sizes to
+     pixel sizes when loading fonts.
+
+     classPath must be the classpath of this app_process process, or
+     NULL.
+
+     emacsService must be the EmacsService singleton, or NULL.
+
+     apiLevel is the version of Android being run.  */
+  public static native void setEmacsParams (AssetManager assetManager,
+                                           String filesDir,
+                                           String libDir,
+                                           String cacheDir,
+                                           float pixelDensityX,
+                                           float pixelDensityY,
+                                           float scaledDensity,
+                                           String classPath,
+                                           EmacsService emacsService,
+                                           int apiLevel);
+
+  /* Initialize Emacs with the argument array ARGV.  Each argument
+     must contain a NULL terminated string, or else the behavior is
+     undefined.
+
+     DUMPFILE is the dump file to use, or NULL if Emacs is to load
+     loadup.el itself.  */
+  public static native void initEmacs (String argv[], String dumpFile);
+
+  /* Abort and generate a native core dump.  */
+  public static native void emacsAbort ();
+
+  /* Set Vquit_flag to t, resulting in Emacs quitting as soon as
+     possible.  */
+  public static native void quit ();
+
+  /* Send an ANDROID_CONFIGURE_NOTIFY event.  The values of all the
+     functions below are the serials of the events sent.  */
+  public static native long sendConfigureNotify (short window, long time,
+                                                int x, int y, int width,
+                                                int height);
+
+  /* Send an ANDROID_KEY_PRESS event.  */
+  public static native long sendKeyPress (short window, long time, int state,
+                                         int keyCode, int unicodeChar);
+
+  /* Send an ANDROID_KEY_RELEASE event.  */
+  public static native long sendKeyRelease (short window, long time, int state,
+                                           int keyCode, int unicodeChar);
+
+  /* Send an ANDROID_FOCUS_IN event.  */
+  public static native long sendFocusIn (short window, long time);
+
+  /* Send an ANDROID_FOCUS_OUT event.  */
+  public static native long sendFocusOut (short window, long time);
+
+  /* Send an ANDROID_WINDOW_ACTION event.  */
+  public static native long sendWindowAction (short window, int action);
+
+  /* Send an ANDROID_ENTER_NOTIFY event.  */
+  public static native long sendEnterNotify (short window, int x, int y,
+                                            long time);
+
+  /* Send an ANDROID_LEAVE_NOTIFY event.  */
+  public static native long sendLeaveNotify (short window, int x, int y,
+                                            long time);
+
+  /* Send an ANDROID_MOTION_NOTIFY event.  */
+  public static native long sendMotionNotify (short window, int x, int y,
+                                             long time);
+
+  /* Send an ANDROID_BUTTON_PRESS event.  */
+  public static native long sendButtonPress (short window, int x, int y,
+                                            long time, int state,
+                                            int button);
+
+  /* Send an ANDROID_BUTTON_RELEASE event.  */
+  public static native long sendButtonRelease (short window, int x, int y,
+                                              long time, int state,
+                                              int button);
+
+  /* Send an ANDROID_TOUCH_DOWN event.  */
+  public static native long sendTouchDown (short window, int x, int y,
+                                          long time, int pointerID,
+                                          int flags);
+
+  /* Send an ANDROID_TOUCH_UP event.  */
+  public static native long sendTouchUp (short window, int x, int y,
+                                        long time, int pointerID,
+                                        int flags);
+
+  /* Send an ANDROID_TOUCH_MOVE event.  */
+  public static native long sendTouchMove (short window, int x, int y,
+                                          long time, int pointerID,
+                                          int flags);
+
+  /* Send an ANDROID_WHEEL event.  */
+  public static native long sendWheel (short window, int x, int y,
+                                      long time, int state,
+                                      float xDelta, float yDelta);
+
+  /* Send an ANDROID_ICONIFIED event.  */
+  public static native long sendIconified (short window);
+
+  /* Send an ANDROID_DEICONIFIED event.  */
+  public static native long sendDeiconified (short window);
+
+  /* Send an ANDROID_CONTEXT_MENU event.  */
+  public static native long sendContextMenu (short window, int menuEventID,
+                                            int menuEventSerial);
+
+  /* Send an ANDROID_EXPOSE event.  */
+  public static native long sendExpose (short window, int x, int y,
+                                       int width, int height);
+
+  /* Return the file name associated with the specified file
+     descriptor, or NULL if there is none.  */
+  public static native byte[] getProcName (int fd);
+
+  /* Notice that the Emacs thread will now start waiting for the main
+     thread's looper to respond.  */
+  public static native void beginSynchronous ();
+
+  /* Notice that the Emacs thread will has finished waiting for the
+     main thread's looper to respond.  */
+  public static native void endSynchronous ();
+
+  /* Prevent deadlocks while reliably allowing queries from the Emacs
+     thread to the main thread to complete by waiting for a query to
+     start from the main thread, then answer it; assume that a query
+     is certain to start shortly.  */
+  public static native void answerQuerySpin ();
+
+  /* Return whether or not KEYCODE_VOLUME_DOWN, KEYCODE_VOLUME_UP and
+     KEYCODE_VOLUME_MUTE should be forwarded to Emacs.  */
+  public static native boolean shouldForwardMultimediaButtons ();
+
+  /* Initialize the current thread, by blocking signals that do not
+     interest it.  */
+  public static native void setupSystemThread ();
+
+
+
+  /* Input connection functions.  These mostly correspond to their
+     counterparts in Android's InputConnection.  */
+
+  public static native void beginBatchEdit (short window);
+  public static native void endBatchEdit (short window);
+  public static native void commitCompletion (short window, String text,
+                                             int position);
+  public static native void commitText (short window, String text,
+                                       int position);
+  public static native void deleteSurroundingText (short window,
+                                                  int leftLength,
+                                                  int rightLength);
+  public static native void finishComposingText (short window);
+  public static native String getSelectedText (short window, int flags);
+  public static native String getTextAfterCursor (short window, int length,
+                                                 int flags);
+  public static native String getTextBeforeCursor (short window, int length,
+                                                  int flags);
+  public static native void setComposingText (short window, String text,
+                                             int newCursorPosition);
+  public static native void setComposingRegion (short window, int start,
+                                               int end);
+  public static native void setSelection (short window, int start, int end);
+  public static native void performEditorAction (short window,
+                                                int editorAction);
+  public static native void performContextMenuAction (short window,
+                                                     int contextMenuAction);
+  public static native ExtractedText getExtractedText (short window,
+                                                      ExtractedTextRequest req,
+                                                      int flags);
+  public static native void requestSelectionUpdate (short window);
+  public static native void requestCursorUpdates (short window, int mode);
+  public static native void clearInputFlags (short window);
+  public static native SurroundingText getSurroundingText (short window,
+                                                          int left, int right,
+                                                          int flags);
+  public static native TextSnapshot takeSnapshot (short window);
+
+
+  /* Return the current value of the selection, or -1 upon
+     failure.  */
+  public static native int[] getSelection (short window);
+
+
+  /* Graphics functions used as a replacement for potentially buggy
+     Android APIs.  */
+
+  public static native void blitRect (Bitmap src, Bitmap dest, int x1,
+                                     int y1, int x2, int y2);
+
+  /* Increment the generation ID of the specified BITMAP, forcing its
+     texture to be re-uploaded to the GPU.  */
+
+  public static native void notifyPixelsChanged (Bitmap bitmap);
+
+
+  /* Functions used to synchronize document provider access with the
+     main thread.  */
+
+  /* Wait for a call to `safPostRequest' while also reading async
+     input.
+
+     If asynchronous input arrives and sets Vquit_flag, return 1.  */
+  public static native int safSyncAndReadInput ();
+
+  /* Wait for a call to `safPostRequest'.  */
+  public static native void safSync ();
+
+  /* Post the semaphore used to await the completion of SAF
+     operations.  */
+  public static native void safPostRequest ();
+
+  /* Detect and return FD is writable.  FD may be truncated to 0 bytes
+     in the process.  */
+  public static native boolean ftruncate (int fd);
+
+  static
+  {
+    /* Older versions of Android cannot link correctly with shared
+       libraries that link with other shared libraries built along
+       Emacs unless all requisite shared libraries are explicitly
+       loaded from Java.
+
+       Every time you add a new shared library dependency to Emacs,
+       please add it here as well.  */
+
+    libraryDeps = new String[] { "png_emacs", "selinux_emacs",
+                                "crypto_emacs", "pcre_emacs",
+                                "packagelistparser_emacs",
+                                "gnutls_emacs", "gmp_emacs",
+                                "nettle_emacs", "p11-kit_emacs",
+                                "tasn1_emacs", "hogweed_emacs",
+                                "jansson_emacs", "jpeg_emacs",
+                                "tiff_emacs", "xml2_emacs",
+                                "icuuc_emacs",
+                                "tree-sitter_emacs", };
+
+    for (String dependency : libraryDeps)
+      {
+       try
+         {
+           System.loadLibrary (dependency);
+         }
+       catch (UnsatisfiedLinkError exception)
+         {
+           /* Ignore this exception.  */
+         }
+      }
+
+    System.loadLibrary ("emacs");
+  };
+};
diff --git a/java/org/gnu/emacs/EmacsNoninteractive.java 
b/java/org/gnu/emacs/EmacsNoninteractive.java
new file mode 100644
index 00000000000..1c7513e1cc9
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsNoninteractive.java
@@ -0,0 +1,203 @@
+/* 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.Looper;
+import android.os.Build;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/* Noninteractive Emacs.
+
+   This is the class that libandroid-emacs.so starts.
+   libandroid-emacs.so figures out the system classpath, then starts
+   dalvikvm with the framework jars.
+
+   At that point, dalvikvm calls main, which sets up the main looper,
+   creates an ActivityThread and attaches it to the main thread.
+
+   Then, it obtains an application context for the LoadedApk in the
+   application thread.
+
+   Finally, it obtains the necessary context specific objects and
+   initializes Emacs.  */
+
+@SuppressWarnings ("unchecked")
+public final class EmacsNoninteractive
+{
+  public static void
+  main (String[] args)
+  {
+    Object activityThread, loadedApk;
+    Class activityThreadClass, loadedApkClass, contextImplClass;
+    Class compatibilityInfoClass;
+    Method method;
+    Context context;
+    AssetManager assets;
+    String filesDir, libDir, cacheDir;
+
+    Looper.prepare ();
+    context = null;
+    assets = null;
+    filesDir = libDir = cacheDir = null;
+
+    try
+      {
+       /* Get the activity thread.  */
+       activityThreadClass = Class.forName ("android.app.ActivityThread");
+
+       /* Get the systemMain method.  */
+       method = activityThreadClass.getMethod ("systemMain");
+
+       /* Create and attach the activity thread.  */
+       activityThread = method.invoke (null);
+       context = null;
+
+       /* Now get an LoadedApk.  */
+
+       try
+         {
+           loadedApkClass = Class.forName ("android.app.LoadedApk");
+         }
+       catch (ClassNotFoundException exception)
+         {
+           /* Android 2.2 has no LoadedApk class, but fortunately it
+              does not need to be used, since contexts can be
+              directly created.  */
+
+           loadedApkClass = null;
+           contextImplClass = Class.forName ("android.app.ContextImpl");
+
+           method = activityThreadClass.getDeclaredMethod ("getSystemContext");
+           context = (Context) method.invoke (activityThread);
+           method = contextImplClass.getDeclaredMethod ("createPackageContext",
+                                                        String.class,
+                                                        int.class);
+           method.setAccessible (true);
+           context = (Context) method.invoke (context, "org.gnu.emacs",
+                                              0);
+         }
+
+       /* If the context has not already been created, then do what
+          is appropriate for newer versions of Android.  */
+
+       if (context == null)
+         {
+           /* Get a LoadedApk.  How to do this varies by Android version.
+              On Android 2.3.3 and earlier, there is no
+              ``compatibilityInfo'' argument to getPackageInfo.  */
+
+           if (Build.VERSION.SDK_INT
+               <= Build.VERSION_CODES.GINGERBREAD_MR1)
+             {
+               method
+                 = activityThreadClass.getMethod ("getPackageInfo",
+                                                  String.class,
+                                                  int.class);
+               loadedApk = method.invoke (activityThread, "org.gnu.emacs",
+                                          0);
+             }
+           else
+             {
+               compatibilityInfoClass
+                 = Class.forName ("android.content.res.CompatibilityInfo");
+
+               method
+                 = activityThreadClass.getMethod ("getPackageInfo",
+                                                  String.class,
+                                                  compatibilityInfoClass,
+                                                  int.class);
+               loadedApk = method.invoke (activityThread, "org.gnu.emacs",
+                                          null, 0);
+             }
+
+           if (loadedApk == null)
+             throw new RuntimeException ("getPackageInfo returned NULL");
+
+           /* Now, get a context.  */
+           contextImplClass = Class.forName ("android.app.ContextImpl");
+
+           try
+             {
+               method
+                 = contextImplClass.getDeclaredMethod ("createAppContext",
+                                                       activityThreadClass,
+                                                       loadedApkClass);
+               method.setAccessible (true);
+               context = (Context) method.invoke (null, activityThread,
+                                                  loadedApk);
+             }
+           catch (NoSuchMethodException exception)
+             {
+               /* Older Android versions don't have createAppContext, but
+                  instead require creating a ContextImpl, and then
+                  calling createPackageContext.  */
+               method
+                 = activityThreadClass.getDeclaredMethod ("getSystemContext");
+               context = (Context) method.invoke (activityThread);
+               method
+                 = contextImplClass.getDeclaredMethod ("createPackageContext",
+                                                       String.class,
+                                                       int.class);
+               method.setAccessible (true);
+               context = (Context) method.invoke (context, "org.gnu.emacs",
+                                                  0);
+             }
+         }
+
+       /* Don't actually start the looper or anything.  Instead, obtain
+          an AssetManager.  */
+       assets = context.getAssets ();
+
+       /* Now configure Emacs.  The class path should already be set.  */
+
+       filesDir = context.getFilesDir ().getCanonicalPath ();
+       libDir = EmacsService.getLibraryDirectory (context);
+       cacheDir = context.getCacheDir ().getCanonicalPath ();
+      }
+    catch (Exception e)
+      {
+       System.err.println ("Internal error: " + e);
+       System.err.println ("This means that the Android platform changed,");
+       System.err.println ("and that Emacs needs adjustments in order to");
+       System.err.println ("obtain required system internal resources.");
+       System.err.println ("Please report this bug to bug-gnu-emacs@gnu.org.");
+       e.printStackTrace ();
+
+       System.exit (1);
+      }
+
+    EmacsNative.setEmacsParams (assets, filesDir,
+                               libDir, cacheDir, 0.0f,
+                               0.0f, 0.0f, null, null,
+                               Build.VERSION.SDK_INT);
+
+    /* Now find the dump file that Emacs should use, if it has already
+       been dumped.  */
+    EmacsApplication.findDumpFile (context);
+
+    /* Start Emacs.  */
+    EmacsNative.initEmacs (args, EmacsApplication.dumpFileName);
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java 
b/java/org/gnu/emacs/EmacsOpenActivity.java
new file mode 100644
index 00000000000..3832cd2faab
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsOpenActivity.java
@@ -0,0 +1,552 @@
+/* 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;
+
+/* This class makes the Emacs server work reasonably on Android.
+
+   There is no way to make the Unix socket publicly available on
+   Android.
+
+   Instead, this activity tries to connect to the Emacs server, to
+   make it open files the system asks Emacs to open, and to emulate
+   some reasonable behavior when Emacs has not yet started.
+
+   First, Emacs registers itself as an application that can open text
+   and image files.
+
+   Then, when the user is asked to open a file and selects ``Emacs''
+   as the application that will open the file, the system pops up a
+   window, this activity, and calls the `onCreate' function.
+
+   `onCreate' then tries very to find the file name of the file that
+   was selected, and give it to emacsclient.
+
+   If emacsclient successfully opens the file, then this activity
+   starts EmacsActivity (to bring it on to the screen); otherwise, it
+   displays the output of emacsclient or any error message that occurs
+   and exits.  */
+
+import android.app.AlertDialog;
+import android.app.Activity;
+
+import android.content.ContentResolver;
+import android.content.DialogInterface;
+import android.content.Intent;
+
+import android.net.Uri;
+
+import android.os.Build;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+
+public final class EmacsOpenActivity extends Activity
+  implements DialogInterface.OnClickListener,
+  DialogInterface.OnCancelListener
+{
+  private static final String TAG = "EmacsOpenActivity";
+
+  /* The name of any file that should be opened as EmacsThread starts
+     Emacs.  This is never cleared, even if EmacsOpenActivity is
+     started a second time, as EmacsThread only starts once.  */
+  public static String fileToOpen;
+
+  /* Any currently focused EmacsOpenActivity.  Used to show pop ups
+     while the activity is active and Emacs doesn't have permission to
+     display over other programs.  */
+  public static EmacsOpenActivity currentActivity;
+
+  private class EmacsClientThread extends Thread
+  {
+    private ProcessBuilder builder;
+
+    public
+    EmacsClientThread (ProcessBuilder processBuilder)
+    {
+      builder = processBuilder;
+    }
+
+    @Override
+    public void
+    run ()
+    {
+      Process process;
+      InputStream error;
+      String errorText;
+
+      try
+       {
+         /* Start emacsclient.  */
+         process = builder.start ();
+         process.waitFor ();
+
+         /* Now figure out whether or not starting the process was
+            successful.  */
+         if (process.exitValue () == 0)
+           finishSuccess ();
+         else
+           finishFailure ("Error opening file", null);
+       }
+      catch (IOException exception)
+       {
+         finishFailure ("Internal error", exception.toString ());
+       }
+      catch (InterruptedException exception)
+       {
+         finishFailure ("Internal error", exception.toString ());
+       }
+    }
+  }
+
+  @Override
+  public void
+  onClick (DialogInterface dialog, int which)
+  {
+    finish ();
+  }
+
+  @Override
+  public void
+  onCancel (DialogInterface dialog)
+  {
+    finish ();
+  }
+
+  public String
+  readEmacsClientLog ()
+  {
+    File file, cache;
+    FileReader reader;
+    char[] buffer;
+    int rc;
+    StringBuilder builder;
+
+    /* Because the ProcessBuilder functions necessary to redirect
+       process output are not implemented on Android 7 and earlier,
+       print a generic error message.  */
+
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+      return ("This is likely because the Emacs server"
+             + " is not running, or because you did"
+             + " not grant Emacs permission to access"
+             + " external storage.");
+
+    cache = getCacheDir ();
+    file = new File (cache, "emacsclient.log");
+    builder = new StringBuilder ();
+    reader = null;
+
+    try
+      {
+       reader = new FileReader (file);
+       buffer = new char[2048];
+
+       while ((rc = reader.read (buffer, 0, 2048)) != -1)
+         builder.append (buffer, 0, rc);
+
+       reader.close ();
+       return builder.toString ();
+      }
+    catch (IOException exception)
+      {
+       /* Close the reader if it's already been opened.  */
+
+       try
+         {
+           if (reader != null)
+             reader.close ();
+         }
+       catch (IOException e)
+         {
+           /* Not sure what to do here.  */
+         }
+
+       return ("Couldn't read emacsclient.log: "
+               + exception.toString ());
+      }
+  }
+
+  private void
+  displayFailureDialog (String title, String text)
+  {
+    AlertDialog.Builder builder;
+    AlertDialog dialog;
+
+    builder = new AlertDialog.Builder (this);
+    dialog = builder.create ();
+    dialog.setTitle (title);
+
+    if (text == null)
+      /* Read in emacsclient.log instead.  */
+      text = readEmacsClientLog ();
+
+    dialog.setMessage (text);
+    dialog.setButton (DialogInterface.BUTTON_POSITIVE, "OK", this);
+    dialog.setOnCancelListener (this);
+    dialog.show ();
+  }
+
+  /* Check that the specified FILE is readable.  If Android 4.4 or
+     later is being used, return URI formatted into a `/content/' file
+     name.
+
+     If it is not, then copy the file in FD to a location in the
+     system cache directory and return the name of that file.  */
+
+  private String
+  checkReadableOrCopy (String file, ParcelFileDescriptor fd,
+                      Uri uri)
+    throws IOException, FileNotFoundException
+  {
+    File inFile;
+    FileOutputStream outStream;
+    InputStream stream;
+    byte buffer[];
+    int read;
+    String content;
+
+    Log.d (TAG, "checkReadableOrCopy: " + file);
+
+    inFile = new File (file);
+
+    if (inFile.canRead ())
+      return file;
+
+    Log.d (TAG, "checkReadableOrCopy: NO");
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
+      {
+       content = EmacsService.buildContentName (uri);
+       Log.d (TAG, "checkReadableOrCopy: " + content);
+       return content;
+      }
+
+    /* inFile is now the file being written to.  */
+    inFile = new File (getCacheDir (), inFile.getName ());
+    buffer = new byte[4098];
+
+    /* Initialize both streams to NULL.  */
+    outStream = null;
+    stream = null;
+
+    try
+      {
+       outStream = new FileOutputStream (inFile);
+       stream = new FileInputStream (fd.getFileDescriptor ());
+
+       while ((read = stream.read (buffer)) >= 0)
+         outStream.write (buffer, 0, read);
+      }
+    finally
+      {
+       /* Note that this does not close FD.
+
+          Keep in mind that execution is transferred to ``finally''
+          even if an exception happens inside the while loop
+          above.  */
+
+       if (stream != null)
+         stream.close ();
+
+       if (outStream != null)
+         outStream.close ();
+      }
+
+    return inFile.getCanonicalPath ();
+  }
+
+  /* Finish this activity in response to emacsclient having
+     successfully opened a file.
+
+     In the main thread, close this window, and open a window
+     belonging to an Emacs frame.  */
+
+  public void
+  finishSuccess ()
+  {
+    runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         Intent intent;
+
+         intent = new Intent (EmacsOpenActivity.this,
+                              EmacsActivity.class);
+
+         /* This means only an existing frame will be displayed.  */
+         intent.addFlags (Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+         startActivity (intent);
+
+         EmacsOpenActivity.this.finish ();
+       }
+      });
+  }
+
+  /* Finish this activity after displaying a dialog associated with
+     failure to open a file.
+
+     Use TITLE as the title of the dialog.  If TEXT is non-NULL,
+     display that text in the dialog.  Otherwise, use the contents of
+     emacsclient.log in the cache directory instead, or describe why
+     that file cannot be read.  */
+
+  public void
+  finishFailure (final String title, final String text)
+  {
+    runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         displayFailureDialog (title, text);
+       }
+      });
+  }
+
+  public void
+  startEmacsClient (String fileName)
+  {
+    String libDir;
+    ProcessBuilder builder;
+    Process process;
+    EmacsClientThread thread;
+    File file;
+    Intent intent;
+
+    /* If the Emacs service is not running, then start Emacs and make
+       it open this file.  */
+
+    if (EmacsService.SERVICE == null)
+      {
+       fileToOpen = fileName;
+       intent = new Intent (EmacsOpenActivity.this,
+                            EmacsActivity.class);
+       finish ();
+       startActivity (intent);
+       return;
+      }
+
+    libDir = EmacsService.getLibraryDirectory (this);
+    builder = new ProcessBuilder (libDir + "/libemacsclient.so",
+                                 fileName, "--reuse-frame",
+                                 "--timeout=10", "--no-wait");
+
+    /* Redirection is unfortunately not possible in Android 7 and
+       earlier.  */
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+      {
+       file = new File (getCacheDir (), "emacsclient.log");
+
+       /* Redirect standard error to a file so that errors can be
+          meaningfully reported.  */
+
+       if (file.exists ())
+         file.delete ();
+
+       builder.redirectError (file);
+      }
+
+    /* Track process output in a new thread, since this is the UI
+       thread and doing so here can cause deadlocks when EmacsService
+       decides to wait for something.  */
+
+    thread = new EmacsClientThread (builder);
+    thread.start ();
+  }
+
+  /* Run emacsclient to open the file specified in the Intent that
+     caused this activity to start.
+
+     Determine the name of the file corresponding to the URI specified
+     in that intent; then, run emacsclient and wait for it to finish.
+
+     Finally, display any error message, transfer the focus to an
+     Emacs frame, and finish the activity.  */
+
+  @Override
+  public void
+  onCreate (Bundle savedInstanceState)
+  {
+    String action, fileName;
+    Intent intent;
+    Uri uri;
+    ContentResolver resolver;
+    ParcelFileDescriptor fd;
+    byte[] names;
+    String errorBlurb;
+
+    super.onCreate (savedInstanceState);
+
+    /* Obtain the intent that started Emacs.  */
+    intent = getIntent ();
+    action = intent.getAction ();
+
+    if (action == null)
+      {
+       finish ();
+       return;
+      }
+
+    /* Now see if the action specified is supported by Emacs.  */
+
+    if (action.equals ("android.intent.action.VIEW")
+       || action.equals ("android.intent.action.EDIT")
+       || action.equals ("android.intent.action.PICK"))
+      {
+       /* Obtain the URI of the action.  */
+       uri = intent.getData ();
+
+       if (uri == null)
+         {
+           finish ();
+           return;
+         }
+
+       /* Now, try to get the file name.  */
+
+       if (uri.getScheme ().equals ("file"))
+         fileName = uri.getPath ();
+       else
+         {
+           fileName = null;
+
+           if (uri.getScheme ().equals ("content"))
+             {
+               /* This is one of the annoying Android ``content''
+                  URIs.  Most of the time, there is actually an
+                  underlying file, but it cannot be found without
+                  opening the file and doing readlink on its file
+                  descriptor in /proc/self/fd.  */
+               resolver = getContentResolver ();
+               fd = null;
+
+               try
+                 {
+                   fd = resolver.openFileDescriptor (uri, "r");
+                   names = EmacsNative.getProcName (fd.getFd ());
+
+                   /* What is the right encoding here? */
+
+                   if (names != null)
+                     fileName = new String (names, "UTF-8");
+
+                   fileName = checkReadableOrCopy (fileName, fd, uri);
+                 }
+               catch (FileNotFoundException exception)
+                 {
+                   /* Do nothing.  */
+                 }
+               catch (IOException exception)
+                 {
+                   /* Do nothing.  */
+                 }
+
+               if (fd != null)
+                 {
+                   try
+                     {
+                       fd.close ();
+                     }
+                   catch (IOException exception)
+                     {
+                       /* Do nothing.  */
+                     }
+                 }
+             }
+
+           if (fileName == null)
+             {
+               errorBlurb = ("The URI: " + uri + " could not be opened"
+                             + ", as it does not encode file name inform"
+                             + "ation.");
+               displayFailureDialog ("Error opening file", errorBlurb);
+               return;
+             }
+         }
+
+       /* And start emacsclient.  Set `currentActivity' to this now.
+          Presumably, it will shortly become capable of displaying
+          dialogs.  */
+       currentActivity = this;
+       startEmacsClient (fileName);
+      }
+    else
+      finish ();
+  }
+
+
+
+  @Override
+  public void
+  onDestroy ()
+  {
+    Log.d (TAG, "onDestroy: " + this);
+
+    /* Clear `currentActivity' if it refers to the activity being
+       destroyed.  */
+
+    if (currentActivity == this)
+      this.currentActivity = null;
+
+    super.onDestroy ();
+  }
+
+  @Override
+  public void
+  onWindowFocusChanged (boolean isFocused)
+  {
+    Log.d (TAG, "onWindowFocusChanged: " + this + ", is now focused: "
+          + isFocused);
+
+    if (isFocused)
+      currentActivity = this;
+    else if (currentActivity == this)
+      currentActivity = null;
+
+    super.onWindowFocusChanged (isFocused);
+  }
+
+  @Override
+  public void
+  onPause ()
+  {
+    Log.d (TAG, "onPause: " + this);
+
+    /* XXX: clear currentActivity here as well; I don't know whether
+       or not onWindowFocusChanged is always called prior to this.  */
+
+    if (currentActivity == this)
+      currentActivity = null;
+
+    super.onPause ();
+  }
+}
diff --git a/java/org/gnu/emacs/EmacsPixmap.java 
b/java/org/gnu/emacs/EmacsPixmap.java
new file mode 100644
index 00000000000..eb011bc5e65
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsPixmap.java
@@ -0,0 +1,192 @@
+/* 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 java.lang.IllegalArgumentException;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+
+import android.os.Build;
+
+/* Drawable backed by bitmap.  */
+
+public final class EmacsPixmap extends EmacsHandleObject
+  implements EmacsDrawable
+{
+  /* The depth of the bitmap.  This is not actually used, just defined
+     in order to be consistent with X.  */
+  public int depth, width, height;
+
+  /* The bitmap itself.  */
+  public Bitmap bitmap;
+
+  /* The canvas used to draw to BITMAP.  */
+  public Canvas canvas;
+
+  /* Whether or not GC should be explicitly triggered upon
+     release.  */
+  private boolean needCollect;
+
+  /* ID used to determine whether or not the GC clip rects
+     changed.  */
+  private long gcClipRectID;
+
+  public
+  EmacsPixmap (short handle, int colors[], int width,
+              int height, int depth)
+  {
+    super (handle);
+
+    if (depth != 1 && depth != 24)
+      throw new IllegalArgumentException ("Invalid depth specified"
+                                         + " for pixmap: " + depth);
+
+    switch (depth)
+      {
+      case 1:
+       bitmap = Bitmap.createBitmap (colors, width, height,
+                                     Bitmap.Config.ALPHA_8);
+       break;
+
+      case 24:
+       bitmap = Bitmap.createBitmap (colors, width, height,
+                                     Bitmap.Config.ARGB_8888);
+       bitmap.setHasAlpha (false);
+       break;
+      }
+
+    this.width = width;
+    this.height = height;
+    this.depth = depth;
+  }
+
+  public
+  EmacsPixmap (short handle, int width, int height, int depth)
+  {
+    super (handle);
+
+    if (depth != 1 && depth != 24)
+      throw new IllegalArgumentException ("Invalid depth specified"
+                                         + " for pixmap: " + depth);
+
+    switch (depth)
+      {
+      case 1:
+       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+         bitmap = Bitmap.createBitmap (width, height,
+                                       Bitmap.Config.ALPHA_8,
+                                       false);
+       else
+         bitmap = Bitmap.createBitmap (width, height,
+                                       Bitmap.Config.ALPHA_8);
+       break;
+
+      case 24:
+
+       /* Emacs doesn't just use the first kind of `createBitmap'
+          because the latter allows specifying that the pixmap is
+          always opaque, which really increases efficiency.  */
+       if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+         bitmap = Bitmap.createBitmap (width, height,
+                                       Bitmap.Config.ARGB_8888);
+       else
+         bitmap = Bitmap.createBitmap (width, height,
+                                       Bitmap.Config.ARGB_8888,
+                                       false);
+       break;
+      }
+
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR1)
+      /* On these old versions of Android, Bitmap.recycle frees bitmap
+        contents immediately.  */
+      needCollect = false;
+    else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
+      needCollect = (bitmap.getByteCount ()
+                    >= 1024 * 512);
+    else
+      needCollect = (bitmap.getAllocationByteCount ()
+                    >= 1024 * 512);
+
+    bitmap.eraseColor (0xff000000);
+
+    this.width = width;
+    this.height = height;
+    this.depth = depth;
+  }
+
+  @Override
+  public Canvas
+  lockCanvas (EmacsGC gc)
+  {
+    int i;
+
+    if (canvas == null)
+      {
+       canvas = new Canvas (bitmap);
+       canvas.save ();
+      }
+
+    /* Now see if clipping has to be redone.  */
+    if (gc.clipRectID == gcClipRectID)
+      return canvas;
+
+    /* It does have to be redone.  Reapply gc.real_clip_rects.  */
+    canvas.restore ();
+    canvas.save ();
+
+    if (gc.real_clip_rects != null)
+      {
+       for (i = 0; i < gc.real_clip_rects.length; ++i)
+         canvas.clipRect (gc.real_clip_rects[i]);
+      }
+
+    /* Save the clip rect ID again.  */
+    gcClipRectID = gc.clipRectID;
+    return canvas;
+  }
+
+  @Override
+  public void
+  damageRect (Rect damageRect)
+  {
+
+  }
+
+  @Override
+  public Bitmap
+  getBitmap ()
+  {
+    return bitmap;
+  }
+
+  @Override
+  public void
+  destroyHandle ()
+  {
+    bitmap.recycle ();
+    bitmap = null;
+
+    /* Collect the bitmap storage if the bitmap is big.  */
+    if (needCollect)
+      Runtime.getRuntime ().gc ();
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsPreferencesActivity.java 
b/java/org/gnu/emacs/EmacsPreferencesActivity.java
new file mode 100644
index 00000000000..7e67cc3679b
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsPreferencesActivity.java
@@ -0,0 +1,168 @@
+/* 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 java.io.File;
+
+import android.app.Activity;
+
+import android.content.Intent;
+
+import android.os.Bundle;
+import android.os.Build;
+
+import android.widget.Toast;
+
+import android.preference.*;
+
+/* This module provides a ``preferences'' display for Emacs.  It is
+   supposed to be launched from inside the Settings application to
+   perform various actions, such as starting Emacs with the ``-Q''
+   option, which would not be possible otherwise, as there is no
+   command line on Android.
+
+   Android provides a preferences activity, but it is deprecated.
+   Unfortunately, there is no alternative that looks the same way.  */
+
+@SuppressWarnings ("deprecation")
+public class EmacsPreferencesActivity extends PreferenceActivity
+{
+  /* Restart Emacs with -Q.  Call EmacsThread.exit to kill Emacs now,
+     and tell the system to start EmacsActivity with some parameters
+     later.  */
+
+  private void
+  startEmacsQ ()
+  {
+    Intent intent;
+
+    intent = new Intent (this, EmacsActivity.class);
+    intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+    intent.putExtra ("org.gnu.emacs.STARTUP_ARGUMENT", "--quick");
+    startActivity (intent);
+    System.exit (0);
+  }
+
+  /* Restart Emacs with `--debug-init'.  Call EmacsThread.exit to kill
+     Emacs now, and tell the system to EmacsActivity with some
+     parameters later.  */
+
+  private void
+  startEmacsDebugInit ()
+  {
+    Intent intent;
+
+    intent = new Intent (this, EmacsActivity.class);
+    intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+    intent.putExtra ("org.gnu.emacs.STARTUP_ARGUMENT", "--debug-init");
+    startActivity (intent);
+    System.exit (0);
+  }
+
+  /* Erase Emacs's dump file.  */
+
+  private void
+  eraseDumpFile ()
+  {
+    String wantedDumpFile;
+    File file;
+    Toast toast;
+
+    wantedDumpFile = ("emacs-" + EmacsNative.getFingerprint ()
+                     + ".pdmp");
+    file = new File (getFilesDir (), wantedDumpFile);
+
+    if (file.exists ())
+      file.delete ();
+
+    /* Make sure to clear EmacsApplication.dumpFileName, or
+       starting Emacs without restarting this program will
+       make Emacs try to load a nonexistent dump file.  */
+    EmacsApplication.dumpFileName = null;
+
+    /* Display a message stating that the dump file has been
+       erased.  */
+    toast = Toast.makeText (this, "Dump file removed",
+                           Toast.LENGTH_SHORT);
+    toast.show ();
+  }
+
+  @Override
+  public final void
+  onCreate (Bundle savedInstanceState)
+  {
+    Preference tem;
+    Preference.OnPreferenceClickListener listener;
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+      setTheme (android.R.style.Theme_DeviceDefault_Settings);
+    else if (Build.VERSION.SDK_INT
+            >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+      setTheme (android.R.style.Theme_DeviceDefault);
+
+    /* This must come before using any preference APIs.  */
+    super.onCreate (savedInstanceState);
+
+    /* Add preferences from the XML file where they are defined.  */
+    addPreferencesFromResource (R.xml.preferences);
+
+    /* Now, set up on click handlers for each of the preferences
+       items.  */
+
+    tem = findPreference ("start_quick");
+    listener = new Preference.OnPreferenceClickListener () {
+       @Override
+       public boolean
+       onPreferenceClick (Preference preference)
+       {
+         startEmacsQ ();
+         return true;
+       }
+      };
+
+    tem.setOnPreferenceClickListener (listener);
+    tem = findPreference ("start_debug_init");
+    listener = new Preference.OnPreferenceClickListener () {
+       @Override
+       public boolean
+       onPreferenceClick (Preference preference)
+       {
+         startEmacsDebugInit ();
+         return true;
+       }
+      };
+
+    tem.setOnPreferenceClickListener (listener);
+    tem = findPreference ("erase_dump");
+    listener = new Preference.OnPreferenceClickListener () {
+       @Override
+       public boolean
+       onPreferenceClick (Preference preference)
+       {
+         eraseDumpFile ();
+         return true;
+       }
+      };
+
+    tem.setOnPreferenceClickListener (listener);
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsSafThread.java 
b/java/org/gnu/emacs/EmacsSafThread.java
new file mode 100644
index 00000000000..3ae3c0839ce
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSafThread.java
@@ -0,0 +1,1687 @@
+/* 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 java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+
+import android.os.Build;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.OperationCanceledException;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+
+import android.util.Log;
+
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
+
+
+
+/* Emacs runs long-running SAF operations on a second thread running
+   its own handler.  These operations include opening files and
+   maintaining the path to document ID cache.
+
+   Because Emacs paths are based on file display names, while Android
+   document identifiers have no discernible hierarchy of their own,
+   each file name lookup must carry out a repeated search for
+   directory documents with the names of all of the file name's
+   constituent components, where each iteration searches within the
+   directory document identified by the previous iteration.
+
+   A time limited cache tying components to document IDs is maintained
+   in order to speed up consecutive searches for file names sharing
+   the same components.  Since listening for changes to each document
+   in the cache is prohibitively expensive, Emacs instead elects to
+   periodically remove entries that are older than a predetermined
+   amount of a time.
+
+   The cache is split into two levels: the first caches the
+   relationships between display names and document IDs, while the
+   second caches individual document IDs and their contents (children,
+   type, etc.)
+
+   Long-running operations are also run on this thread for another
+   reason: Android uses special cancellation objects to terminate
+   ongoing IPC operations.  However, the functions that perform these
+   operations block instead of providing mechanisms for the caller to
+   wait for their completion while also reading async input, as a
+   consequence of which the calling thread is unable to signal the
+   cancellation objects that it provides.  Performing the blocking
+   operations in this auxiliary thread enables the main thread to wait
+   for completion itself, signaling the cancellation objects when it
+   deems necessary.  */
+
+
+
+public final class EmacsSafThread extends HandlerThread
+{
+  private static final String TAG = "EmacsSafThread";
+
+  /* The content resolver used by this thread.  */
+  private final ContentResolver resolver;
+
+  /* Map between tree URIs and the cache entry representing its
+     toplevel directory.  */
+  private final HashMap<Uri, CacheToplevel> cacheToplevels;
+
+  /* Handler for this thread's main loop.  */
+  private Handler handler;
+
+  /* File access mode constants.  See `man 7 inode'.  */
+  public static final int S_IRUSR = 0000400;
+  public static final int S_IWUSR = 0000200;
+  public static final int S_IXUSR = 0000100;
+  public static final int S_IFCHR = 0020000;
+  public static final int S_IFDIR = 0040000;
+  public static final int S_IFREG = 0100000;
+
+  /* Number of seconds in between each attempt to prune the storage
+     cache.  */
+  public static final int CACHE_PRUNE_TIME = 10;
+
+  /* Number of seconds after which an entry in the cache is to be
+     considered invalid.  */
+  public static final int CACHE_INVALID_TIME = 10;
+
+  public
+  EmacsSafThread (ContentResolver resolver)
+  {
+    super ("Document provider access thread");
+    this.resolver = resolver;
+    this.cacheToplevels = new HashMap<Uri, CacheToplevel> ();
+  }
+
+
+
+  @Override
+  public void
+  start ()
+  {
+    super.start ();
+
+    /* Set up the handler after the thread starts.  */
+    handler = new Handler (getLooper ());
+
+    /* And start periodically pruning the cache.  */
+    postPruneMessage ();
+  }
+
+
+  private static final class CacheToplevel
+  {
+    /* Map between document names and children.  */
+    HashMap<String, DocIdEntry> children;
+
+    /* Map between document names and file status.  */
+    HashMap<String, StatCacheEntry> statCache;
+
+    /* Map between document IDs and cache items.  */
+    HashMap<String, CacheEntry> idCache;
+  };
+
+  private static final class StatCacheEntry
+  {
+    /* The time at which this cache entry was created.  */
+    long time;
+
+    /* Flags, size, and modification time of this file.  */
+    long flags, size, mtime;
+
+    /* Whether or not this file is a directory.  */
+    boolean isDirectory;
+
+    public
+    StatCacheEntry ()
+    {
+      time = SystemClock.uptimeMillis ();
+    }
+
+    public boolean
+    isValid ()
+    {
+      return ((SystemClock.uptimeMillis () - time)
+             < CACHE_INVALID_TIME * 1000);
+    }
+  };
+
+  private static final class DocIdEntry
+  {
+    /* The document ID.  */
+    String documentId;
+
+    /* The time this entry was created.  */
+    long time;
+
+    public
+    DocIdEntry ()
+    {
+      time = SystemClock.uptimeMillis ();
+    }
+
+    /* Return a cache entry comprised of the state of the file
+       identified by `documentId'.  TREE is the URI of the tree
+       containing this entry, and TOPLEVEL is the toplevel
+       representing it.  SIGNAL is a cancellation signal.
+
+       RESOLVER is the content provider used to retrieve file
+       information.
+
+       Value is NULL if the file cannot be found.  */
+
+    public CacheEntry
+    getCacheEntry (ContentResolver resolver, Uri tree,
+                  CacheToplevel toplevel,
+                  CancellationSignal signal)
+    {
+      Uri uri;
+      String[] projection;
+      String type;
+      Cursor cursor;
+      int column;
+      CacheEntry entry;
+
+      /* Create a document URI representing DOCUMENTID within URI's
+        authority.  */
+
+      uri = DocumentsContract.buildDocumentUriUsingTree (tree,
+                                                        documentId);
+      projection = new String[] {
+       Document.COLUMN_MIME_TYPE,
+      };
+
+      cursor = null;
+
+      try
+       {
+         cursor = resolver.query (uri, projection, null,
+                                  null, null, signal);
+
+         if (!cursor.moveToFirst ())
+           return null;
+
+         column = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE);
+
+         if (column < 0)
+           return null;
+
+         type = cursor.getString (column);
+
+         if (type == null)
+           return null;
+
+         entry = new CacheEntry ();
+         entry.type = type;
+         toplevel.idCache.put (documentId, entry);
+         return entry;
+       }
+      catch (OperationCanceledException e)
+       {
+         throw e;
+       }
+      catch (Throwable e)
+       {
+         return null;
+       }
+      finally
+       {
+         if (cursor != null)
+           cursor.close ();
+       }
+    }
+
+    public boolean
+    isValid ()
+    {
+      return ((SystemClock.uptimeMillis () - time)
+             < CACHE_INVALID_TIME * 1000);
+    }
+  };
+
+  private static final class CacheEntry
+  {
+    /* The type of this document.  */
+    String type;
+
+    /* Map between document names and children.  */
+    HashMap<String, DocIdEntry> children;
+
+    /* The time this entry was created.  */
+    long time;
+
+    public
+    CacheEntry ()
+    {
+      children = new HashMap<String, DocIdEntry> ();
+      time = SystemClock.uptimeMillis ();
+    }
+
+    public boolean
+    isValid ()
+    {
+      return ((SystemClock.uptimeMillis () - time)
+             < CACHE_INVALID_TIME * 1000);
+    }
+  };
+
+  /* Create or return a toplevel for the given tree URI.  */
+
+  private CacheToplevel
+  getCache (Uri uri)
+  {
+    CacheToplevel toplevel;
+
+    toplevel = cacheToplevels.get (uri);
+
+    if (toplevel != null)
+      return toplevel;
+
+    toplevel = new CacheToplevel ();
+    toplevel.children = new HashMap<String, DocIdEntry> ();
+    toplevel.statCache = new HashMap<String, StatCacheEntry> ();
+    toplevel.idCache = new HashMap<String, CacheEntry> ();
+    cacheToplevels.put (uri, toplevel);
+    return toplevel;
+  }
+
+  /* Remove each cache entry within COLLECTION older than
+     CACHE_INVALID_TIME.  */
+
+  private void
+  pruneCache1 (Collection<DocIdEntry> collection)
+  {
+    Iterator<DocIdEntry> iter;
+    DocIdEntry tem;
+
+    iter = collection.iterator ();
+    while (iter.hasNext ())
+      {
+       /* Get the cache entry.  */
+       tem = iter.next ();
+
+       /* If it's not valid anymore, remove it.  Iterating over a
+          collection whose contents are being removed is undefined
+          unless the removal is performed using the iterator's own
+          `remove' function, so tem.remove cannot be used here.  */
+
+       if (tem.isValid ())
+         continue;
+
+       iter.remove ();
+      }
+  }
+
+  /* Remove every entry older than CACHE_INVALID_TIME from each
+     toplevel inside `cachedToplevels'.  */
+
+  private void
+  pruneCache ()
+  {
+    Iterator<CacheEntry> iter;
+    Iterator<StatCacheEntry> statIter;
+    CacheEntry tem;
+    StatCacheEntry stat;
+
+    for (CacheToplevel toplevel : cacheToplevels.values ())
+      {
+       /* First, clean up expired cache entries.  */
+       iter = toplevel.idCache.values ().iterator ();
+
+       while (iter.hasNext ())
+         {
+           /* Get the cache entry.  */
+           tem = iter.next ();
+
+           /* If it's not valid anymore, remove it.  Iterating over a
+              collection whose contents are being removed is
+              undefined unless the removal is performed using the
+              iterator's own `remove' function, so tem.remove cannot
+              be used here.  */
+
+           if (tem.isValid ())
+             {
+               /* Otherwise, clean up expired items in its document
+                  ID cache.  */
+               pruneCache1 (tem.children.values ());
+               continue;
+             }
+
+           iter.remove ();
+         }
+
+       statIter = toplevel.statCache.values ().iterator ();
+
+       while (statIter.hasNext ())
+         {
+           /* Get the cache entry.  */
+           stat = statIter.next ();
+
+           /* If it's not valid anymore, remove it.  Iterating over a
+              collection whose contents are being removed is
+              undefined unless the removal is performed using the
+              iterator's own `remove' function, so tem.remove cannot
+              be used here.  */
+
+           if (stat.isValid ())
+             continue;
+
+           statIter.remove ();
+         }
+      }
+
+    postPruneMessage ();
+  }
+
+  /* Cache file information within TOPLEVEL, under the list of
+     children CHILDREN.
+
+     NAME, ID, and TYPE should respectively be the display name of the
+     document within its parent document (the CacheEntry whose
+     `children' field is CHILDREN), its document ID, and its MIME
+     type.
+
+     If ID_ENTRY_EXISTS, don't create a new document ID entry within
+     CHILDREN indexed by NAME.
+
+     Value is the cache entry saved for the document ID.  */
+
+  private CacheEntry
+  cacheChild (CacheToplevel toplevel,
+             HashMap<String, DocIdEntry> children,
+             String name, String id, String type,
+             boolean id_entry_exists)
+  {
+    DocIdEntry idEntry;
+    CacheEntry cacheEntry;
+
+    if (!id_entry_exists)
+      {
+       idEntry = new DocIdEntry ();
+       idEntry.documentId = id;
+       children.put (name, idEntry);
+      }
+
+    cacheEntry = new CacheEntry ();
+    cacheEntry.type = type;
+    toplevel.idCache.put (id, cacheEntry);
+    return cacheEntry;
+  }
+
+  /* Cache file status for DOCUMENTID within TOPLEVEL.  Value is the
+     new cache entry.  CURSOR is the cursor from where to retrieve the
+     file status, in the form of the columns COLUMN_FLAGS,
+     COLUMN_SIZE, COLUMN_MIME_TYPE and COLUMN_LAST_MODIFIED.  */
+
+  private StatCacheEntry
+  cacheFileStatus (String documentId, CacheToplevel toplevel,
+                  Cursor cursor)
+  {
+    StatCacheEntry entry;
+    int flagsIndex, columnIndex, typeIndex;
+    int sizeIndex, mtimeIndex;
+    String type;
+
+    /* Obtain the indices for columns wanted from this cursor.  */
+    flagsIndex = cursor.getColumnIndex (Document.COLUMN_FLAGS);
+    sizeIndex = cursor.getColumnIndex (Document.COLUMN_SIZE);
+    typeIndex = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE);
+    mtimeIndex = cursor.getColumnIndex (Document.COLUMN_LAST_MODIFIED);
+
+    /* COLUMN_LAST_MODIFIED is allowed to be absent in a
+       conforming documents provider.  */
+    if (flagsIndex < 0 || sizeIndex < 0 || typeIndex < 0)
+      return null;
+
+    /* Get the file status from CURSOR.  */
+    entry = new StatCacheEntry ();
+    entry.flags = cursor.getInt (flagsIndex);
+    type = cursor.getString (typeIndex);
+
+    if (type == null)
+      return null;
+
+    entry.isDirectory = type.equals (Document.MIME_TYPE_DIR);
+
+    if (cursor.isNull (sizeIndex))
+      /* The size is unknown.  */
+      entry.size = -1;
+    else
+      entry.size = cursor.getLong (sizeIndex);
+
+    /* mtimeIndex is potentially unset, since document providers
+       aren't obligated to provide modification times.  */
+
+    if (mtimeIndex >= 0 && !cursor.isNull (mtimeIndex))
+      entry.mtime = cursor.getLong (mtimeIndex);
+
+    /* Finally, add this entry to the cache and return.  */
+    toplevel.statCache.put (documentId, entry);
+    return entry;
+  }
+
+  /* Cache the type and as many of the children of the directory
+     designated by DOCUMENTID as possible into TOPLEVEL.
+
+     CURSOR should be a cursor representing an open directory stream,
+     with its projection consisting of at least the display name,
+     document ID and MIME type columns.
+
+     Rewind the position of CURSOR to before its first element after
+     completion.  */
+
+  private void
+  cacheDirectoryFromCursor (CacheToplevel toplevel, String documentId,
+                           Cursor cursor)
+  {
+    CacheEntry entry, constitutent;
+    int nameColumn, idColumn, typeColumn;
+    String id, name, type;
+    DocIdEntry idEntry;
+
+    /* Find the numbers of the columns wanted.  */
+
+    nameColumn
+      = cursor.getColumnIndex (Document.COLUMN_DISPLAY_NAME);
+    idColumn
+      = cursor.getColumnIndex (Document.COLUMN_DOCUMENT_ID);
+    typeColumn
+      = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE);
+
+    if (nameColumn < 0 || idColumn < 0 || typeColumn < 0)
+      return;
+
+    entry = new CacheEntry ();
+
+    /* We know this is a directory already.  */
+    entry.type = Document.MIME_TYPE_DIR;
+    toplevel.idCache.put (documentId, entry);
+
+    /* Now, try to cache each of its constituents.  */
+
+    while (cursor.moveToNext ())
+      {
+       try
+         {
+           name = cursor.getString (nameColumn);
+           id = cursor.getString (idColumn);
+           type = cursor.getString (typeColumn);
+
+           if (name == null || id == null || type == null)
+             continue;
+
+           /* First, add the name and ID to ENTRY's map of
+              children.  */
+           idEntry = new DocIdEntry ();
+           idEntry.documentId = id;
+           entry.children.put (id, idEntry);
+
+           /* Cache the file status for ID within TOPELVEL too; if a
+              directory listing is being requested, it's very likely
+              that a series of calls for file status will follow.  */
+
+           cacheFileStatus (id, toplevel, cursor);
+
+           /* If this constituent is a directory, don't cache any
+              information about it.  It cannot be cached without
+              knowing its children.  */
+
+           if (type.equals (Document.MIME_TYPE_DIR))
+             continue;
+
+           /* Otherwise, create a new cache entry comprised of its
+              type.  */
+           constitutent = new CacheEntry ();
+           constitutent.type = type;
+           toplevel.idCache.put (documentId, entry);
+         }
+       catch (Exception e)
+         {
+           e.printStackTrace ();
+           continue;
+         }
+      }
+
+    /* Rewind cursor back to the beginning.  */
+    cursor.moveToPosition (-1);
+  }
+
+  /* Post a message to run `pruneCache' every CACHE_PRUNE_TIME
+     seconds.  */
+
+  private void
+  postPruneMessage ()
+  {
+    handler.postDelayed (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         pruneCache ();
+       }
+      }, CACHE_PRUNE_TIME * 1000);
+  }
+
+  /* Invalidate the cache entry denoted by DOCUMENT_ID, within the
+     document tree URI.
+     Call this after deleting a document or directory.
+
+     At the same time, remove the final component within the file name
+     CACHENAME from the cache if it exists.  */
+
+  public void
+  postInvalidateCache (final Uri uri, final String documentId,
+                      final String cacheName)
+  {
+    handler.post (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         CacheToplevel toplevel;
+         HashMap<String, DocIdEntry> children;
+         String[] components;
+         CacheEntry entry;
+         DocIdEntry idEntry;
+
+         toplevel = getCache (uri);
+         toplevel.idCache.remove (documentId);
+         toplevel.statCache.remove (documentId);
+
+         /* If the parent of CACHENAME is cached, remove it.  */
+
+         children = toplevel.children;
+         components = cacheName.split ("/");
+
+         for (String component : components)
+           {
+             /* Java `split' removes trailing empty matches but not
+                leading or intermediary ones.  */
+             if (component.isEmpty ())
+               continue;
+
+             if (component == components[components.length - 1])
+               {
+                 /* This is the last component, so remove it from
+                    children.  */
+                 children.remove (component);
+                 return;
+               }
+             else
+               {
+                 /* Search for this component within the last level
+                    of the cache.  */
+
+                 idEntry = children.get (component);
+
+                 if (idEntry == null)
+                   /* Not cached, so return.  */
+                   return;
+
+                 entry = toplevel.idCache.get (idEntry.documentId);
+
+                 if (entry == null)
+                   /* Not cached, so return.  */
+                   return;
+
+                 /* Locate the next component within this
+                    directory.  */
+                 children = entry.children;
+               }
+           }
+       }
+      });
+  }
+
+  /* Invalidate the cache entry denoted by DOCUMENT_ID, within the
+     document tree URI.
+     Call this after deleting a document or directory.
+
+     At the same time, remove the child referring to DOCUMENTID from
+     within CACHENAME's cache entry if it exists.  */
+
+  public void
+  postInvalidateCacheDir (final Uri uri, final String documentId,
+                         final String cacheName)
+  {
+    handler.post (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         CacheToplevel toplevel;
+         HashMap<String, DocIdEntry> children;
+         String[] components;
+         CacheEntry entry;
+         DocIdEntry idEntry;
+         Iterator<DocIdEntry> iter;
+
+         toplevel = getCache (uri);
+         toplevel.idCache.remove (documentId);
+         toplevel.statCache.remove (documentId);
+
+         /* Now remove DOCUMENTID from CACHENAME's cache entry, if
+            any.  */
+
+         children = toplevel.children;
+         components = cacheName.split ("/");
+
+         for (String component : components)
+           {
+             /* Java `split' removes trailing empty matches but not
+                leading or intermediary ones.  */
+             if (component.isEmpty ())
+               continue;
+
+             /* Search for this component within the last level
+                of the cache.  */
+
+             idEntry = children.get (component);
+
+             if (idEntry == null)
+               /* Not cached, so return.  */
+               return;
+
+             entry = toplevel.idCache.get (idEntry.documentId);
+
+             if (entry == null)
+               /* Not cached, so return.  */
+               return;
+
+             /* Locate the next component within this
+                directory.  */
+             children = entry.children;
+           }
+
+         iter = children.values ().iterator ();
+         while (iter.hasNext ())
+           {
+             idEntry = iter.next ();
+
+             if (idEntry.documentId.equals (documentId))
+               {
+                 iter.remove ();
+                 break;
+               }
+           }
+       }
+      });
+  }
+
+  /* Invalidate the file status cache entry for DOCUMENTID within URI.
+     Call this when the contents of a file (i.e. the constituents of a
+     directory file) may have changed, but the document's display name
+     has not.  */
+
+  public void
+  postInvalidateStat (final Uri uri, final String documentId)
+  {
+    handler.post (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         CacheToplevel toplevel;
+
+         toplevel = getCache (uri);
+         toplevel.statCache.remove (documentId);
+       }
+      });
+  }
+
+
+
+  /* ``Prototypes'' for nested functions that are run within the SAF
+     thread and accepts a cancellation signal.  They differ in their
+     return types.  */
+
+  private abstract class SafIntFunction
+  {
+    /* The ``throws Throwable'' here is a Java idiosyncracy that tells
+       the compiler to allow arbitrary error objects to be signaled
+       from within this function.
+
+       Later, runIntFunction will try to re-throw any error object
+       generated by this function in the Emacs thread, using a trick
+       to avoid the compiler requirement to expressly declare that an
+       error (and which types of errors) will be signaled.  */
+
+    public abstract int runInt (CancellationSignal signal)
+      throws Throwable;
+  };
+
+  private abstract class SafObjectFunction
+  {
+    /* The ``throws Throwable'' here is a Java idiosyncracy that tells
+       the compiler to allow arbitrary error objects to be signaled
+       from within this function.
+
+       Later, runObjectFunction will try to re-throw any error object
+       generated by this function in the Emacs thread, using a trick
+       to avoid the compiler requirement to expressly declare that an
+       error (and which types of errors) will be signaled.  */
+
+    public abstract Object runObject (CancellationSignal signal)
+      throws Throwable;
+  };
+
+
+
+  /* Functions that run cancel-able queries.  These functions are
+     internally run within the SAF thread.  */
+
+  /* Throw the specified EXCEPTION.  The type template T is erased by
+     the compiler before the object is compiled, so the compiled code
+     simply throws EXCEPTION without the cast being verified.
+
+     T should be RuntimeException to obtain the desired effect of
+     throwing an exception without a compiler check.  */
+
+  @SuppressWarnings("unchecked")
+  private static <T extends Throwable> void
+  throwException (Throwable exception)
+    throws T
+  {
+    throw (T) exception;
+  }
+
+  /* Run the given function (or rather, its `runInt' field) within the
+     SAF thread, waiting for it to complete.
+
+     If async input arrives in the meantime and sets Vquit_flag,
+     signal the cancellation signal supplied to that function.
+
+     Rethrow any exception thrown from that function, and return its
+     value otherwise.  */
+
+  private int
+  runIntFunction (final SafIntFunction function)
+  {
+    final EmacsHolder<Object> result;
+    final CancellationSignal signal;
+    Throwable throwable;
+
+    result = new EmacsHolder<Object> ();
+    signal = new CancellationSignal ();
+
+    handler.post (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         try
+           {
+             result.thing
+               = Integer.valueOf (function.runInt (signal));
+           }
+         catch (Throwable throwable)
+           {
+             result.thing = throwable;
+           }
+
+         EmacsNative.safPostRequest ();
+       }
+      });
+
+    if (EmacsNative.safSyncAndReadInput () != 0)
+      {
+       signal.cancel ();
+
+       /* Now wait for the function to finish.  Either the signal has
+          arrived after the query took place, in which case it will
+          finish normally, or an OperationCanceledException will be
+          thrown.  */
+
+       EmacsNative.safSync ();
+      }
+
+    if (result.thing instanceof Throwable)
+      {
+       throwable = (Throwable) result.thing;
+       EmacsSafThread.<RuntimeException>throwException (throwable);
+      }
+
+    return (Integer) result.thing;
+  }
+
+  /* Run the given function (or rather, its `runObject' field) within
+     the SAF thread, waiting for it to complete.
+
+     If async input arrives in the meantime and sets Vquit_flag,
+     signal the cancellation signal supplied to that function.
+
+     Rethrow any exception thrown from that function, and return its
+     value otherwise.  */
+
+  private Object
+  runObjectFunction (final SafObjectFunction function)
+  {
+    final EmacsHolder<Object> result;
+    final CancellationSignal signal;
+    Throwable throwable;
+
+    result = new EmacsHolder<Object> ();
+    signal = new CancellationSignal ();
+
+    handler.post (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         try
+           {
+             result.thing = function.runObject (signal);
+           }
+         catch (Throwable throwable)
+           {
+             result.thing = throwable;
+           }
+
+         EmacsNative.safPostRequest ();
+       }
+      });
+
+    if (EmacsNative.safSyncAndReadInput () != 0)
+      {
+       signal.cancel ();
+
+       /* Now wait for the function to finish.  Either the signal has
+          arrived after the query took place, in which case it will
+          finish normally, or an OperationCanceledException will be
+          thrown.  */
+
+       EmacsNative.safSync ();
+      }
+
+    if (result.thing instanceof Throwable)
+      {
+       throwable = (Throwable) result.thing;
+       EmacsSafThread.<RuntimeException>throwException (throwable);
+      }
+
+    return result.thing;
+  }
+
+  /* The crux of `documentIdFromName1', run within the SAF thread.
+     SIGNAL should be a cancellation signal run upon quitting.  */
+
+  private int
+  documentIdFromName1 (String tree_uri, String name,
+                      String[] id_return, CancellationSignal signal)
+  {
+    Uri uri, treeUri;
+    String id, type, newId, newType;
+    String[] components, projection;
+    Cursor cursor;
+    int nameColumn, idColumn, typeColumn;
+    CacheToplevel toplevel;
+    DocIdEntry idEntry;
+    HashMap<String, DocIdEntry> children, next;
+    CacheEntry cache;
+
+    projection = new String[] {
+      Document.COLUMN_DISPLAY_NAME,
+      Document.COLUMN_DOCUMENT_ID,
+      Document.COLUMN_MIME_TYPE,
+    };
+
+    /* Parse the URI identifying the tree first.  */
+    uri = Uri.parse (tree_uri);
+
+    /* Now, split NAME into its individual components.  */
+    components = name.split ("/");
+
+    /* Set id and type to the value at the root of the tree.  */
+    type = id = null;
+    cursor = null;
+
+    /* Obtain the top level of this cache.  */
+    toplevel = getCache (uri);
+
+    /* Set the current map of children to this top level.  */
+    children = toplevel.children;
+
+    /* For each component... */
+
+    try
+      {
+       for (String component : components)
+         {
+           /* Java split doesn't behave very much like strtok when it
+              comes to trailing and leading delimiters...  */
+           if (component.isEmpty ())
+             continue;
+
+           /* Search for component within the currently cached list
+              of children.  */
+
+           idEntry = children.get (component);
+
+           if (idEntry != null)
+             {
+               /* The document ID is known.  Now find the
+                  corresponding document ID cache.  */
+
+               cache = toplevel.idCache.get (idEntry.documentId);
+
+               /* Fetch just the information for this document.  */
+
+               if (cache == null)
+                 cache = idEntry.getCacheEntry (resolver, uri, toplevel,
+                                                signal);
+
+               if (cache == null)
+                 {
+                   /* File status matching idEntry could not be
+                      obtained.  Treat this as if the file does not
+                      exist.  */
+
+                   children.remove (component);
+
+                   if (id == null)
+                     id = DocumentsContract.getTreeDocumentId (uri);
+
+                   id_return[0] = id;
+
+                   if ((type == null
+                        || type.equals (Document.MIME_TYPE_DIR))
+                       /* ... and type and id currently represent the
+                          penultimate component.  */
+                       && component == components[components.length  - 1])
+                     return -2;
+
+                   return -1;
+                 }
+
+               /* Otherwise, use the cached information.  */
+               id = idEntry.documentId;
+               type = cache.type;
+               children = cache.children;
+               continue;
+             }
+
+           /* Create the tree URI for URI from ID if it exists, or
+              the root otherwise.  */
+
+           if (id == null)
+             id = DocumentsContract.getTreeDocumentId (uri);
+
+           treeUri
+             = DocumentsContract.buildChildDocumentsUriUsingTree (uri, id);
+
+           /* Look for a file in this directory by the name of
+              component.  */
+
+           cursor = resolver.query (treeUri, projection,
+                                    (Document.COLUMN_DISPLAY_NAME
+                                     + " = ?"),
+                                    new String[] { component, },
+                                    null, signal);
+
+           if (cursor == null)
+             return -1;
+
+           /* Find the column numbers for each of the columns that
+              are wanted.  */
+
+           nameColumn
+             = cursor.getColumnIndex (Document.COLUMN_DISPLAY_NAME);
+           idColumn
+             = cursor.getColumnIndex (Document.COLUMN_DOCUMENT_ID);
+           typeColumn
+             = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE);
+
+           if (nameColumn < 0 || idColumn < 0 || typeColumn < 0)
+             return -1;
+
+           next = null;
+
+           while (true)
+             {
+               /* Even though the query selects for a specific
+                  display name, some content providers nevertheless
+                  return every file within the directory.  */
+
+               if (!cursor.moveToNext ())
+                 {
+                   /* If a component has been found, break out of the
+                      loop.  */
+
+                   if (next != null)
+                     break;
+
+                   /* If the last component considered is a
+                      directory... */
+                   if ((type == null
+                        || type.equals (Document.MIME_TYPE_DIR))
+                       /* ... and type and id currently represent the
+                          penultimate component.  */
+                       && component == components[components.length  - 1])
+                     {
+                       /* The cursor is empty.  In this case, return
+                          -2 and the current document ID (belonging
+                          to the previous component) in
+                          ID_RETURN.  */
+
+                       id_return[0] = id;
+
+                       /* But return -1 on the off chance that id is
+                          null.  */
+
+                       if (id == null)
+                         return -1;
+
+                       return -2;
+                     }
+
+                   /* The last component found is not a directory, so
+                      return -1.  */
+                   return -1;
+                 }
+
+               /* So move CURSOR to a row with the right display
+                  name.  */
+
+               name = cursor.getString (nameColumn);
+               newId = cursor.getString (idColumn);
+               newType = cursor.getString (typeColumn);
+
+               /* Any of the three variables above may be NULL if the
+                  column data is of the wrong type depending on how
+                  the Cursor returned is implemented.  */
+
+               if (name == null || newId == null || newType == null)
+                 return -1;
+
+               /* Cache this name, even if it isn't the document
+                  that's being searched for.  */
+
+               cache = cacheChild (toplevel, children, name,
+                                   newId, newType,
+                                   idEntry != null);
+
+               /* Record the desired component once it is located,
+                  but continue reading and caching items from the
+                  cursor.  */
+
+               if (name.equals (component))
+                 {
+                   id = newId;
+                   next = cache.children;
+                   type = newType;
+                 }
+             }
+
+           children = next;
+
+           /* Now close the cursor.  */
+           cursor.close ();
+           cursor = null;
+
+           /* ID may have become NULL if the data is in an invalid
+              format.  */
+           if (id == null)
+             return -1;
+         }
+      }
+    finally
+      {
+       /* If an error is thrown within the block above, let
+          android_saf_exception_check handle it, but make sure the
+          cursor is closed.  */
+
+       if (cursor != null)
+         cursor.close ();
+      }
+
+    /* Here, id is either NULL (meaning the same as TREE_URI), and
+       type is either NULL (in which case id should also be NULL) or
+       the MIME type of the file.  */
+
+    /* First return the ID.  */
+
+    if (id == null)
+      id_return[0] = DocumentsContract.getTreeDocumentId (uri);
+    else
+      id_return[0] = id;
+
+    /* Next, return whether or not this is a directory.  */
+    if (type == null || type.equals (Document.MIME_TYPE_DIR))
+      return 1;
+
+    return 0;
+  }
+
+  /* Find the document ID of the file within TREE_URI designated by
+     NAME.
+
+     NAME is a ``file name'' comprised of the display names of
+     individual files.  Each constituent component prior to the last
+     must name a directory file within TREE_URI.
+
+     Upon success, return 0 or 1 (contingent upon whether or not the
+     last component within NAME is a directory) and place the document
+     ID of the named file in ID_RETURN[0].
+
+     If the designated file can't be located, but each component of
+     NAME up to the last component can and is a directory, return -2
+     and the ID of the last component located in ID_RETURN[0].
+
+     If the designated file can't be located, return -1, or signal one
+     of OperationCanceledException, SecurityException,
+     FileNotFoundException, or UnsupportedOperationException.  */
+
+  public int
+  documentIdFromName (final String tree_uri, final String name,
+                     final String[] id_return)
+  {
+    return runIntFunction (new SafIntFunction () {
+       @Override
+       public int
+       runInt (CancellationSignal signal)
+       {
+         return documentIdFromName1 (tree_uri, name, id_return,
+                                     signal);
+       }
+      });
+  }
+
+  /* The bulk of `statDocument'.  SIGNAL should be a cancelation
+     signal.  */
+
+  private long[]
+  statDocument1 (String uri, String documentId,
+                CancellationSignal signal)
+  {
+    Uri uriObject, tree;
+    String[] projection;
+    long[] stat;
+    Cursor cursor;
+    CacheToplevel toplevel;
+    StatCacheEntry cache;
+
+    tree = Uri.parse (uri);
+
+    if (documentId == null)
+      documentId = DocumentsContract.getTreeDocumentId (tree);
+
+    /* Create a document URI representing DOCUMENTID within URI's
+       authority.  */
+
+    uriObject
+      = DocumentsContract.buildDocumentUriUsingTree (tree, documentId);
+
+    /* See if the file status cache currently contains this
+       document.  */
+
+    toplevel = getCache (tree);
+    cache = toplevel.statCache.get (documentId);
+
+    if (cache == null || !cache.isValid ())
+      {
+       /* Stat this document and enter its information into the
+          cache.  */
+
+       projection = new String[] {
+         Document.COLUMN_FLAGS,
+         Document.COLUMN_LAST_MODIFIED,
+         Document.COLUMN_MIME_TYPE,
+         Document.COLUMN_SIZE,
+       };
+
+       cursor = resolver.query (uriObject, projection, null,
+                                null, null, signal);
+
+       if (cursor == null)
+         return null;
+
+       try
+         {
+           if (!cursor.moveToFirst ())
+             return null;
+
+           cache = cacheFileStatus (documentId, toplevel, cursor);
+         }
+       finally
+         {
+           cursor.close ();
+         }
+
+       /* If cache is still null, return null.  */
+
+       if (cache == null)
+         return null;
+      }
+
+    /* Create the array of file status and populate it with the
+       information within cache.  */
+    stat = new long[3];
+
+    stat[0] |= S_IRUSR;
+    if ((cache.flags & Document.FLAG_SUPPORTS_WRITE) != 0)
+      stat[0] |= S_IWUSR;
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
+       && (cache.flags & Document.FLAG_VIRTUAL_DOCUMENT) != 0)
+      stat[0] |= S_IFCHR;
+
+    stat[1] = cache.size;
+
+    /* Check if this is a directory file.  */
+    if (cache.isDirectory
+       /* Files shouldn't be specials and directories at the same
+          time, but Android doesn't forbid document providers
+          from returning this information.  */
+       && (stat[0] & S_IFCHR) == 0)
+      {
+       /* Since FLAG_SUPPORTS_WRITE doesn't apply to directories,
+          just assume they're writable.  */
+       stat[0] |= S_IFDIR | S_IWUSR | S_IXUSR;
+
+       /* Directory files cannot be modified if
+          FLAG_DIR_SUPPORTS_CREATE is not set.  */
+
+       if ((cache.flags & Document.FLAG_DIR_SUPPORTS_CREATE) == 0)
+         stat[0] &= ~S_IWUSR;
+      }
+
+    /* If this file is neither a character special nor a
+       directory, indicate that it's a regular file.  */
+
+    if ((stat[0] & (S_IFDIR | S_IFCHR)) == 0)
+      stat[0] |= S_IFREG;
+
+    stat[2] = cache.mtime;
+    return stat;
+  }
+
+  /* Return file status for the document designated by the given
+     DOCUMENTID and tree URI.  If DOCUMENTID is NULL, use the document
+     ID in URI itself.
+
+     Value is null upon failure, or an array of longs [MODE, SIZE,
+     MTIM] upon success, where MODE contains the file type and access
+     modes of the file as in `struct stat', SIZE is the size of the
+     file in BYTES or -1 if not known, and MTIM is the time of the
+     last modification to this file in milliseconds since 00:00,
+     January 1st, 1970.
+
+     OperationCanceledException and other typical exceptions may be
+     signaled upon receiving async input or other errors.  */
+
+  public long[]
+  statDocument (final String uri, final String documentId)
+  {
+    return (long[]) runObjectFunction (new SafObjectFunction () {
+       @Override
+       public Object
+       runObject (CancellationSignal signal)
+       {
+         return statDocument1 (uri, documentId, signal);
+       }
+      });
+  }
+
+  /* The bulk of `accessDocument'.  SIGNAL should be a cancellation
+     signal.  */
+
+  private int
+  accessDocument1 (String uri, String documentId, boolean writable,
+                  CancellationSignal signal)
+  {
+    Uri uriObject;
+    String[] projection;
+    int tem, index;
+    String tem1;
+    Cursor cursor;
+    CacheToplevel toplevel;
+    CacheEntry entry;
+
+    uriObject = Uri.parse (uri);
+
+    if (documentId == null)
+      documentId = DocumentsContract.getTreeDocumentId (uriObject);
+
+    /* If WRITABLE is false and the document ID is cached, use its
+       cached value instead.  This speeds up
+       `directory-files-with-attributes' a little.  */
+
+    if (!writable)
+      {
+       toplevel = getCache (uriObject);
+       entry = toplevel.idCache.get (documentId);
+
+       if (entry != null)
+         return 0;
+      }
+
+    /* Create a document URI representing DOCUMENTID within URI's
+       authority.  */
+
+    uriObject
+      = DocumentsContract.buildDocumentUriUsingTree (uriObject, documentId);
+
+    /* Now stat this document.  */
+
+    projection = new String[] {
+      Document.COLUMN_FLAGS,
+      Document.COLUMN_MIME_TYPE,
+    };
+
+    cursor = resolver.query (uriObject, projection, null,
+                            null, null, signal);
+
+    if (cursor == null)
+      return -1;
+
+    try
+      {
+       if (!cursor.moveToFirst ())
+         return -1;
+
+       if (!writable)
+         return 0;
+
+       index = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE);
+       if (index < 0)
+         return -3;
+
+       /* Get the type of this file to check if it's a directory.  */
+       tem1 = cursor.getString (index);
+
+       /* Check if this is a directory file.  */
+       if (tem1.equals (Document.MIME_TYPE_DIR))
+         {
+           /* If so, don't check for FLAG_SUPPORTS_WRITE.
+              Check for FLAG_DIR_SUPPORTS_CREATE instead.  */
+
+           if (!writable)
+             return 0;
+
+           index = cursor.getColumnIndex (Document.COLUMN_FLAGS);
+           if (index < 0)
+             return -3;
+
+           tem = cursor.getInt (index);
+           if ((tem & Document.FLAG_DIR_SUPPORTS_CREATE) == 0)
+             return -3;
+
+           return 0;
+         }
+
+       index = cursor.getColumnIndex (Document.COLUMN_FLAGS);
+       if (index < 0)
+         return -3;
+
+       tem = cursor.getInt (index);
+       if (writable && (tem & Document.FLAG_SUPPORTS_WRITE) == 0)
+         return -3;
+      }
+    finally
+      {
+       /* Close the cursor if an exception occurs.  */
+       cursor.close ();
+      }
+
+    return 0;
+  }
+
+  /* Find out whether Emacs has access to the document designated by
+     the specified DOCUMENTID within the tree URI.  If DOCUMENTID is
+     NULL, use the document ID in URI itself.
+
+     If WRITABLE, also check that the file is writable, which is true
+     if it is either a directory or its flags contains
+     FLAG_SUPPORTS_WRITE.
+
+     Value is 0 if the file is accessible, and one of the following if
+     not:
+
+     -1, if the file does not exist.
+     -2, if WRITABLE and the file is not writable.
+     -3, upon any other error.
+
+     In addition, arbitrary runtime exceptions (such as
+     SecurityException or UnsupportedOperationException) may be
+     thrown.  */
+
+  public int
+  accessDocument (final String uri, final String documentId,
+                 final boolean writable)
+  {
+    return runIntFunction (new SafIntFunction () {
+       @Override
+       public int
+       runInt (CancellationSignal signal)
+       {
+         return accessDocument1 (uri, documentId, writable,
+                                 signal);
+       }
+      });
+  }
+
+  /* The crux of openDocumentDirectory.  SIGNAL must be a cancellation
+     signal.  */
+
+  private Cursor
+  openDocumentDirectory1 (String uri, String documentId,
+                         CancellationSignal signal)
+  {
+    Uri uriObject, tree;
+    Cursor cursor;
+    String projection[];
+    CacheToplevel toplevel;
+
+    tree = uriObject = Uri.parse (uri);
+
+    /* If documentId is not set, use the document ID of the tree URI
+       itself.  */
+
+    if (documentId == null)
+      documentId = DocumentsContract.getTreeDocumentId (uriObject);
+
+    /* Build a URI representing each directory entry within
+       DOCUMENTID.  */
+
+    uriObject
+      = DocumentsContract.buildChildDocumentsUriUsingTree (uriObject,
+                                                          documentId);
+
+    projection = new String [] {
+      Document.COLUMN_DISPLAY_NAME,
+      Document.COLUMN_DOCUMENT_ID,
+      Document.COLUMN_MIME_TYPE,
+      Document.COLUMN_FLAGS,
+      Document.COLUMN_LAST_MODIFIED,
+      Document.COLUMN_SIZE,
+    };
+
+    cursor = resolver.query (uriObject, projection, null, null,
+                            null, signal);
+
+    /* Create a new cache entry tied to this document ID.  */
+
+    if (cursor != null)
+      {
+       toplevel = getCache (tree);
+       cacheDirectoryFromCursor (toplevel, documentId,
+                                 cursor);
+      }
+
+    /* Return the cursor.  */
+    return cursor;
+  }
+
+  /* Open a cursor representing each entry within the directory
+     designated by the specified DOCUMENTID within the tree URI.
+
+     If DOCUMENTID is NULL, use the document ID within URI itself.
+     Value is NULL upon failure.
+
+     In addition, arbitrary runtime exceptions (such as
+     SecurityException or UnsupportedOperationException) may be
+     thrown.  */
+
+  public Cursor
+  openDocumentDirectory (final String uri, final String documentId)
+  {
+    return (Cursor) runObjectFunction (new SafObjectFunction () {
+       @Override
+       public Object
+       runObject (CancellationSignal signal)
+       {
+         return openDocumentDirectory1 (uri, documentId, signal);
+       }
+      });
+  }
+
+  /* The crux of `openDocument'.  SIGNAL must be a cancellation
+     signal.  */
+
+  public ParcelFileDescriptor
+  openDocument1 (String uri, String documentId, boolean write,
+                boolean truncate, CancellationSignal signal)
+    throws Throwable
+  {
+    Uri treeUri, documentUri;
+    String mode;
+    ParcelFileDescriptor fileDescriptor;
+    CacheToplevel toplevel;
+
+    treeUri = Uri.parse (uri);
+
+    /* documentId must be set for this request, since it doesn't make
+       sense to ``open'' the root of the directory tree.  */
+
+    documentUri
+      = DocumentsContract.buildDocumentUriUsingTree (treeUri, documentId);
+
+    /* Select the mode used to open the file.  */
+
+    if (write)
+      {
+       if (truncate)
+         mode = "rwt";
+       else
+         mode = "rw";
+      }
+    else
+      mode = "r";
+
+    fileDescriptor
+      = resolver.openFileDescriptor (documentUri, mode,
+                                    signal);
+
+    /* If a writable file descriptor is requested and TRUNCATE is set,
+       then probe the file descriptor to detect if it is actually
+       readable.  If not, close this file descriptor and reopen it
+       with MODE set to rw; some document providers granting access to
+       Samba shares don't implement rwt, but these document providers
+       invariably truncate the file opened even when the mode is
+       merely rw.
+
+       This may be ascribed to a mix-up in Android's documentation
+       regardin DocumentsProvider: the `openDocument' function is only
+       documented to accept r or rw, whereas the default
+       implementation of the `openFile' function (which documents rwt)
+       delegates to `openDocument'.  */
+
+    if (write && truncate && fileDescriptor != null
+       && !EmacsNative.ftruncate (fileDescriptor.getFd ()))
+      {
+       try
+         {
+           fileDescriptor.closeWithError ("File descriptor requested"
+                                          + " is not writable");
+         }
+       catch (IOException e)
+         {
+           Log.w (TAG, "Leaking unclosed file descriptor " + e);
+         }
+
+       fileDescriptor
+         = resolver.openFileDescriptor (documentUri, "rw", signal);
+
+       /* Try to truncate fileDescriptor just to stay on the safe
+          side.  */
+       if (fileDescriptor != null)
+         EmacsNative.ftruncate (fileDescriptor.getFd ());
+      }
+
+    /* Every time a document is opened, remove it from the file status
+       cache.  */
+    toplevel = getCache (treeUri);
+    toplevel.statCache.remove (documentId);
+
+    return fileDescriptor;
+  }
+
+  /* Open a file descriptor for a file document designated by
+     DOCUMENTID within the document tree identified by URI.  If
+     TRUNCATE and the document already exists, truncate its contents
+     before returning.
+
+     On Android 9.0 and earlier, always open the document in
+     ``read-write'' mode; this instructs the document provider to
+     return a seekable file that is stored on disk and returns correct
+     file status.
+
+     Under newer versions of Android, open the document in a
+     non-writable mode if WRITE is false.  This is possible because
+     these versions allow Emacs to explicitly request a seekable
+     on-disk file.
+
+     Value is NULL upon failure or a parcel file descriptor upon
+     success.  Call `ParcelFileDescriptor.close' on this file
+     descriptor instead of using the `close' system call.
+
+     FileNotFoundException and/or SecurityException and/or
+     UnsupportedOperationException and/or OperationCanceledException
+     may be thrown upon failure.  */
+
+  public ParcelFileDescriptor
+  openDocument (final String uri, final String documentId,
+               final boolean write, final boolean truncate)
+  {
+    Object tem;
+
+    tem = runObjectFunction (new SafObjectFunction () {
+       @Override
+       public Object
+       runObject (CancellationSignal signal)
+         throws Throwable
+       {
+         return openDocument1 (uri, documentId, write, truncate,
+                               signal);
+       }
+      });
+
+    return (ParcelFileDescriptor) tem;
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsSdk11Clipboard.java 
b/java/org/gnu/emacs/EmacsSdk11Clipboard.java
new file mode 100644
index 00000000000..4959ec36eed
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSdk11Clipboard.java
@@ -0,0 +1,290 @@
+/* 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.ContentResolver;
+import android.content.ClipData;
+import android.content.ClipDescription;
+
+import android.content.res.AssetFileDescriptor;
+
+import android.net.Uri;
+
+import android.util.Log;
+
+import android.os.Build;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+/* This class implements EmacsClipboard for Android 3.0 and later
+   systems.  */
+
+public final 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;
+  private ContentResolver resolver;
+
+  public
+  EmacsSdk11Clipboard ()
+  {
+    manager = EmacsService.SERVICE.getClipboardManager ();
+
+    /* The system forbids Emacs from reading clipboard data in the
+       background under Android 10 or later.  */
+
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
+      manager.addPrimaryClipChangedListener (this);
+
+    /* Now obtain the content resolver used to open file
+       descriptors.  */
+
+    resolver = EmacsService.SERVICE.getContentResolver ();
+  }
+
+  @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 ()
+  {
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
+      return -1;
+
+    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;
+  }
+
+  /* Return an array of targets currently provided by the
+     clipboard, or NULL if there are none.  */
+
+  @Override
+  public byte[][]
+  getClipboardTargets ()
+  {
+    ClipData clip;
+    ClipDescription description;
+    byte[][] typeArray;
+    int i;
+
+    /* N.B. that Android calls the clipboard the ``primary clip''; it
+       is not related to the X primary selection.  */
+    clip = manager.getPrimaryClip ();
+    description = clip.getDescription ();
+    i = description.getMimeTypeCount ();
+    typeArray = new byte[i][i];
+
+    try
+      {
+       for (i = 0; i < description.getMimeTypeCount (); ++i)
+         typeArray[i] = description.getMimeType (i).getBytes ("UTF-8");
+      }
+    catch (UnsupportedEncodingException exception)
+      {
+       return null;
+      }
+
+    return typeArray;
+  }
+
+  /* Return the clipboard data for the given target, or NULL if it
+     does not exist.
+
+     Value is normally an array of three longs: the file descriptor,
+     the start offset of the data, and its length; length may be
+     AssetFileDescriptor.UNKOWN_LENGTH, meaning that the data extends
+     from that offset to the end of the file.
+
+     Do not use this function to open text targets; use `getClipboard'
+     for that instead, as it will handle selection data consisting
+     solely of a URI.  */
+
+  @Override
+  public long[]
+  getClipboardData (byte[] target)
+  {
+    ClipData data;
+    String mimeType;
+    int fd;
+    AssetFileDescriptor assetFd;
+    Uri uri;
+    long[] value;
+
+    /* Decode the target given by Emacs.  */
+    try
+      {
+       mimeType = new String (target, "UTF-8");
+      }
+    catch (UnsupportedEncodingException exception)
+      {
+       return null;
+      }
+
+    Log.d (TAG, "getClipboardData: "+ mimeType);
+
+    /* Now obtain the clipboard data and the data corresponding to
+       that MIME type.  */
+
+    data = manager.getPrimaryClip ();
+
+    if (data.getItemCount () < 1)
+      return null;
+
+    try
+      {
+       uri = data.getItemAt (0).getUri ();
+
+       if (uri == null)
+         return null;
+
+       Log.d (TAG, "getClipboardData: "+ uri);
+
+       /* Now open the file descriptor.  */
+       assetFd = resolver.openTypedAssetFileDescriptor (uri, mimeType,
+                                                        null);
+
+       /* Duplicate the file descriptor.  */
+       fd = assetFd.getParcelFileDescriptor ().getFd ();
+       fd = EmacsNative.dup (fd);
+
+       /* Return the relevant information.  */
+       value = new long[] { fd, assetFd.getStartOffset (),
+                            assetFd.getLength (), };
+
+       /* Close the original offset.  */
+       assetFd.close ();
+
+       Log.d (TAG, "getClipboardData: "+ value);
+      }
+    catch (FileNotFoundException e)
+      {
+       return null;
+      }
+    catch (IOException e)
+      {
+       return null;
+      }
+
+    /* Don't return value if the file descriptor couldn't be
+       created.  */
+
+    return fd != -1 ? value : null;
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsSdk23FontDriver.java 
b/java/org/gnu/emacs/EmacsSdk23FontDriver.java
new file mode 100644
index 00000000000..aaba8dbd166
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSdk23FontDriver.java
@@ -0,0 +1,114 @@
+/* Font backend 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.graphics.Paint;
+import android.graphics.Rect;
+
+public final class EmacsSdk23FontDriver extends EmacsSdk7FontDriver
+{
+  private void
+  textExtents1 (Sdk7FontObject font, int code, FontMetrics metrics,
+               Paint paint, Rect bounds)
+  {
+    char[] text;
+
+    text = new char[2];
+    text[0] = (char) code;
+    text[1] = 'c';
+
+    paint.getTextBounds (text, 0, 1, bounds);
+
+    metrics.lbearing = (short) bounds.left;
+    metrics.rbearing = (short) bounds.right;
+    metrics.ascent = (short) -bounds.top;
+    metrics.descent = (short) bounds.bottom;
+    metrics.width
+      = (short) paint.getRunAdvance (text, 0, 1, 0, 1, false, 1);
+  }
+
+  @Override
+  public void
+  textExtents (FontObject font, int code[], FontMetrics fontMetrics)
+  {
+    int i;
+    Paint paintCache;
+    Rect boundsCache;
+    Sdk7FontObject fontObject;
+    char[] text;
+    float width;
+
+    fontObject = (Sdk7FontObject) font;
+    paintCache = fontObject.typeface.typefacePaint;
+    paintCache.setTextSize (fontObject.pixelSize);
+    boundsCache = new Rect ();
+
+    if (code.length == 0)
+      {
+       fontMetrics.lbearing = 0;
+       fontMetrics.rbearing = 0;
+       fontMetrics.ascent = 0;
+       fontMetrics.descent = 0;
+       fontMetrics.width = 0;
+      }
+    else if (code.length == 1)
+      textExtents1 ((Sdk7FontObject) font, code[0], fontMetrics,
+                   paintCache, boundsCache);
+    else
+      {
+       text = new char[code.length + 1];
+
+       for (i = 0; i < code.length; ++i)
+         text[i] = (char) code[i];
+
+       text[code.length] = 'c';
+
+       paintCache.getTextBounds (text, 0, code.length,
+                                 boundsCache);
+       width = paintCache.getRunAdvance (text, 0, code.length, 0,
+                                         code.length,
+                                         false, code.length);
+
+       fontMetrics.lbearing = (short) boundsCache.left;
+       fontMetrics.rbearing = (short) boundsCache.right;
+       fontMetrics.ascent = (short) -boundsCache.top;
+       fontMetrics.descent = (short) boundsCache.bottom;
+       fontMetrics.width = (short) width;
+      }
+  }
+
+  @Override
+  public int
+  hasChar (FontSpec font, char charCode)
+  {
+    Sdk7FontObject fontObject;
+    Paint paint;
+
+    if (font instanceof Sdk7FontObject)
+      {
+       fontObject = (Sdk7FontObject) font;
+       paint = fontObject.typeface.typefacePaint;
+      }
+    else
+      paint = ((Sdk7FontEntity) font).typeface.typefacePaint;
+
+    return paint.hasGlyph (String.valueOf (charCode)) ? 1 : 0;
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java 
b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
new file mode 100644
index 00000000000..97969585d16
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
@@ -0,0 +1,539 @@
+/* Font backend 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 java.io.File;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.Canvas;
+
+import android.util.Log;
+
+public class EmacsSdk7FontDriver extends EmacsFontDriver
+{
+  private static final String TOFU_STRING = "\uDB3F\uDFFD";
+  private static final String EM_STRING   = "m";
+  private static final String TAG        = "EmacsSdk7FontDriver";
+
+  protected static final class Sdk7Typeface
+  {
+    /* The typeface and paint.  */
+    public Typeface typeface;
+    public Paint typefacePaint;
+    public String familyName;
+    public int slant, width, weight, spacing;
+
+    public
+    Sdk7Typeface (String fileName, Typeface typeface)
+    {
+      String style, testString;
+      int index, measured, i;
+      float[] widths;
+
+      slant = NORMAL;
+      weight = REGULAR;
+      width = UNSPECIFIED;
+      spacing = PROPORTIONAL;
+
+      this.typeface = typeface;
+
+      typefacePaint = new Paint ();
+      typefacePaint.setAntiAlias (true);
+      typefacePaint.setTypeface (typeface);
+
+      /* For the calls to measureText below.  */
+      typefacePaint.setTextSize (10.0f);
+
+      /* Parse the file name into some useful data.  First, strip off
+        the extension.  */
+      fileName = fileName.split ("\\.", 2)[0];
+
+      /* Next, split the file name by dashes.  Everything before the
+        last dash is part of the family name.  */
+      index = fileName.lastIndexOf ("-");
+
+      if (index > 0)
+       {
+         style = fileName.substring (index + 1, fileName.length ());
+         familyName = fileName.substring (0, index);
+
+         /* Look for something describing the weight.  */
+         if (style.contains ("Thin"))
+           weight = THIN;
+         else if (style.contains ("UltraLight"))
+           weight = ULTRA_LIGHT;
+         else if (style.contains ("SemiLight"))
+           weight = SEMI_LIGHT;
+         else if (style.contains ("Light"))
+           weight = LIGHT;
+         else if (style.contains ("Medium"))
+           weight = MEDIUM;
+         else if (style.contains ("SemiBold"))
+           weight = SEMI_BOLD;
+         else if (style.contains ("ExtraBold"))
+           weight = EXTRA_BOLD;
+         else if (style.contains ("Bold"))
+           weight = BOLD;
+         else if (style.contains ("Black"))
+           weight = BLACK;
+         else if (style.contains ("UltraHeavy"))
+           weight = ULTRA_HEAVY;
+
+         /* And the slant.  */
+         if (style.contains ("ReverseOblique"))
+           slant = OBLIQUE;
+         else if (style.contains ("ReverseItalic"))
+           slant = REVERSE_ITALIC;
+         else if (style.contains ("Italic"))
+           slant = ITALIC;
+         else if (style.contains ("Oblique"))
+           slant = OBLIQUE;
+
+         /* Finally, the width.  */
+         if (style.contains ("UltraCondensed"))
+           width = ULTRA_CONDENSED;
+         else if (style.contains ("ExtraCondensed"))
+           width = EXTRA_CONDENSED;
+         else if (style.contains ("SemiCondensed"))
+           width = SEMI_CONDENSED;
+         else if (style.contains ("Condensed"))
+           width = CONDENSED;
+         else if (style.contains ("SemiExpanded"))
+           width = SEMI_EXPANDED;
+         else if (style.contains ("ExtraExpanded"))
+           width = EXTRA_EXPANDED;
+         else if (style.contains ("UltraExpanded"))
+           width = ULTRA_EXPANDED;
+         else if (style.contains ("Expanded"))
+           width = EXPANDED;
+
+         /* Guess the spacing information.  */
+         testString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+         widths = new float[testString.length ()];
+
+         measured = typefacePaint.getTextWidths (testString,
+                                                 0, testString.length (),
+                                                 widths);
+         spacing = MONO;
+         for (i = 0; i < measured; ++i)
+           {
+             if (i != 0 && widths[i - 1] != widths[i])
+               /* This isn't a monospace font.  */
+               spacing = PROPORTIONAL;
+           }
+       }
+      else
+       familyName = fileName;
+    }
+
+    @Override
+    public String
+    toString ()
+    {
+      return ("Sdk7Typeface ("
+             + String.valueOf (familyName) + ", "
+             + String.valueOf (slant) + ", "
+             + String.valueOf (width) + ", "
+             + String.valueOf (weight) + ", "
+             + String.valueOf (spacing) + ")");
+    }
+  };
+
+  protected static final class Sdk7FontEntity extends FontEntity
+  {
+    /* The typeface.  */
+    public Sdk7Typeface typeface;
+
+    public
+    Sdk7FontEntity (Sdk7Typeface typeface)
+    {
+      foundry = "Google";
+      family = typeface.familyName;
+      adstyle = null;
+      weight = typeface.weight;
+      slant = typeface.slant;
+      spacing = typeface.spacing;
+      width = typeface.width;
+      dpi = Math.round (EmacsService.SERVICE.metrics.scaledDensity * 160f);
+
+      this.typeface = typeface;
+    }
+  };
+
+  protected final class Sdk7FontObject extends FontObject
+  {
+    /* The typeface.  */
+    public Sdk7Typeface typeface;
+
+    public
+    Sdk7FontObject (Sdk7Typeface typeface, int pixelSize)
+    {
+      float totalWidth;
+      String testWidth, testString;
+
+      this.typeface = typeface;
+      this.pixelSize = pixelSize;
+
+      family = typeface.familyName;
+      adstyle = null;
+      weight = typeface.weight;
+      slant = typeface.slant;
+      spacing = typeface.spacing;
+      width = typeface.width;
+      dpi = Math.round (EmacsService.SERVICE.metrics.scaledDensity * 160f);
+
+      /* Compute the ascent and descent.  */
+      typeface.typefacePaint.setTextSize (pixelSize);
+      ascent
+       = Math.round (-typeface.typefacePaint.ascent ());
+      descent
+       = Math.round (typeface.typefacePaint.descent ());
+
+      /* Compute the average width.  */
+      testString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+      totalWidth = typeface.typefacePaint.measureText (testString);
+
+      if (totalWidth > 0)
+       avgwidth = Math.round (totalWidth
+                              / testString.length ());
+
+      /* Android doesn't expose the font average width and height
+        information, so this will have to do.  */
+      minWidth = maxWidth = avgwidth;
+
+      /* This is different from avgwidth in the font spec! */
+      averageWidth = avgwidth;
+
+      /* Set the space width.  */
+      totalWidth = typeface.typefacePaint.measureText (" ");
+      spaceWidth = Math.round (totalWidth);
+
+      /* Set the height and default ascent.  */
+      height = ascent + descent;
+      defaultAscent = ascent;
+    }
+  };
+
+  private String[] fontFamilyList;
+  private Sdk7Typeface[] typefaceList;
+  private Sdk7Typeface fallbackTypeface;
+
+  public
+  EmacsSdk7FontDriver ()
+  {
+    int i;
+    File systemFontsDirectory, fontFile;
+    Typeface typeface;
+
+    systemFontsDirectory = new File ("/system/fonts");
+
+    fontFamilyList = systemFontsDirectory.list ();
+
+    /* If that returned null, replace it with an empty array.  */
+    fontFamilyList = new String[0];
+
+    typefaceList = new Sdk7Typeface[fontFamilyList.length + 3];
+
+    /* It would be nice to avoid opening each and every font upon
+       startup.  But that doesn't seem to be possible on
+       Android.  */
+
+    for (i = 0; i < fontFamilyList.length; ++i)
+      {
+       fontFile = new File (systemFontsDirectory,
+                            fontFamilyList[i]);
+       typeface = Typeface.createFromFile (fontFile);
+       typefaceList[i] = new Sdk7Typeface (fontFile.getName (),
+                                           typeface);
+      }
+
+    /* Initialize the default monospace and serif typefaces.  */
+    fallbackTypeface = new Sdk7Typeface ("monospace",
+                                        Typeface.MONOSPACE);
+    typefaceList[fontFamilyList.length] = fallbackTypeface;
+
+    fallbackTypeface = new Sdk7Typeface ("Monospace",
+                                        Typeface.MONOSPACE);
+    typefaceList[fontFamilyList.length + 1] = fallbackTypeface;
+
+    fallbackTypeface = new Sdk7Typeface ("Sans Serif",
+                                        Typeface.DEFAULT);
+    typefaceList[fontFamilyList.length + 2] = fallbackTypeface;
+  }
+
+  private boolean
+  checkMatch (Sdk7Typeface typeface, FontSpec fontSpec)
+  {
+    if (fontSpec.family != null
+       && !fontSpec.family.equals (typeface.familyName))
+      return false;
+
+    if (fontSpec.slant != null
+       && !fontSpec.weight.equals (typeface.weight))
+      return false;
+
+    if (fontSpec.spacing != null
+       && !fontSpec.spacing.equals (typeface.spacing))
+      return false;
+
+    if (fontSpec.weight != null
+       && !fontSpec.weight.equals (typeface.weight))
+      return false;
+
+    if (fontSpec.width != null
+       && !fontSpec.width.equals (typeface.width))
+      return false;
+
+    return true;
+  }
+
+  @Override
+  public FontEntity[]
+  list (FontSpec fontSpec)
+  {
+    LinkedList<FontEntity> list;
+    int i;
+
+    list = new LinkedList<FontEntity> ();
+
+    for (i = 0; i < typefaceList.length; ++i)
+      {
+       if (checkMatch (typefaceList[i], fontSpec))
+         list.add (new Sdk7FontEntity (typefaceList[i]));
+      }
+
+    return list.toArray (new FontEntity[0]);
+  }
+
+  @Override
+  public FontEntity
+  match (FontSpec fontSpec)
+  {
+    FontEntity[] entities;
+    int i;
+
+    entities = this.list (fontSpec);
+
+    if (entities.length == 0)
+      return new Sdk7FontEntity (fallbackTypeface);
+
+    return entities[0];
+  }
+
+  @Override
+  public String[]
+  listFamilies ()
+  {
+    return fontFamilyList;
+  }
+
+  @Override
+  public FontObject
+  openFont (FontEntity fontEntity, int pixelSize)
+  {
+    return new Sdk7FontObject (((Sdk7FontEntity) fontEntity).typeface,
+                              pixelSize);
+  }
+
+  @Override
+  public int
+  hasChar (FontSpec font, char charCode)
+  {
+    float missingGlyphWidth, width;
+    Rect rect1, rect2;
+    Paint paint;
+    Sdk7FontObject fontObject;
+
+    if (font instanceof Sdk7FontObject)
+      {
+       fontObject = (Sdk7FontObject) font;
+       paint = fontObject.typeface.typefacePaint;
+      }
+    else
+      paint = ((Sdk7FontEntity) font).typeface.typefacePaint;
+
+    paint.setTextSize (10);
+
+    if (Character.isWhitespace (charCode))
+      return 1;
+
+    missingGlyphWidth = paint.measureText (TOFU_STRING);
+    width = paint.measureText ("" + charCode);
+
+    if (width == 0f)
+      return 0;
+
+    if (width != missingGlyphWidth)
+      return 1;
+
+    rect1 = new Rect ();
+    rect2 = new Rect ();
+
+    paint.getTextBounds (TOFU_STRING, 0, TOFU_STRING.length (),
+                        rect1);
+    paint.getTextBounds ("" + charCode, 0, 1, rect2);
+    return rect1.equals (rect2) ? 0 : 1;
+  }
+
+  private void
+  textExtents1 (Sdk7FontObject font, int code, FontMetrics metrics,
+               Paint paint, Rect bounds)
+  {
+    char[] text;
+
+    text = new char[1];
+    text[0] = (char) code;
+
+    paint.getTextBounds (text, 0, 1, bounds);
+
+    /* bounds is the bounding box of the glyph corresponding to CODE.
+       Translate these into XCharStruct values.
+
+       The origin is at 0, 0, and lbearing is the distance counting
+       rightwards from the origin to the left most pixel in the glyph
+       raster.  rbearing is the distance between the origin and the
+       rightmost pixel in the glyph raster.  ascent is the distance
+       counting upwards between the the topmost pixel in the glyph
+       raster.  descent is the distance (once again counting
+       downwards) between the origin and the bottommost pixel in the
+       glyph raster.
+
+       width is the distance between the origin and the origin of any
+       character to the right.  */
+
+    metrics.lbearing = (short) bounds.left;
+    metrics.rbearing = (short) bounds.right;
+    metrics.ascent = (short) -bounds.top;
+    metrics.descent = (short) bounds.bottom;
+    metrics.width = (short) paint.measureText ("" + text[0]);
+  }
+
+  @Override
+  public void
+  textExtents (FontObject font, int code[], FontMetrics fontMetrics)
+  {
+    int i;
+    Paint paintCache;
+    Rect boundsCache;
+    Sdk7FontObject fontObject;
+    char[] text;
+    float width;
+
+    fontObject = (Sdk7FontObject) font;
+    paintCache = fontObject.typeface.typefacePaint;
+    paintCache.setTextSize (fontObject.pixelSize);
+    boundsCache = new Rect ();
+
+    if (code.length == 0)
+      {
+       fontMetrics.lbearing = 0;
+       fontMetrics.rbearing = 0;
+       fontMetrics.ascent = 0;
+       fontMetrics.descent = 0;
+       fontMetrics.width = 0;
+      }
+    else if (code.length == 1)
+      textExtents1 ((Sdk7FontObject) font, code[0], fontMetrics,
+                   paintCache, boundsCache);
+    else
+      {
+       text = new char[code.length];
+
+       for (i = 0; i < code.length; ++i)
+         text[i] = (char) code[i];
+
+       paintCache.getTextBounds (text, 0, code.length,
+                                 boundsCache);
+       width = paintCache.measureText (text, 0, code.length);
+
+       fontMetrics.lbearing = (short) boundsCache.left;
+       fontMetrics.rbearing = (short) boundsCache.right;
+       fontMetrics.ascent = (short) -boundsCache.top;
+       fontMetrics.descent = (short) boundsCache.bottom;
+       fontMetrics.width = (short) Math.round (width);
+      }
+  }
+
+  @Override
+  public int
+  encodeChar (FontObject fontObject, char charCode)
+  {
+    return charCode;
+  }
+
+  @Override
+  public int
+  draw (FontObject fontObject, EmacsGC gc, EmacsDrawable drawable,
+       int[] chars, int x, int y, int backgroundWidth,
+       boolean withBackground)
+  {
+    Rect backgroundRect, bounds;
+    Sdk7FontObject sdk7FontObject;
+    char[] charsArray;
+    int i;
+    Canvas canvas;
+    Paint paint;
+
+    sdk7FontObject = (Sdk7FontObject) fontObject;
+    charsArray = new char[chars.length];
+
+    for (i = 0; i < chars.length; ++i)
+      charsArray[i] = (char) chars[i];
+
+    backgroundRect = new Rect ();
+    backgroundRect.top = y - sdk7FontObject.ascent;
+    backgroundRect.left = x;
+    backgroundRect.right = x + backgroundWidth;
+    backgroundRect.bottom = y + sdk7FontObject.descent;
+
+    canvas = drawable.lockCanvas (gc);
+
+    if (canvas == null)
+      return 0;
+
+    paint = gc.gcPaint;
+    paint.setStyle (Paint.Style.FILL);
+
+    if (withBackground)
+      {
+       paint.setColor (gc.background | 0xff000000);
+       canvas.drawRect (backgroundRect, paint);
+       paint.setColor (gc.foreground | 0xff000000);
+      }
+
+    paint.setTextSize (sdk7FontObject.pixelSize);
+    paint.setTypeface (sdk7FontObject.typeface.typeface);
+    paint.setAntiAlias (true);
+    canvas.drawText (charsArray, 0, chars.length, x, y, paint);
+
+    bounds = new Rect ();
+    paint.getTextBounds (charsArray, 0, chars.length, bounds);
+    bounds.offset (x, y);
+    bounds.union (backgroundRect);
+    drawable.damageRect (bounds);
+    paint.setAntiAlias (false);
+    return 1;
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsSdk8Clipboard.java 
b/java/org/gnu/emacs/EmacsSdk8Clipboard.java
new file mode 100644
index 00000000000..9622641810f
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSdk8Clipboard.java
@@ -0,0 +1,147 @@
+/* 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 instead of just the legacy
+   ClipboardManager class 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 final 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;
+  }
+
+  /* Return an array of targets currently provided by the
+     clipboard, or NULL if there are none.  */
+
+  @Override
+  public byte[][]
+  getClipboardTargets ()
+  {
+    return null;
+  }
+
+  /* Return the clipboard data for the given target, or NULL if it
+     does not exist.
+
+     Value is normally an array of three longs: the file descriptor,
+     the start offset of the data, and its length; length may be
+     AssetFileDescriptor.UNKOWN_LENGTH, meaning that the data extends
+     from that offset to the end of the file.
+
+     Do not use this function to open text targets; use `getClipboard'
+     for that instead, as it will handle selection data consisting
+     solely of a URI.  */
+
+  @Override
+  public long[]
+  getClipboardData (byte[] target)
+  {
+    return null;
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsService.java 
b/java/org/gnu/emacs/EmacsService.java
new file mode 100644
index 00000000000..d91d8f66009
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -0,0 +1,1820 @@
+/* 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 java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import android.database.Cursor;
+
+import android.graphics.Matrix;
+import android.graphics.Point;
+
+import android.webkit.MimeTypeMap;
+
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.ExtractedText;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.NotificationChannel;
+import android.app.Service;
+
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.UriPermission;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager;
+
+import android.content.res.AssetManager;
+
+import android.hardware.input.InputManager;
+
+import android.net.Uri;
+
+import android.os.BatteryManager;
+import android.os.Build;
+import android.os.Looper;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.Vibrator;
+import android.os.VibratorManager;
+import android.os.VibrationEffect;
+
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
+
+import android.util.Log;
+import android.util.DisplayMetrics;
+
+import android.widget.Toast;
+
+/* EmacsService is the service that starts the thread running Emacs
+   and handles requests by that Emacs instance.  */
+
+public final class EmacsService extends Service
+{
+  public static final String TAG = "EmacsService";
+
+  /* The started Emacs service object.  */
+  public static EmacsService SERVICE;
+
+  /* If non-NULL, an extra argument to pass to
+     `android_emacs_init'.  */
+  public static String extraStartupArgument;
+
+  /* The thread running Emacs C code.  */
+  private EmacsThread thread;
+
+  /* Handler used to run tasks on the main thread.  */
+  private Handler handler;
+
+  /* Content resolver used to access URIs.  */
+  private ContentResolver resolver;
+
+  /* Keep this in synch with androidgui.h.  */
+  public static final int IC_MODE_NULL   = 0;
+  public static final int IC_MODE_ACTION = 1;
+  public static final int IC_MODE_TEXT   = 2;
+
+  /* Display metrics used by font backends.  */
+  public DisplayMetrics metrics;
+
+  /* Flag that says whether or not to print verbose debugging
+     information when responding to an input method.  */
+  public static final boolean DEBUG_IC = false;
+
+  /* Flag that says whether or not to stringently check that only the
+     Emacs thread is performing drawing calls.  */
+  private static final boolean DEBUG_THREADS = false;
+
+  /* Atomic integer used for synchronization between
+     icBeginSynchronous/icEndSynchronous and viewGetSelection.
+
+     Value is 0 if no query is in progress, 1 if viewGetSelection is
+     being called, and 2 if icBeginSynchronous was called.  */
+  public static final AtomicInteger servicingQuery;
+
+  /* Thread used to query document providers, or null if it hasn't
+     been created yet.  */
+  private EmacsSafThread storageThread;
+
+  static
+  {
+    servicingQuery = new AtomicInteger ();
+  };
+
+  /* Return the directory leading to the directory in which native
+     library files are stored on behalf of CONTEXT.  */
+
+  public static String
+  getLibraryDirectory (Context context)
+  {
+    int apiLevel;
+
+    apiLevel = Build.VERSION.SDK_INT;
+
+    if (apiLevel >= Build.VERSION_CODES.GINGERBREAD)
+      return context.getApplicationInfo ().nativeLibraryDir;
+
+    return context.getApplicationInfo ().dataDir + "/lib";
+  }
+
+  @Override
+  public int
+  onStartCommand (Intent intent, int flags, int startId)
+  {
+    Notification notification;
+    NotificationManager manager;
+    NotificationChannel channel;
+    String infoBlurb;
+    Object tem;
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+      {
+       tem = getSystemService (Context.NOTIFICATION_SERVICE);
+       manager = (NotificationManager) tem;
+       infoBlurb = ("This notification is displayed to keep Emacs"
+                    + " running while it is in the background.  You"
+                    + " may disable it if you want;"
+                    + " see (emacs)Android Environment.");
+       channel
+         = new NotificationChannel ("emacs", "Emacs persistent notification",
+                                    NotificationManager.IMPORTANCE_DEFAULT);
+       manager.createNotificationChannel (channel);
+       notification = (new Notification.Builder (this, "emacs")
+                       .setContentTitle ("Emacs")
+                       .setContentText (infoBlurb)
+                       .setSmallIcon (android.R.drawable.sym_def_app_icon)
+                       .build ());
+       manager.notify (1, notification);
+       startForeground (1, notification);
+      }
+
+    return START_NOT_STICKY;
+  }
+
+  @Override
+  public IBinder
+  onBind (Intent intent)
+  {
+    return null;
+  }
+
+  @SuppressWarnings ("deprecation")
+  private String
+  getApkFile ()
+  {
+    PackageManager manager;
+    ApplicationInfo info;
+
+    manager = getPackageManager ();
+
+    try
+      {
+       if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
+         info = manager.getApplicationInfo ("org.gnu.emacs", 0);
+       else
+         info = manager.getApplicationInfo ("org.gnu.emacs",
+                                            ApplicationInfoFlags.of (0));
+
+       /* Return an empty string upon failure.  */
+
+       if (info.sourceDir != null)
+         return info.sourceDir;
+
+       return "";
+      }
+    catch (Exception e)
+      {
+       return "";
+      }
+  }
+
+  @Override
+  public void
+  onCreate ()
+  {
+    final AssetManager manager;
+    Context app_context;
+    final String filesDir, libDir, cacheDir, classPath;
+    final double pixelDensityX;
+    final double pixelDensityY;
+    final double scaledDensity;
+    double tempScaledDensity;
+
+    SERVICE = this;
+    handler = new Handler (Looper.getMainLooper ());
+    manager = getAssets ();
+    app_context = getApplicationContext ();
+    metrics = getResources ().getDisplayMetrics ();
+    pixelDensityX = metrics.xdpi;
+    pixelDensityY = metrics.ydpi;
+    tempScaledDensity = ((metrics.scaledDensity
+                         / metrics.density)
+                        * pixelDensityX);
+    resolver = getContentResolver ();
+
+    /* If the density used to compute the text size is lesser than
+       160, there's likely a bug with display density computation.
+       Reset it to 160 in that case.
+
+       Note that Android uses 160 ``dpi'' as the density where 1 point
+       corresponds to 1 pixel, not 72 or 96 as used elsewhere.  This
+       difference is codified in PT_PER_INCH defined in font.h.  */
+
+    if (tempScaledDensity < 160)
+      tempScaledDensity = 160;
+
+    /* scaledDensity is const as required to refer to it from within
+       the nested function below.  */
+    scaledDensity = tempScaledDensity;
+
+    try
+      {
+       /* Configure Emacs with the asset manager and other necessary
+          parameters.  */
+       filesDir = app_context.getFilesDir ().getCanonicalPath ();
+       libDir = getLibraryDirectory (this);
+       cacheDir = app_context.getCacheDir ().getCanonicalPath ();
+
+       /* Now provide this application's apk file, so a recursive
+          invocation of app_process (through android-emacs) can
+          find EmacsNoninteractive.  */
+       classPath = getApkFile ();
+
+       Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir
+              + ", libDir = " + libDir + ", and classPath = " + classPath
+              + "; fileToOpen = " + EmacsOpenActivity.fileToOpen
+              + "; display density: " + pixelDensityX + " by "
+              + pixelDensityY + " scaled to " + scaledDensity);
+
+       /* Start the thread that runs Emacs.  */
+       thread = new EmacsThread (this, new Runnable () {
+           @Override
+           public void
+           run ()
+           {
+             EmacsNative.setEmacsParams (manager, filesDir, libDir,
+                                         cacheDir, (float) pixelDensityX,
+                                         (float) pixelDensityY,
+                                         (float) scaledDensity,
+                                         classPath, EmacsService.this,
+                                         Build.VERSION.SDK_INT);
+           }
+         }, extraStartupArgument,
+         /* If any file needs to be opened, open it now.  */
+         EmacsOpenActivity.fileToOpen);
+       thread.start ();
+      }
+    catch (IOException exception)
+      {
+       EmacsNative.emacsAbort ();
+       return;
+      }
+  }
+
+
+
+  /* Functions from here on must only be called from the Emacs
+     thread.  */
+
+  public void
+  runOnUiThread (Runnable runnable)
+  {
+    handler.post (runnable);
+  }
+
+  public EmacsView
+  getEmacsView (final EmacsWindow window, final int visibility,
+               final boolean isFocusedByDefault)
+  {
+    Runnable runnable;
+    final EmacsHolder<EmacsView> view;
+
+    view = new EmacsHolder<EmacsView> ();
+
+    runnable = new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         synchronized (this)
+           {
+             view.thing = new EmacsView (window);
+             view.thing.setVisibility (visibility);
+
+             /* The following function is only present on Android 26
+                or later.  */
+             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+               view.thing.setFocusedByDefault (isFocusedByDefault);
+
+             notify ();
+           }
+       }
+      };
+
+    syncRunnable (runnable);
+    return view.thing;
+  }
+
+  public void
+  getLocationOnScreen (final EmacsView view, final int[] coordinates)
+  {
+    Runnable runnable;
+
+    runnable = new Runnable () {
+       public void
+       run ()
+       {
+         synchronized (this)
+           {
+             view.getLocationOnScreen (coordinates);
+             notify ();
+           }
+       }
+      };
+
+    syncRunnable (runnable);
+  }
+
+
+
+  public static void
+  checkEmacsThread ()
+  {
+    if (DEBUG_THREADS)
+      {
+       if (Thread.currentThread () instanceof EmacsThread)
+         return;
+
+       throw new RuntimeException ("Emacs thread function"
+                                   + " called from other thread!");
+      }
+  }
+
+  /* These drawing functions must only be called from the Emacs
+     thread.  */
+
+  public void
+  fillRectangle (EmacsDrawable drawable, EmacsGC gc,
+                int x, int y, int width, int height)
+  {
+    checkEmacsThread ();
+    EmacsFillRectangle.perform (drawable, gc, x, y,
+                               width, height);
+  }
+
+  public void
+  fillPolygon (EmacsDrawable drawable, EmacsGC gc,
+              Point points[])
+  {
+    checkEmacsThread ();
+    EmacsFillPolygon.perform (drawable, gc, points);
+  }
+
+  public void
+  drawRectangle (EmacsDrawable drawable, EmacsGC gc,
+                int x, int y, int width, int height)
+  {
+    checkEmacsThread ();
+    EmacsDrawRectangle.perform (drawable, gc, x, y,
+                               width, height);
+  }
+
+  public void
+  drawLine (EmacsDrawable drawable, EmacsGC gc,
+           int x, int y, int x2, int y2)
+  {
+    checkEmacsThread ();
+    EmacsDrawLine.perform (drawable, gc, x, y,
+                          x2, y2);
+  }
+
+  public void
+  drawPoint (EmacsDrawable drawable, EmacsGC gc,
+            int x, int y)
+  {
+    checkEmacsThread ();
+    EmacsDrawPoint.perform (drawable, gc, x, y);
+  }
+
+  public void
+  clearWindow (EmacsWindow window)
+  {
+    checkEmacsThread ();
+    window.clearWindow ();
+  }
+
+  public void
+  clearArea (EmacsWindow window, int x, int y, int width,
+            int height)
+  {
+    checkEmacsThread ();
+    window.clearArea (x, y, width, height);
+  }
+
+  @SuppressWarnings ("deprecation")
+  public void
+  ringBell ()
+  {
+    Vibrator vibrator;
+    VibrationEffect effect;
+    VibratorManager vibratorManager;
+    Object tem;
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
+      {
+       tem = getSystemService (Context.VIBRATOR_MANAGER_SERVICE);
+       vibratorManager = (VibratorManager) tem;
+        vibrator = vibratorManager.getDefaultVibrator ();
+      }
+    else
+      vibrator
+       = (Vibrator) getSystemService (Context.VIBRATOR_SERVICE);
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+      {
+       effect
+         = VibrationEffect.createOneShot (50,
+                                          VibrationEffect.DEFAULT_AMPLITUDE);
+       vibrator.vibrate (effect);
+      }
+    else
+      vibrator.vibrate (50);
+  }
+
+  public short[]
+  queryTree (EmacsWindow window)
+  {
+    short[] array;
+    List<EmacsWindow> windowList;
+    int i;
+
+    if (window == null)
+      /* Just return all the windows without a parent.  */
+      windowList = EmacsWindowAttachmentManager.MANAGER.copyWindows ();
+    else
+      windowList = window.children;
+
+    array = new short[windowList.size () + 1];
+    i = 1;
+
+    array[0] = (window == null
+               ? 0 : (window.parent != null
+                      ? window.parent.handle : 0));
+
+    for (EmacsWindow treeWindow : windowList)
+      array[i++] = treeWindow.handle;
+
+    return array;
+  }
+
+  public int
+  getScreenWidth (boolean mmWise)
+  {
+    DisplayMetrics metrics;
+
+    metrics = getResources ().getDisplayMetrics ();
+
+    if (!mmWise)
+      return metrics.widthPixels;
+    else
+      return (int) ((metrics.widthPixels / metrics.xdpi) * 2540.0);
+  }
+
+  public int
+  getScreenHeight (boolean mmWise)
+  {
+    DisplayMetrics metrics;
+
+    metrics = getResources ().getDisplayMetrics ();
+
+    if (!mmWise)
+      return metrics.heightPixels;
+    else
+      return (int) ((metrics.heightPixels / metrics.ydpi) * 2540.0);
+  }
+
+  public boolean
+  detectMouse ()
+  {
+    InputManager manager;
+    InputDevice device;
+    int[] ids;
+    int i;
+
+    if (Build.VERSION.SDK_INT
+       /* Android 4.0 and earlier don't support mouse input events at
+          all.  */
+       < Build.VERSION_CODES.JELLY_BEAN)
+      return false;
+
+    manager = (InputManager) getSystemService (Context.INPUT_SERVICE);
+    ids = manager.getInputDeviceIds ();
+
+    for (i = 0; i < ids.length; ++i)
+      {
+       device = manager.getInputDevice (ids[i]);
+
+       if (device == null)
+         continue;
+
+       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+         {
+           if (device.supportsSource (InputDevice.SOURCE_MOUSE))
+             return true;
+         }
+       else
+         {
+           /* `supportsSource' is only present on API level 21 and
+              later, but earlier versions provide a bit mask
+              containing each supported source.  */
+
+           if ((device.getSources () & InputDevice.SOURCE_MOUSE) != 0)
+             return true;
+         }
+      }
+
+    return false;
+  }
+
+  public String
+  nameKeysym (int keysym)
+  {
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1)
+      return KeyEvent.keyCodeToString (keysym);
+
+    return String.valueOf (keysym);
+  }
+
+
+
+  /* Start the Emacs service if necessary.  On Android 26 and up,
+     start Emacs as a foreground service with a notification, to avoid
+     it being killed by the system.
+
+     On older systems, simply start it as a normal background
+     service.  */
+
+  public static void
+  startEmacsService (Context context)
+  {
+    if (EmacsService.SERVICE == null)
+      {
+       if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+         /* Start the Emacs service now.  */
+         context.startService (new Intent (context,
+                                           EmacsService.class));
+       else
+         /* Display the permanant notification and start Emacs as a
+            foreground service.  */
+         context.startForegroundService (new Intent (context,
+                                                     EmacsService.class));
+      }
+  }
+
+  /* Ask the system to open the specified URL in an application that
+     understands how to open it.
+
+     If SEND, tell the system to also open applications that can
+     ``send'' the URL (through mail, for example), instead of only
+     those that can view the URL.
+
+     Value is NULL upon success, or a string describing the error
+     upon failure.  */
+
+  public String
+  browseUrl (String url, boolean send)
+  {
+    Intent intent;
+    Uri uri;
+
+    try
+      {
+       /* Parse the URI.  */
+       if (!send)
+         {
+           uri = Uri.parse (url);
+
+           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
+             {
+               /* On Android 4.4 and later, check if URI is actually
+                  a file name.  If so, rewrite it into a content
+                  provider URI, so that it can be accessed by other
+                  programs.  */
+
+               if (uri.getScheme ().equals ("file")
+                   && uri.getPath () != null)
+                 uri
+                   = DocumentsContract.buildDocumentUri ("org.gnu.emacs",
+                                                         uri.getPath ());
+             }
+
+           Log.d (TAG, ("browseUri: browsing " + url
+                        + " --> " + uri.getPath ()
+                        + " --> " + uri));
+
+           intent = new Intent (Intent.ACTION_VIEW, uri);
+           intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK
+                            | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+         }
+       else
+         {
+           intent = new Intent (Intent.ACTION_SEND);
+           intent.setType ("text/plain");
+           intent.putExtra (Intent.EXTRA_SUBJECT, "Sharing link");
+           intent.putExtra (Intent.EXTRA_TEXT, url);
+
+           /* Display a list of programs able to send this URL.  */
+           intent = Intent.createChooser (intent, "Send");
+
+           /* Apparently flags need to be set after a choser is
+              created.  */
+           intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK);
+         }
+
+       startActivity (intent);
+      }
+    catch (Exception e)
+      {
+       return e.toString ();
+      }
+
+    return null;
+  }
+
+  /* Get a SDK 11 ClipboardManager.
+
+     Android 4.0.x requires that this be called from the main
+     thread.  */
+
+  public ClipboardManager
+  getClipboardManager ()
+  {
+    final EmacsHolder<ClipboardManager> manager;
+    Runnable runnable;
+
+    manager = new EmacsHolder<ClipboardManager> ();
+
+    runnable = new Runnable () {
+       public void
+       run ()
+       {
+         Object tem;
+
+         synchronized (this)
+           {
+             tem = getSystemService (Context.CLIPBOARD_SERVICE);
+             manager.thing = (ClipboardManager) tem;
+             notify ();
+           }
+       }
+      };
+
+    syncRunnable (runnable);
+    return manager.thing;
+  }
+
+  public void
+  restartEmacs ()
+  {
+    Intent intent;
+
+    intent = new Intent (this, EmacsActivity.class);
+    intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+    startActivity (intent);
+    System.exit (0);
+  }
+
+  /* Wait synchronously for the specified RUNNABLE to complete in the
+     UI thread.  Must be called from the Emacs thread.  */
+
+  public static void
+  syncRunnable (Runnable runnable)
+  {
+    EmacsNative.beginSynchronous ();
+
+    synchronized (runnable)
+      {
+       SERVICE.runOnUiThread (runnable);
+
+       while (true)
+         {
+           try
+             {
+               runnable.wait ();
+               break;
+             }
+           catch (InterruptedException e)
+             {
+               continue;
+             }
+         }
+      }
+
+    EmacsNative.endSynchronous ();
+  }
+
+
+
+  /* IMM functions such as `updateSelection' holds an internal lock
+     that is also taken before `onCreateInputConnection' (in
+     EmacsView.java) is called; when that then asks the UI thread for
+     the current selection, a dead lock results.  To remedy this,
+     reply to any synchronous queries now -- and prohibit more queries
+     for the duration of `updateSelection' -- if EmacsView may have
+     been asking for the value of the region.  */
+
+  public static void
+  icBeginSynchronous ()
+  {
+    /* Set servicingQuery to 2, so viewGetSelection knows it shouldn't
+       proceed.  */
+
+    if (servicingQuery.getAndSet (2) == 1)
+      /* But if viewGetSelection is already in progress, answer it
+        first.  */
+      EmacsNative.answerQuerySpin ();
+  }
+
+  public static void
+  icEndSynchronous ()
+  {
+    if (servicingQuery.getAndSet (0) != 2)
+      throw new RuntimeException ("incorrect value of `servicingQuery': "
+                                 + "likely 1");
+  }
+
+  public static int[]
+  viewGetSelection (short window)
+  {
+    int[] selection;
+
+    /* See if a query is already in progress from the other
+       direction.  */
+    if (!servicingQuery.compareAndSet (0, 1))
+      return null;
+
+    /* Now call the regular getSelection.  Note that this can't race
+       with answerQuerySpin, as `android_servicing_query' can never be
+       2 when icBeginSynchronous is called, so a query will always be
+       started.  */
+    selection = EmacsNative.getSelection (window);
+
+    /* Finally, clear servicingQuery if its value is still 1.  If a
+       query has started from the other side, it ought to be 2.  */
+
+    servicingQuery.compareAndSet (1, 0);
+    return selection;
+  }
+
+
+
+  public void
+  updateIC (EmacsWindow window, int newSelectionStart,
+           int newSelectionEnd, int composingRegionStart,
+           int composingRegionEnd)
+  {
+    if (DEBUG_IC)
+      Log.d (TAG, ("updateIC: " + window + " " + newSelectionStart
+                  + " " + newSelectionEnd + " "
+                  + composingRegionStart + " "
+                  + composingRegionEnd));
+
+    icBeginSynchronous ();
+    window.view.imManager.updateSelection (window.view,
+                                          newSelectionStart,
+                                          newSelectionEnd,
+                                          composingRegionStart,
+                                          composingRegionEnd);
+    icEndSynchronous ();
+  }
+
+  public void
+  resetIC (EmacsWindow window, int icMode)
+  {
+    int oldMode;
+
+    if (DEBUG_IC)
+      Log.d (TAG, "resetIC: " + window + ", " + icMode);
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+       && (oldMode = window.view.getICMode ()) == icMode
+       /* Don't do this if there is currently no input
+          connection.  */
+       && oldMode != IC_MODE_NULL)
+      {
+       if (DEBUG_IC)
+         Log.d (TAG, "resetIC: calling invalidateInput");
+
+       /* Android 33 and later allow the IM reset to be optimized out
+          and replaced by a call to `invalidateInput', which is much
+          faster, as it does not involve resetting the input
+          connection.  */
+
+       icBeginSynchronous ();
+       window.view.imManager.invalidateInput (window.view);
+       icEndSynchronous ();
+
+       return;
+      }
+
+    window.view.setICMode (icMode);
+
+    icBeginSynchronous ();
+    window.view.icGeneration++;
+    window.view.imManager.restartInput (window.view);
+    icEndSynchronous ();
+  }
+
+  public void
+  updateCursorAnchorInfo (EmacsWindow window, float x,
+                         float y, float yBaseline,
+                         float yBottom)
+  {
+    CursorAnchorInfo info;
+    CursorAnchorInfo.Builder builder;
+    Matrix matrix;
+    int[] offsets;
+
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
+      return;
+
+    offsets = new int[2];
+    builder = new CursorAnchorInfo.Builder ();
+    matrix = new Matrix (window.view.getMatrix ());
+    window.view.getLocationOnScreen (offsets);
+    matrix.postTranslate (offsets[0], offsets[1]);
+    builder.setMatrix (matrix);
+    builder.setInsertionMarkerLocation (x, y, yBaseline, yBottom,
+                                       0);
+    info = builder.build ();
+
+
+
+    if (DEBUG_IC)
+      Log.d (TAG, ("updateCursorAnchorInfo: " + x + " " + y
+                  + " " + yBaseline + "-" + yBottom));
+
+    icBeginSynchronous ();
+    window.view.imManager.updateCursorAnchorInfo (window.view, info);
+    icEndSynchronous ();
+  }
+
+
+
+  /* Content provider functions.  */
+
+  /* Open a content URI described by the bytes BYTES, a non-terminated
+     string; make it writable if WRITABLE, and readable if READABLE.
+     Truncate the file if TRUNCATE.
+
+     Value is the resulting file descriptor or -1 upon failure.  */
+
+  public int
+  openContentUri (byte[] bytes, boolean writable, boolean readable,
+                 boolean truncate)
+  {
+    String name, mode;
+    ParcelFileDescriptor fd;
+    int i;
+
+    /* Figure out the file access mode.  */
+
+    mode = "";
+
+    if (readable)
+      mode += "r";
+
+    if (writable)
+      mode += "w";
+
+    if (truncate)
+      mode += "t";
+
+    /* Try to open an associated ParcelFileDescriptor.  */
+
+    try
+      {
+       /* The usual file name encoding question rears its ugly head
+          again.  */
+
+       name = new String (bytes, "UTF-8");
+       fd = resolver.openFileDescriptor (Uri.parse (name), mode);
+
+       /* Use detachFd on newer versions of Android or plain old
+          dup.  */
+
+       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1)
+         {
+           i = fd.detachFd ();
+           fd.close ();
+
+           return i;
+         }
+       else
+         {
+           i = EmacsNative.dup (fd.getFd ());
+           fd.close ();
+
+           return i;
+         }
+      }
+    catch (Exception exception)
+      {
+       return -1;
+      }
+  }
+
+  public boolean
+  checkContentUri (byte[] string, boolean readable, boolean writable)
+  {
+    String mode, name;
+    ParcelFileDescriptor fd;
+
+    /* Decode this into a URI.  */
+
+    try
+      {
+       /* The usual file name encoding question rears its ugly head
+          again.  */
+       name = new String (string, "UTF-8");
+      }
+    catch (UnsupportedEncodingException exception)
+      {
+       name = null;
+       throw new RuntimeException (exception);
+      }
+
+    mode = "r";
+
+    if (writable)
+      mode += "w";
+
+    try
+      {
+       fd = resolver.openFileDescriptor (Uri.parse (name), mode);
+       fd.close ();
+
+       return true;
+      }
+    catch (Exception exception)
+      {
+       /* Fall through.  */
+      }
+
+    return false;
+  }
+
+  /* Build a content file name for URI.
+
+     Return a file name within the /contents/by-authority
+     pseudo-directory that `android_get_content_name' can then
+     transform back into an encoded URI.
+
+     A content name consists of any number of unencoded path segments
+     separated by `/' characters, possibly followed by a question mark
+     and an encoded query string.  */
+
+  public static String
+  buildContentName (Uri uri)
+  {
+    StringBuilder builder;
+
+    builder = new StringBuilder ("/content/by-authority/");
+    builder.append (uri.getAuthority ());
+
+    /* First, append each path segment.  */
+
+    for (String segment : uri.getPathSegments ())
+      {
+       /* FIXME: what if segment contains a slash character? */
+       builder.append ('/');
+       builder.append (uri.encode (segment));
+      }
+
+    /* Now, append the query string if necessary.  */
+
+    if (uri.getEncodedQuery () != null)
+      builder.append ('?').append (uri.getEncodedQuery ());
+
+    return builder.toString ();
+  }
+
+
+
+  private long[]
+  queryBattery19 ()
+  {
+    IntentFilter filter;
+    Intent battery;
+    long capacity, chargeCounter, currentAvg, currentNow;
+    long status, remaining, plugged, temp;
+
+    filter = new IntentFilter (Intent.ACTION_BATTERY_CHANGED);
+    battery = registerReceiver (null, filter);
+
+    if (battery == null)
+      return null;
+
+    capacity = battery.getIntExtra (BatteryManager.EXTRA_LEVEL, 0);
+    chargeCounter
+      = (battery.getIntExtra (BatteryManager.EXTRA_SCALE, 0)
+        / battery.getIntExtra (BatteryManager.EXTRA_LEVEL, 100) * 100);
+    currentAvg = 0;
+    currentNow = 0;
+    status = battery.getIntExtra (BatteryManager.EXTRA_STATUS, 0);
+    remaining = -1;
+    plugged = battery.getIntExtra (BatteryManager.EXTRA_PLUGGED, 0);
+    temp = battery.getIntExtra (BatteryManager.EXTRA_TEMPERATURE, 0);
+
+    return new long[] { capacity, chargeCounter, currentAvg,
+                       currentNow, remaining, status, plugged,
+                       temp, };
+  }
+
+  /* Return the status of the battery.  See struct
+     android_battery_status for the order of the elements
+     returned.
+
+     Value may be null upon failure.  */
+
+  public long[]
+  queryBattery ()
+  {
+    Object tem;
+    BatteryManager manager;
+    long capacity, chargeCounter, currentAvg, currentNow;
+    long status, remaining, plugged, temp;
+    int prop;
+    IntentFilter filter;
+    Intent battery;
+
+    /* Android 4.4 or earlier require applications to use a different
+       API to query the battery status.  */
+
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
+      return queryBattery19 ();
+
+    tem = getSystemService (Context.BATTERY_SERVICE);
+    manager = (BatteryManager) tem;
+    remaining = -1;
+
+    prop = BatteryManager.BATTERY_PROPERTY_CAPACITY;
+    capacity = manager.getLongProperty (prop);
+    prop = BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER;
+    chargeCounter = manager.getLongProperty (prop);
+    prop = BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE;
+    currentAvg = manager.getLongProperty (prop);
+    prop = BatteryManager.BATTERY_PROPERTY_CURRENT_NOW;
+    currentNow = manager.getLongProperty (prop);
+
+    /* Return the battery status.  N.B. that Android 7.1 and earlier
+       only return ``charging'' or ``discharging''.  */
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+      status
+       = manager.getIntProperty (BatteryManager.BATTERY_PROPERTY_STATUS);
+    else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
+      status = (manager.isCharging ()
+               ? BatteryManager.BATTERY_STATUS_CHARGING
+               : BatteryManager.BATTERY_STATUS_DISCHARGING);
+    else
+      status = (currentNow > 0
+               ? BatteryManager.BATTERY_STATUS_CHARGING
+               : BatteryManager.BATTERY_STATUS_DISCHARGING);
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
+      remaining = manager.computeChargeTimeRemaining ();
+
+    plugged = -1;
+    temp = -1;
+
+    /* Now obtain additional information from the battery manager.  */
+
+    filter = new IntentFilter (Intent.ACTION_BATTERY_CHANGED);
+    battery = registerReceiver (null, filter);
+
+    if (battery != null)
+      {
+       plugged = battery.getIntExtra (BatteryManager.EXTRA_PLUGGED, 0);
+       temp = battery.getIntExtra (BatteryManager.EXTRA_TEMPERATURE, 0);
+
+       /* Make status more reliable.  */
+       if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
+         status = battery.getIntExtra (BatteryManager.EXTRA_STATUS, 0);
+      }
+
+    return new long[] { capacity, chargeCounter, currentAvg,
+                       currentNow, remaining, status, plugged,
+                       temp, };
+  }
+
+  public void
+  updateExtractedText (EmacsWindow window, ExtractedText text,
+                      int token)
+  {
+    if (DEBUG_IC)
+      Log.d (TAG, "updateExtractedText: @" + token + ", " + text);
+
+    window.view.imManager.updateExtractedText (window.view,
+                                              token, text);
+  }
+
+
+
+  /* Document tree management functions.  These functions shouldn't be
+     called before Android 5.0.  */
+
+  /* Return an array of each document authority providing at least one
+     tree URI that Emacs holds the rights to persistently access.  */
+
+  public String[]
+  getDocumentAuthorities ()
+  {
+    List<UriPermission> permissions;
+    HashSet<String> allProviders;
+    Uri uri;
+
+    permissions = resolver.getPersistedUriPermissions ();
+    allProviders = new HashSet<String> ();
+
+    for (UriPermission permission : permissions)
+      {
+       uri = permission.getUri ();
+
+       if (DocumentsContract.isTreeUri (uri)
+           && permission.isReadPermission ())
+         allProviders.add (uri.getAuthority ());
+      }
+
+    return allProviders.toArray (new String[0]);
+  }
+
+  /* Start a file chooser activity to request access to a directory
+     tree.
+
+     Value is 1 if the activity couldn't be started for some reason,
+     and 0 in any other case.  */
+
+  public int
+  requestDirectoryAccess ()
+  {
+    Runnable runnable;
+    final EmacsHolder<Integer> rc;
+
+    /* Return 1 if Android is too old to support this feature.  */
+
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
+      return 1;
+
+    rc = new EmacsHolder<Integer> ();
+    rc.thing = Integer.valueOf (1);
+
+    runnable = new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         EmacsActivity activity;
+         Intent intent;
+         int id;
+
+         synchronized (this)
+           {
+             /* Try to obtain an activity that will receive the
+                response from the file chooser dialog.  */
+
+             if (EmacsActivity.focusedActivities.isEmpty ())
+               {
+                 /* If focusedActivities is empty then this dialog
+                    may have been displayed immediately after another
+                    popup dialog was dismissed.  Try the
+                    EmacsActivity to be focused.  */
+
+                 activity = EmacsActivity.lastFocusedActivity;
+
+                 if (activity == null)
+                   {
+                     /* Still no luck.  Return failure.  */
+                     notify ();
+                     return;
+                   }
+               }
+             else
+               activity = EmacsActivity.focusedActivities.get (0);
+
+             /* Now create the intent.  */
+             intent = new Intent (Intent.ACTION_OPEN_DOCUMENT_TREE);
+
+             try
+               {
+                 id = EmacsActivity.ACCEPT_DOCUMENT_TREE;
+                 activity.startActivityForResult (intent, id, null);
+                 rc.thing = Integer.valueOf (0);
+               }
+             catch (Exception e)
+               {
+                 e.printStackTrace ();
+               }
+
+             notify ();
+           }
+       }
+      };
+
+    syncRunnable (runnable);
+    return rc.thing;
+  }
+
+  /* Return an array of each tree provided by the document PROVIDER
+     that Emacs has permission to access.
+
+     Value is an array if the provider really does exist, NULL
+     otherwise.  */
+
+  public String[]
+  getDocumentTrees (byte provider[])
+  {
+    String providerName;
+    List<String> treeList;
+    List<UriPermission> permissions;
+    Uri uri;
+
+    try
+      {
+       providerName = new String (provider, "US-ASCII");
+      }
+    catch (UnsupportedEncodingException exception)
+      {
+       return null;
+      }
+
+    permissions = resolver.getPersistedUriPermissions ();
+    treeList = new ArrayList<String> ();
+
+    for (UriPermission permission : permissions)
+      {
+       uri = permission.getUri ();
+
+       if (DocumentsContract.isTreeUri (uri)
+           && uri.getAuthority ().equals (providerName)
+           && permission.isReadPermission ())
+         /* Make sure the tree document ID is encoded.  Refrain from
+            encoding characters such as +:&?#, since they don't
+            conflict with file name separators or other special
+            characters.  */
+         treeList.add (Uri.encode (DocumentsContract.getTreeDocumentId (uri),
+                                   " +:&?#"));
+      }
+
+    return treeList.toArray (new String[0]);
+  }
+
+  /* Find the document ID of the file within TREE_URI designated by
+     NAME.
+
+     NAME is a ``file name'' comprised of the display names of
+     individual files.  Each constituent component prior to the last
+     must name a directory file within TREE_URI.
+
+     Upon success, return 0 or 1 (contingent upon whether or not the
+     last component within NAME is a directory) and place the document
+     ID of the named file in ID_RETURN[0].
+
+     If the designated file can't be located, but each component of
+     NAME up to the last component can and is a directory, return -2
+     and the ID of the last component located in ID_RETURN[0].
+
+     If the designated file can't be located, return -1, or signal one
+     of OperationCanceledException, SecurityException,
+     FileNotFoundException, or UnsupportedOperationException.  */
+
+  private int
+  documentIdFromName (String tree_uri, String name, String[] id_return)
+  {
+    /* Start the thread used to run SAF requests if it isn't already
+       running.  */
+
+    if (storageThread == null)
+      {
+       storageThread = new EmacsSafThread (resolver);
+       storageThread.start ();
+      }
+
+    return storageThread.documentIdFromName (tree_uri, name,
+                                            id_return);
+  }
+
+  /* Return an encoded document URI representing a tree with the
+     specified IDENTIFIER supplied by the authority AUTHORITY.
+
+     Return null instead if Emacs does not have permanent access
+     to the specified document tree recorded on disk.  */
+
+  public String
+  getTreeUri (String tree, String authority)
+  {
+    Uri uri, grantedUri;
+    List<UriPermission> permissions;
+
+    /* First, build the URI.  */
+    tree = Uri.decode (tree);
+    uri = DocumentsContract.buildTreeDocumentUri (authority, tree);
+
+    /* Now, search for it within the list of persisted URI
+       permissions.  */
+    permissions = resolver.getPersistedUriPermissions ();
+
+    for (UriPermission permission : permissions)
+      {
+       /* If the permission doesn't entitle Emacs to read access,
+          skip it.  */
+
+       if (!permission.isReadPermission ())
+         continue;
+
+        grantedUri = permission.getUri ();
+
+       if (grantedUri.equals (uri))
+         return uri.toString ();
+      }
+
+    /* Emacs doesn't have permission to access this tree URI.  */
+    return null;
+  }
+
+  /* Return file status for the document designated by the given
+     DOCUMENTID and tree URI.  If DOCUMENTID is NULL, use the document
+     ID in URI itself.
+
+     Value is null upon failure, or an array of longs [MODE, SIZE,
+     MTIM] upon success, where MODE contains the file type and access
+     modes of the file as in `struct stat', SIZE is the size of the
+     file in BYTES or -1 if not known, and MTIM is the time of the
+     last modification to this file in milliseconds since 00:00,
+     January 1st, 1970.
+
+     OperationCanceledException and other typical exceptions may be
+     signaled upon receiving async input or other errors.  */
+
+  public long[]
+  statDocument (String uri, String documentId)
+  {
+    /* Start the thread used to run SAF requests if it isn't already
+       running.  */
+
+    if (storageThread == null)
+      {
+       storageThread = new EmacsSafThread (resolver);
+       storageThread.start ();
+      }
+
+    return storageThread.statDocument (uri, documentId);
+  }
+
+  /* Find out whether Emacs has access to the document designated by
+     the specified DOCUMENTID within the tree URI.  If DOCUMENTID is
+     NULL, use the document ID in URI itself.
+
+     If WRITABLE, also check that the file is writable, which is true
+     if it is either a directory or its flags contains
+     FLAG_SUPPORTS_WRITE.
+
+     Value is 0 if the file is accessible, and one of the following if
+     not:
+
+       -1, if the file does not exist.
+       -2, if WRITABLE and the file is not writable.
+       -3, upon any other error.
+
+     In addition, arbitrary runtime exceptions (such as
+     SecurityException or UnsupportedOperationException) may be
+     thrown.  */
+
+  public int
+  accessDocument (String uri, String documentId, boolean writable)
+  {
+    /* Start the thread used to run SAF requests if it isn't already
+       running.  */
+
+    if (storageThread == null)
+      {
+       storageThread = new EmacsSafThread (resolver);
+       storageThread.start ();
+      }
+
+    return storageThread.accessDocument (uri, documentId, writable);
+  }
+
+  /* Open a cursor representing each entry within the directory
+     designated by the specified DOCUMENTID within the tree URI.
+
+     If DOCUMENTID is NULL, use the document ID within URI itself.
+     Value is NULL upon failure.
+
+     In addition, arbitrary runtime exceptions (such as
+     SecurityException or UnsupportedOperationException) may be
+     thrown.  */
+
+  public Cursor
+  openDocumentDirectory (String uri, String documentId)
+  {
+    /* Start the thread used to run SAF requests if it isn't already
+       running.  */
+
+    if (storageThread == null)
+      {
+       storageThread = new EmacsSafThread (resolver);
+       storageThread.start ();
+      }
+
+    return storageThread.openDocumentDirectory (uri, documentId);
+  }
+
+  /* Read a single directory entry from the specified CURSOR.  Return
+     NULL if at the end of the directory stream, and a directory entry
+     with `d_name' set to NULL if an error occurs.  */
+
+  public EmacsDirectoryEntry
+  readDirectoryEntry (Cursor cursor)
+  {
+    EmacsDirectoryEntry entry;
+    int index;
+    String name, type;
+
+    entry = new EmacsDirectoryEntry ();
+
+    while (true)
+      {
+       if (!cursor.moveToNext ())
+         return null;
+
+       /* First, retrieve the display name.  */
+       index = cursor.getColumnIndex (Document.COLUMN_DISPLAY_NAME);
+
+       if (index < 0)
+         /* Return an invalid directory entry upon failure.  */
+         return entry;
+
+       try
+         {
+           name = cursor.getString (index);
+         }
+       catch (Exception exception)
+         {
+           return entry;
+         }
+
+       /* Skip this entry if its name cannot be represented.  */
+
+       if (name.equals ("..") || name.equals (".") || name.contains ("/"))
+         continue;
+
+       /* Now, look for its type.  */
+
+       index = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE);
+
+       if (index < 0)
+         /* Return an invalid directory entry upon failure.  */
+         return entry;
+
+       try
+         {
+           type = cursor.getString (index);
+         }
+       catch (Exception exception)
+         {
+           return entry;
+         }
+
+       if (type != null
+           && type.equals (Document.MIME_TYPE_DIR))
+         entry.d_type = 1;
+       entry.d_name = name;
+       return entry;
+      }
+
+    /* Not reached.  */
+  }
+
+  /* Open a file descriptor for a file document designated by
+     DOCUMENTID within the document tree identified by URI.  If
+     TRUNCATE and the document already exists, truncate its contents
+     before returning.
+
+     On Android 9.0 and earlier, always open the document in
+     ``read-write'' mode; this instructs the document provider to
+     return a seekable file that is stored on disk and returns correct
+     file status.
+
+     Under newer versions of Android, open the document in a
+     non-writable mode if WRITE is false.  This is possible because
+     these versions allow Emacs to explicitly request a seekable
+     on-disk file.
+
+     Value is NULL upon failure or a parcel file descriptor upon
+     success.  Call `ParcelFileDescriptor.close' on this file
+     descriptor instead of using the `close' system call.
+
+     FileNotFoundException and/or SecurityException and
+     UnsupportedOperationException may be thrown upon failure.  */
+
+  public ParcelFileDescriptor
+  openDocument (String uri, String documentId, boolean write,
+               boolean truncate)
+  {
+    /* Start the thread used to run SAF requests if it isn't already
+       running.  */
+
+    if (storageThread == null)
+      {
+       storageThread = new EmacsSafThread (resolver);
+       storageThread.start ();
+      }
+
+    return storageThread.openDocument (uri, documentId, write,
+                                      truncate);
+  }
+
+  /* Create a new document with the given display NAME within the
+     directory identified by DOCUMENTID inside the document tree
+     designated by URI.
+
+     If DOCUMENTID is NULL, create the document inside the root of
+     that tree.
+
+     Either FileNotFoundException, SecurityException or
+     UnsupportedOperationException may be thrown upon failure.
+
+     Return the document ID of the new file upon success, NULL
+     otherwise.  */
+
+  public String
+  createDocument (String uri, String documentId, String name)
+    throws FileNotFoundException
+  {
+    String mimeType, separator, mime, extension;
+    int index;
+    MimeTypeMap singleton;
+    Uri treeUri, directoryUri, docUri;
+
+    /* Try to get the MIME type for this document.
+       Default to ``application/octet-stream''.  */
+
+    mimeType = "application/octet-stream";
+
+    /* Abuse WebView stuff to get the file's MIME type.  */
+
+    index = name.lastIndexOf ('.');
+
+    if (index > 0)
+      {
+       singleton = MimeTypeMap.getSingleton ();
+       extension = name.substring (index + 1);
+       mime = singleton.getMimeTypeFromExtension (extension);
+
+       if (mime != null)
+         mimeType = mime;
+      }
+
+    /* Now parse URI.  */
+    treeUri = Uri.parse (uri);
+
+    if (documentId == null)
+      documentId = DocumentsContract.getTreeDocumentId (treeUri);
+
+    /* And build a file URI referring to the directory.  */
+
+    directoryUri
+      = DocumentsContract.buildChildDocumentsUriUsingTree (treeUri,
+                                                          documentId);
+
+    docUri = DocumentsContract.createDocument (resolver,
+                                              directoryUri,
+                                              mimeType, name);
+
+    if (docUri == null)
+      return null;
+
+    /* Invalidate the file status of the containing directory.  */
+
+    if (storageThread != null)
+      storageThread.postInvalidateStat (treeUri, documentId);
+
+    /* Return the ID of the new document.  */
+    return DocumentsContract.getDocumentId (docUri);
+  }
+
+  /* Like `createDocument', but create a directory instead of an
+     ordinary document.  */
+
+  public String
+  createDirectory (String uri, String documentId, String name)
+    throws FileNotFoundException
+  {
+    int index;
+    Uri treeUri, directoryUri, docUri;
+
+    /* Now parse URI.  */
+    treeUri = Uri.parse (uri);
+
+    if (documentId == null)
+      documentId = DocumentsContract.getTreeDocumentId (treeUri);
+
+    /* And build a file URI referring to the directory.  */
+
+    directoryUri
+      = DocumentsContract.buildChildDocumentsUriUsingTree (treeUri,
+                                                          documentId);
+
+    /* If name ends with a directory separator character, delete
+       it.  */
+
+    if (name.endsWith ("/"))
+      name = name.substring (0, name.length () - 1);
+
+    /* From Android's perspective, directories are just ordinary
+       documents with the `MIME_TYPE_DIR' type.  */
+
+    docUri = DocumentsContract.createDocument (resolver,
+                                              directoryUri,
+                                              Document.MIME_TYPE_DIR,
+                                              name);
+
+    if (docUri == null)
+      return null;
+
+    /* Return the ID of the new document, but first invalidate the
+       state of the containing directory.  */
+
+    if (storageThread != null)
+      storageThread.postInvalidateStat (treeUri, documentId);
+
+    return DocumentsContract.getDocumentId (docUri);
+  }
+
+  /* Delete the document identified by ID from the document tree
+     identified by URI.  Return 0 upon success and -1 upon
+     failure.
+
+     NAME should be the name of the document being deleted, and is
+     used to invalidate the cache.  */
+
+  public int
+  deleteDocument (String uri, String id, String name)
+    throws FileNotFoundException
+  {
+    Uri uriObject, tree;
+
+    tree = Uri.parse (uri);
+    uriObject = DocumentsContract.buildDocumentUriUsingTree (tree, id);
+
+    if (DocumentsContract.deleteDocument (resolver, uriObject))
+      {
+       if (storageThread != null)
+         storageThread.postInvalidateCache (tree, id, name);
+
+       return 0;
+      }
+
+    return -1;
+  }
+
+  /* Rename the document designated by DOCID inside the directory tree
+     identified by URI, which should be within the directory
+     designated by DIR, to NAME.  If the file can't be renamed because
+     it doesn't support renaming, return -1, 0 otherwise.  */
+
+  public int
+  renameDocument (String uri, String docId, String dir, String name)
+    throws FileNotFoundException
+  {
+    Uri tree, uriObject;
+
+    tree = Uri.parse (uri);
+    uriObject = DocumentsContract.buildDocumentUriUsingTree (tree, docId);
+
+    if (DocumentsContract.renameDocument (resolver, uriObject,
+                                         name)
+       != null)
+      {
+       /* Invalidate the cache.  */
+       if (storageThread != null)
+         storageThread.postInvalidateCacheDir (tree, docId,
+                                               name);
+       return 0;
+      }
+
+    /* Handle errors specially, so `android_saf_rename_document' can
+       return ENXDEV.  */
+    return -1;
+  }
+
+  /* Move the document designated by DOCID from the directory under
+     DIR_NAME designated by SRCID to the directory designated by
+     DSTID.  If the ID of the document being moved changes as a
+     consequence of the movement, return the new ID, else NULL.
+
+     URI is the document tree containing all three documents.  */
+
+  public String
+  moveDocument (String uri, String docId, String dirName,
+               String dstId, String srcId)
+    throws FileNotFoundException
+  {
+    Uri uri1, docId1, dstId1, srcId1;
+    Uri name;
+
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
+      throw new UnsupportedOperationException ("Documents aren't capable"
+                                              + " of being moved on Android"
+                                              + " versions before 7.0.");
+
+    uri1 = Uri.parse (uri);
+    docId1 = DocumentsContract.buildDocumentUriUsingTree (uri1, docId);
+    dstId1 = DocumentsContract.buildDocumentUriUsingTree (uri1, dstId);
+    srcId1 = DocumentsContract.buildDocumentUriUsingTree (uri1, srcId);
+
+    /* Move the document; this function returns the new ID of the
+       document should it change.  */
+    name = DocumentsContract.moveDocument (resolver, docId1,
+                                          srcId1, dstId1);
+
+    /* Now invalidate the caches for both DIRNAME and DOCID.  */
+
+    if (storageThread != null)
+      {
+       storageThread.postInvalidateCacheDir (uri1, docId, dirName);
+
+       /* Invalidate the stat cache entries for both the source and
+          destination directories, since their contents have
+          changed.  */
+       storageThread.postInvalidateStat (uri1, dstId);
+       storageThread.postInvalidateStat (uri1, srcId);
+      }
+
+    return (name != null
+           ? DocumentsContract.getDocumentId (name)
+           : null);
+  }
+
+  /* Return if there is a content provider by the name of AUTHORITY
+     supplying at least one tree URI Emacs retains persistent rights
+     to access.  */
+
+  public boolean
+  validAuthority (String authority)
+  {
+    List<UriPermission> permissions;
+    Uri uri;
+
+    permissions = resolver.getPersistedUriPermissions ();
+
+    for (UriPermission permission : permissions)
+      {
+       uri = permission.getUri ();
+
+       if (DocumentsContract.isTreeUri (uri)
+           && permission.isReadPermission ()
+           && uri.getAuthority ().equals (authority))
+         return true;
+      }
+
+    return false;
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java 
b/java/org/gnu/emacs/EmacsSurfaceView.java
new file mode 100644
index 00000000000..c47696b35c0
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSurfaceView.java
@@ -0,0 +1,223 @@
+/* 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.view.View;
+
+import android.os.Build;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.Paint;
+
+import java.lang.ref.WeakReference;
+
+/* This originally extended SurfaceView.  However, doing so proved to
+   be too slow, and Android's surface view keeps up to three of its
+   own back buffers, which use too much memory (up to 96 MB for a
+   single frame.) */
+
+public final class EmacsSurfaceView extends View
+{
+  private static final String TAG = "EmacsSurfaceView";
+
+  /* The complete buffer contents at the time of the last draw.  */
+  private Bitmap frontBuffer;
+
+  /* Whether frontBuffer has been updated since the last call to
+     `onDraw'.  */
+  private boolean bitmapChanged;
+
+  /* Canvas representing the front buffer.  */
+  private Canvas bitmapCanvas;
+
+  /* Reference to the last bitmap copied to the front buffer.  */
+  private WeakReference<Bitmap> bitmap;
+
+  /* Paint objects used on the main and UI threads, respectively.  */
+  private static final Paint bitmapPaint, uiThreadPaint;
+
+  static
+  {
+    /* Create two different Paint objects; one is used on the main
+       thread for buffer swaps, while the other is used from the UI
+       thread in `onDraw'.  This is necessary because Paint objects
+       are not thread-safe, even if their uses are interlocked.  */
+
+    bitmapPaint = new Paint ();
+    uiThreadPaint = new Paint ();
+  };
+
+  public
+  EmacsSurfaceView (EmacsView view)
+  {
+    super (view.getContext ());
+
+    this.bitmap = new WeakReference<Bitmap> (null);
+  }
+
+  private void
+  copyToFrontBuffer (Bitmap bitmap, Rect damageRect)
+  {
+    EmacsService.checkEmacsThread ();
+
+    if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O
+       && Build.VERSION.SDK_INT != Build.VERSION_CODES.O_MR1
+       && Build.VERSION.SDK_INT != Build.VERSION_CODES.N_MR1
+       && Build.VERSION.SDK_INT != Build.VERSION_CODES.N)
+      {
+       /* If `drawBitmap' can safely be used while a bitmap is locked
+          by another thread, continue here... */
+
+       if (damageRect != null)
+         bitmapCanvas.drawBitmap (bitmap, damageRect, damageRect,
+                                  bitmapPaint);
+       else
+         bitmapCanvas.drawBitmap (bitmap, 0f, 0f, bitmapPaint);
+      }
+    else
+      {
+       /* But if it can not, as on Android 7.0 through 8.1, then use
+          a replacement function.  */
+
+       if (damageRect != null)
+         EmacsNative.blitRect (bitmap, frontBuffer,
+                               damageRect.left,
+                               damageRect.top,
+                               damageRect.right,
+                               damageRect.bottom);
+       else
+         EmacsNative.blitRect (bitmap, frontBuffer, 0, 0,
+                               bitmap.getWidth (),
+                               bitmap.getHeight ());
+      }
+
+    /* See the large comment inside `onDraw'.  */
+    bitmapChanged = true;
+  }
+
+  private void
+  reconfigureFrontBuffer (Bitmap bitmap)
+  {
+    /* First, remove the old front buffer.  */
+
+    if (frontBuffer != null)
+      {
+       frontBuffer.recycle ();
+       frontBuffer = null;
+       bitmapCanvas = null;
+      }
+
+    this.bitmap = new WeakReference<Bitmap> (bitmap);
+
+    /* Next, create the new front buffer if necessary.  */
+
+    if (bitmap != null && frontBuffer == null)
+      {
+       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+         frontBuffer = Bitmap.createBitmap (bitmap.getWidth (),
+                                            bitmap.getHeight (),
+                                            Bitmap.Config.ARGB_8888,
+                                            false);
+       else
+         frontBuffer = Bitmap.createBitmap (bitmap.getWidth (),
+                                            bitmap.getHeight (),
+                                            Bitmap.Config.ARGB_8888);
+
+       bitmapCanvas = new Canvas (frontBuffer);
+
+       /* And copy over the bitmap contents.  */
+       copyToFrontBuffer (bitmap, null);
+      }
+    else if (bitmap != null)
+      /* Just copy over the bitmap contents.  */
+      copyToFrontBuffer (bitmap, null);
+  }
+
+  public synchronized void
+  setBitmap (Bitmap bitmap, Rect damageRect)
+  {
+    if (bitmap != this.bitmap.get ())
+      reconfigureFrontBuffer (bitmap);
+    else if (bitmap != null)
+      copyToFrontBuffer (bitmap, damageRect);
+
+    if (bitmap != null)
+      {
+       /* In newer versions of Android, the invalid rectangle is
+          supposedly internally calculated by the system.  How that
+          is done is unknown, but calling `invalidateRect' is now
+          deprecated.
+
+          Fortunately, nobody has deprecated the version of
+          `postInvalidate' that accepts a dirty rectangle.  */
+
+       if (damageRect != null)
+         postInvalidate (damageRect.left, damageRect.top,
+                         damageRect.right, damageRect.bottom);
+       else
+         postInvalidate ();
+      }
+  }
+
+  @Override
+  public synchronized void
+  onDraw (Canvas canvas)
+  {
+    /* Paint the view's bitmap; the bitmap might be recycled right
+       now.  */
+
+    if (frontBuffer != null)
+      {
+       /* The first time the bitmap is drawn after a buffer swap,
+          mark its contents as having changed.  This increments the
+          ``generation ID'' used by Android to avoid uploading buffer
+          textures for unchanged bitmaps.
+
+          When a buffer swap takes place, the bitmap is initially
+          updated from the Emacs thread, resulting in the generation
+          ID being increased.  If the render thread is texturizing
+          the bitmap while the swap takes place, it might record the
+          generation ID after the update for a texture containing the
+          contents of the bitmap prior to the swap, leaving the
+          texture tied to the bitmap partially updated.
+
+          Android never calls `onDraw' if the render thread is still
+          processing the bitmap.  Update the generation ID here to
+          ensure that a new texture will be uploaded if the bitmap
+          has changed.
+
+          Uploading the bitmap contents to the GPU uses an excessive
+          amount of memory, as the entire bitmap is placed into the
+          graphics command queue, but this memory is actually shared
+          among all other applications and reclaimed by the system
+          when necessary.  */
+
+       if (bitmapChanged)
+         {
+           EmacsNative.notifyPixelsChanged (frontBuffer);
+           bitmapChanged = false;
+         }
+
+       canvas.drawBitmap (frontBuffer, 0f, 0f, uiThreadPaint);
+      }
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsThread.java 
b/java/org/gnu/emacs/EmacsThread.java
new file mode 100644
index 00000000000..5307015b46f
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsThread.java
@@ -0,0 +1,82 @@
+/* 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 java.lang.Thread;
+import java.util.Arrays;
+
+import android.util.Log;
+
+public final class EmacsThread extends Thread
+{
+  private static final String TAG = "EmacsThread";
+
+  /* Whether or not Emacs should be started with an additional
+     argument, and that additional argument if non-NULL.  */
+  private String extraStartupArgument;
+
+  /* Runnable run to initialize Emacs.  */
+  private Runnable paramsClosure;
+
+  /* Whether or not to open a file after starting Emacs.  */
+  private String fileToOpen;
+
+  public
+  EmacsThread (EmacsService service, Runnable paramsClosure,
+              String extraStartupArgument, String fileToOpen)
+  {
+    super ("Emacs main thread");
+    this.extraStartupArgument = extraStartupArgument;
+    this.paramsClosure = paramsClosure;
+    this.fileToOpen = fileToOpen;
+  }
+
+  @Override
+  public void
+  run ()
+  {
+    String args[];
+
+    if (fileToOpen == null)
+      {
+       if (extraStartupArgument == null)
+         args = new String[] { "libandroid-emacs.so", };
+       else
+         args = new String[] { "libandroid-emacs.so",
+                               extraStartupArgument, };
+      }
+    else
+      {
+       if (extraStartupArgument == null)
+         args = new String[] { "libandroid-emacs.so",
+                               fileToOpen, };
+       else
+         args = new String[] { "libandroid-emacs.so",
+                               extraStartupArgument,
+                               fileToOpen, };
+      }
+
+    paramsClosure.run ();
+
+    /* Run the native code now.  */
+    Log.d (TAG, "run: " + Arrays.toString (args));
+    EmacsNative.initEmacs (args, EmacsApplication.dumpFileName);
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsView.java 
b/java/org/gnu/emacs/EmacsView.java
new file mode 100644
index 00000000000..12d8ff4da56
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -0,0 +1,777 @@
+/* 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.Context;
+
+import android.text.InputType;
+
+import android.view.ContextMenu;
+import android.view.View;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Paint;
+
+import android.os.Build;
+import android.util.Log;
+
+/* This is an Android view which has a back and front buffer.  When
+   swapBuffers is called, the back buffer is swapped to the front
+   buffer, and any damage is invalidated.  frontBitmap and backBitmap
+   are modified and used both from the UI and the Emacs thread.  As a
+   result, there is a lock held during all drawing operations.
+
+   It is also a ViewGroup, as it also lays out children.  */
+
+public final class EmacsView extends ViewGroup
+  implements ViewTreeObserver.OnGlobalLayoutListener
+{
+  public static final String TAG = "EmacsView";
+
+  /* The associated EmacsWindow.  */
+  public EmacsWindow window;
+
+  /* The buffer bitmap.  */
+  public Bitmap bitmap;
+
+  /* The associated canvases.  */
+  public Canvas canvas;
+
+  /* The damage region.  */
+  public Region damageRegion;
+
+  /* The associated surface view.  */
+  private EmacsSurfaceView surfaceView;
+
+  /* Whether or not a configure event must be sent for the next layout
+     event regardless of what changed.  */
+  public boolean mustReportLayout;
+
+  /* Whether or not bitmaps must be recreated upon the next call to
+     getBitmap.  */
+  private boolean bitmapDirty;
+
+  /* Whether or not a popup is active.  */
+  private boolean popupActive;
+
+  /* The current context menu.  */
+  private EmacsContextMenu contextMenu;
+
+  /* The last measured width and height.  */
+  private int measuredWidth, measuredHeight;
+
+  /* Object acting as a lock for those values.  */
+  private Object dimensionsLock;
+
+  /* The serial of the last clip rectangle change.  */
+  private long lastClipSerial;
+
+  /* The InputMethodManager for this view's context.  */
+  public InputMethodManager imManager;
+
+  /* Whether or not this view is attached to a window.  */
+  public boolean isAttachedToWindow;
+
+  /* Whether or not this view should have the on screen keyboard
+     displayed whenever possible.  */
+  public boolean isCurrentlyTextEditor;
+
+  /* The associated input connection.  */
+  private EmacsInputConnection inputConnection;
+
+  /* The current IC mode.  See `android_reset_ic' for more
+     details.  */
+  private int icMode;
+
+  /* The number of calls to `resetIC' to have taken place the last
+     time an InputConnection was created.  */
+  public long icSerial;
+
+  /* The number of calls to `recetIC' that have taken place.  */
+  public volatile long icGeneration;
+
+  public
+  EmacsView (EmacsWindow window)
+  {
+    super (EmacsService.SERVICE);
+
+    Object tem;
+    Context context;
+
+    this.window = window;
+    this.damageRegion = new Region ();
+
+    setFocusable (true);
+    setFocusableInTouchMode (true);
+
+    /* Create the surface view.  */
+    this.surfaceView = new EmacsSurfaceView (this);
+    addView (this.surfaceView);
+
+    /* Get rid of the default focus highlight.  */
+    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
+      setDefaultFocusHighlightEnabled (false);
+
+    /* Obtain the input method manager.  */
+    context = getContext ();
+    tem = context.getSystemService (Context.INPUT_METHOD_SERVICE);
+    imManager = (InputMethodManager) tem;
+
+    /* Add this view as its own global layout listener.  */
+    getViewTreeObserver ().addOnGlobalLayoutListener (this);
+
+    /* Create an object used as a lock.  */
+    this.dimensionsLock = new Object ();
+  }
+
+  private void
+  handleDirtyBitmap ()
+  {
+    Bitmap oldBitmap;
+    int measuredWidth, measuredHeight;
+
+    synchronized (dimensionsLock)
+      {
+       /* Load measuredWidth and measuredHeight.  */
+       measuredWidth = this.measuredWidth;
+       measuredHeight = this.measuredHeight;
+      }
+
+    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.  */
+    if (bitmap != null
+       && (bitmap.getWidth () == measuredWidth
+           && bitmap.getHeight () == measuredHeight))
+      {
+       bitmapDirty = false;
+       return;
+      }
+
+    /* Save the old bitmap.  */
+    oldBitmap = bitmap;
+
+    /* Recreate the back buffer bitmap.  */
+    bitmap
+      = Bitmap.createBitmap (measuredWidth,
+                            measuredHeight,
+                            Bitmap.Config.ARGB_8888);
+    bitmap.eraseColor (window.background | 0xff000000);
+
+    /* And canvases.  */
+    canvas = new Canvas (bitmap);
+    canvas.save ();
+
+    /* Since the clip rectangles have been cleared, clear the clip
+       rectangle ID.  */
+    lastClipSerial = 0;
+
+    /* Copy over the contents of the old bitmap.  */
+    if (oldBitmap != null)
+      canvas.drawBitmap (oldBitmap, 0f, 0f, new Paint ());
+
+    bitmapDirty = false;
+
+    /* Explicitly free the old bitmap's memory.  */
+
+    if (oldBitmap != null)
+      oldBitmap.recycle ();
+
+    /* Some Android versions still don't free the bitmap until the
+       next GC.  */
+    Runtime.getRuntime ().gc ();
+  }
+
+  public synchronized void
+  explicitlyDirtyBitmap ()
+  {
+    bitmapDirty = true;
+  }
+
+  public synchronized Bitmap
+  getBitmap ()
+  {
+    if (bitmapDirty || bitmap == null)
+      handleDirtyBitmap ();
+
+    return bitmap;
+  }
+
+  public synchronized Canvas
+  getCanvas (EmacsGC gc)
+  {
+    int i;
+
+    if (bitmapDirty || bitmap == null)
+      handleDirtyBitmap ();
+
+    if (canvas == null)
+      return null;
+
+    /* Update clip rectangles if necessary.  */
+    if (gc.clipRectID != lastClipSerial)
+      {
+       canvas.restore ();
+       canvas.save ();
+
+       if (gc.real_clip_rects != null)
+         {
+           for (i = 0; i < gc.real_clip_rects.length; ++i)
+             canvas.clipRect (gc.real_clip_rects[i]);
+         }
+
+       lastClipSerial = gc.clipRectID;
+      }
+
+    return canvas;
+  }
+
+  public void
+  prepareForLayout (int wantedWidth, int wantedHeight)
+  {
+    synchronized (dimensionsLock)
+      {
+       measuredWidth = wantedWidth;
+       measuredHeight = wantedWidth;
+      }
+  }
+
+  @Override
+  protected void
+  onMeasure (int widthMeasureSpec, int heightMeasureSpec)
+  {
+    Rect measurements;
+    int width, height;
+
+    /* Return the width and height of the window regardless of what
+       the parent says.  */
+    measurements = window.getGeometry ();
+
+    width = measurements.width ();
+    height = measurements.height ();
+
+    /* Now apply any extra requirements in widthMeasureSpec and
+       heightMeasureSpec.  */
+
+    if (MeasureSpec.getMode (widthMeasureSpec) == MeasureSpec.EXACTLY)
+      width = MeasureSpec.getSize (widthMeasureSpec);
+    else if (MeasureSpec.getMode (widthMeasureSpec) == MeasureSpec.AT_MOST
+            && width > MeasureSpec.getSize (widthMeasureSpec))
+      width = MeasureSpec.getSize (widthMeasureSpec);
+
+    if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.EXACTLY)
+      height = MeasureSpec.getSize (heightMeasureSpec);
+    else if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.AT_MOST
+            && height > MeasureSpec.getSize (heightMeasureSpec))
+      height = MeasureSpec.getSize (heightMeasureSpec);
+
+    super.setMeasuredDimension (width, height);
+  }
+
+  /* Note that the monitor lock for the window must never be held from
+     within the lock for the view, because the window also locks the
+     other way around.  */
+
+  @Override
+  protected void
+  onLayout (boolean changed, int left, int top, int right,
+           int bottom)
+  {
+    int count, i, oldMeasuredWidth, oldMeasuredHeight;
+    View child;
+    Rect windowRect;
+    boolean needExpose;
+
+    count = getChildCount ();
+    needExpose = false;
+
+    synchronized (dimensionsLock)
+      {
+       /* Load measuredWidth and measuredHeight.  */
+       oldMeasuredWidth = measuredWidth;
+       oldMeasuredHeight = measuredHeight;
+
+       /* Set measuredWidth and measuredHeight.  */
+       measuredWidth = right - left;
+       measuredHeight = bottom - top;
+      }
+
+    /* Dirty the back buffer if the layout change resulted in the view
+       being resized.  */
+
+    if (changed && (right - left != oldMeasuredWidth
+                   || bottom - top != oldMeasuredHeight))
+      {
+       explicitlyDirtyBitmap ();
+
+       /* Expose the window upon a change in the view's size.  */
+
+       if (right - left > oldMeasuredWidth
+           || bottom - top > oldMeasuredHeight)
+         needExpose = true;
+      }
+
+    for (i = 0; i < count; ++i)
+      {
+       child = getChildAt (i);
+
+       Log.d (TAG, "onLayout: " + child);
+
+       if (child == surfaceView)
+         child.layout (0, 0, right - left, bottom - top);
+       else if (child.getVisibility () != GONE)
+         {
+           if (!(child instanceof EmacsView))
+             continue;
+
+           /* What to do: lay out the view precisely according to its
+              window rect.  */
+           windowRect = ((EmacsView) child).window.getGeometry ();
+           child.layout (windowRect.left, windowRect.top,
+                         windowRect.right, windowRect.bottom);
+         }
+      }
+
+    /* Now report the layout change to the window.  */
+
+    if (changed || mustReportLayout)
+      {
+       mustReportLayout = false;
+       window.viewLayout (left, top, right, bottom);
+      }
+
+    if (needExpose)
+      EmacsNative.sendExpose (this.window.handle, 0, 0,
+                             right - left, bottom - top);
+  }
+
+  public void
+  damageRect (Rect damageRect)
+  {
+    EmacsService.checkEmacsThread ();
+    damageRegion.union (damageRect);
+  }
+
+  /* This method is called from both the UI thread and the Emacs
+     thread.  */
+
+  public void
+  swapBuffers ()
+  {
+    Canvas canvas;
+    Rect damageRect;
+    Bitmap bitmap;
+
+    /* Make sure this function is called only from the Emacs
+       thread.  */
+    EmacsService.checkEmacsThread ();
+
+    damageRect = null;
+
+    /* Now see if there is a damage region.  */
+
+    if (damageRegion.isEmpty ())
+      return;
+
+    /* And extract and clear the damage region.  */
+
+    damageRect = damageRegion.getBounds ();
+    damageRegion.setEmpty ();
+
+    bitmap = getBitmap ();
+
+    /* Transfer the bitmap to the surface view, then invalidate
+       it.  */
+    surfaceView.setBitmap (bitmap, damageRect);
+  }
+
+  @Override
+  public boolean
+  onKeyDown (int keyCode, KeyEvent event)
+  {
+    if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
+        || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+        || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE)
+       && !EmacsNative.shouldForwardMultimediaButtons ())
+      return false;
+
+    window.onKeyDown (keyCode, event);
+    return true;
+  }
+
+  @Override
+  public boolean
+  onKeyMultiple (int keyCode, int repeatCount, KeyEvent event)
+  {
+    if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
+        || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+        || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE)
+       && !EmacsNative.shouldForwardMultimediaButtons ())
+      return false;
+
+    window.onKeyDown (keyCode, event);
+    return true;
+  }
+
+  @Override
+  public boolean
+  onKeyUp (int keyCode, KeyEvent event)
+  {
+    if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
+        || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+        || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE)
+       && !EmacsNative.shouldForwardMultimediaButtons ())
+      return false;
+
+    window.onKeyUp (keyCode, event);
+    return true;
+  }
+
+  @Override
+  public void
+  onFocusChanged (boolean gainFocus, int direction,
+                 Rect previouslyFocusedRect)
+  {
+    window.onFocusChanged (gainFocus);
+    super.onFocusChanged (gainFocus, direction,
+                         previouslyFocusedRect);
+  }
+
+  @Override
+  public boolean
+  onGenericMotionEvent (MotionEvent motion)
+  {
+    return window.onGenericMotionEvent (motion);
+  }
+
+  @Override
+  public boolean
+  onTouchEvent (MotionEvent motion)
+  {
+    return window.onTouchEvent (motion);
+  }
+
+  private void
+  moveChildToBack (View child)
+  {
+    int index;
+
+    index = indexOfChild (child);
+
+    if (index > 0)
+      {
+       detachViewFromParent (index);
+
+       /* The view at 0 is the surface view.  */
+       attachViewToParent (child, 1,
+                           child.getLayoutParams());
+      }
+  }
+
+  /* The following two functions must not be called if the view has no
+     parent, or is parented to an activity.  */
+
+  public void
+  raise ()
+  {
+    EmacsView parent;
+
+    parent = (EmacsView) getParent ();
+
+    Log.d (TAG, "raise: parent " + parent);
+
+    if (parent.indexOfChild (this)
+       == parent.getChildCount () - 1)
+      return;
+
+    parent.bringChildToFront (this);
+  }
+
+  public void
+  lower ()
+  {
+    EmacsView parent;
+
+    parent = (EmacsView) getParent ();
+
+    Log.d (TAG, "lower: parent " + parent);
+
+    if (parent.indexOfChild (this) == 1)
+      return;
+
+    parent.moveChildToBack (this);
+  }
+
+  @Override
+  protected void
+  onCreateContextMenu (ContextMenu menu)
+  {
+    if (contextMenu == null)
+      return;
+
+    contextMenu.expandTo (menu, this);
+  }
+
+  public boolean
+  popupMenu (EmacsContextMenu menu, int xPosition,
+            int yPosition, boolean force)
+  {
+    if (popupActive && !force)
+      return false;
+
+    contextMenu = menu;
+    popupActive = true;
+
+    Log.d (TAG, "popupMenu: " + menu + " @" + xPosition
+          + ", " + yPosition + " " + force);
+
+    /* Use showContextMenu (float, float) on N to get actual popup
+       behavior.  */
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+      return showContextMenu ((float) xPosition, (float) yPosition);
+    else
+      return showContextMenu ();
+  }
+
+  public void
+  cancelPopupMenu ()
+  {
+    if (!popupActive)
+      throw new IllegalStateException ("cancelPopupMenu called without"
+                                      + " popupActive set");
+
+    contextMenu = null;
+    popupActive = false;
+
+    /* It is not possible to know with 100% certainty which activity
+       is currently displaying the context menu.  Loop through each
+       activity and call `closeContextMenu' instead.  */
+
+    for (EmacsWindowAttachmentManager.WindowConsumer consumer
+          : EmacsWindowAttachmentManager.MANAGER.consumers)
+      {
+       if (consumer instanceof EmacsActivity)
+         ((EmacsActivity) consumer).closeContextMenu ();
+      }
+  }
+
+  @Override
+  public synchronized void
+  onDetachedFromWindow ()
+  {
+    isAttachedToWindow = false;
+
+    /* Recycle the bitmap and call GC.  */
+
+    if (bitmap != null)
+      bitmap.recycle ();
+
+    bitmap = null;
+    canvas = null;
+    surfaceView.setBitmap (null, null);
+
+    /* Collect the bitmap storage; it could be large.  */
+    Runtime.getRuntime ().gc ();
+
+    super.onDetachedFromWindow ();
+  }
+
+  @Override
+  public synchronized void
+  onAttachedToWindow ()
+  {
+    isAttachedToWindow = true;
+
+    /* Dirty the bitmap, as it was destroyed when onDetachedFromWindow
+       was called.  */
+    bitmapDirty = true;
+
+    synchronized (dimensionsLock)
+      {
+       /* Now expose the view contents again.  */
+       EmacsNative.sendExpose (this.window.handle, 0, 0,
+                               measuredWidth, measuredHeight);
+      }
+
+    super.onAttachedToWindow ();
+  }
+
+  public void
+  showOnScreenKeyboard ()
+  {
+    /* Specifying no flags at all tells the system the user asked for
+       the input method to be displayed.  */
+
+    imManager.showSoftInput (this, 0);
+    isCurrentlyTextEditor = true;
+  }
+
+  public void
+  hideOnScreenKeyboard ()
+  {
+    imManager.hideSoftInputFromWindow (this.getWindowToken (),
+                                      0);
+    isCurrentlyTextEditor = false;
+  }
+
+  @Override
+  public InputConnection
+  onCreateInputConnection (EditorInfo info)
+  {
+    int mode;
+    int[] selection;
+
+    /* Figure out what kind of IME behavior Emacs wants.  */
+    mode = getICMode ();
+
+    /* Make sure the input method never displays a full screen input
+       box that obscures Emacs.  */
+    info.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
+    info.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI;
+
+    /* Set a reasonable inputType.  */
+    info.inputType = InputType.TYPE_CLASS_TEXT;
+
+    /* If this fails or ANDROID_IC_MODE_NULL was requested, then don't
+       initialize the input connection.  */
+
+    if (mode == EmacsService.IC_MODE_NULL)
+      {
+       info.inputType = InputType.TYPE_NULL;
+       return null;
+      }
+
+    /* Set icSerial.  If icSerial < icGeneration, the input connection
+       has been reset, and future input should be ignored until a new
+       connection is created.  */
+
+    icSerial = icGeneration;
+
+    /* Reset flags set by the previous input method.  */
+
+    EmacsNative.clearInputFlags (window.handle);
+
+    /* Obtain the current position of point and set it as the
+       selection.  Don't do this under one specific situation: if
+       `android_update_ic' is being called in the main thread, trying
+       to synchronize with it can cause a dead lock in the IM manager.
+       See icBeginSynchronous in EmacsService.java for more
+       details.  */
+
+    selection = EmacsService.viewGetSelection (window.handle);
+
+    if (selection != null)
+      Log.d (TAG, "onCreateInputConnection: current selection is: "
+            + selection[0] + ", by " + selection[1]);
+    else
+      {
+       Log.d (TAG, "onCreateInputConnection: current selection could"
+              + " not be retrieved.");
+
+       /* If the selection could not be obtained, return 0 by 0.
+          However, ask for the selection position to be updated as
+          soon as possible.  */
+
+       selection = new int[] { 0, 0, };
+       EmacsNative.requestSelectionUpdate (window.handle);
+      }
+
+    if (mode == EmacsService.IC_MODE_ACTION)
+      info.imeOptions |= EditorInfo.IME_ACTION_DONE;
+
+    /* Set the initial selection fields.  */
+    info.initialSelStart = selection[0];
+    info.initialSelEnd = selection[1];
+
+    /* Create the input connection if necessary.  */
+
+    if (inputConnection == null)
+      inputConnection = new EmacsInputConnection (this);
+    else
+      /* Clear several pieces of state in the input connection.  */
+      inputConnection.reset ();
+
+    /* Return the input connection.  */
+    return inputConnection;
+  }
+
+  @Override
+  public synchronized boolean
+  onCheckIsTextEditor ()
+  {
+    /* If value is true, then the system will display the on screen
+       keyboard.  */
+    return isCurrentlyTextEditor;
+  }
+
+  @Override
+  public boolean
+  isOpaque ()
+  {
+    /* Returning true here allows the system to not draw the contents
+       of windows underneath this view, thereby improving
+       performance.  */
+    return true;
+  }
+
+  public synchronized void
+  setICMode (int icMode)
+  {
+    this.icMode = icMode;
+  }
+
+  public synchronized int
+  getICMode ()
+  {
+    return icMode;
+  }
+
+  @Override
+  public void
+  onGlobalLayout ()
+  {
+    int[] locations;
+
+    /* Get the absolute offset of this view and specify its left and
+       top position in subsequent ConfigureNotify events.  */
+
+    locations = new int[2];
+    getLocationInWindow (locations);
+    window.notifyContentRectPosition (locations[0],
+                                     locations[1]);
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsWindow.java 
b/java/org/gnu/emacs/EmacsWindow.java
new file mode 100644
index 00000000000..a1f70644e16
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -0,0 +1,1445 @@
+/* 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 java.lang.IllegalStateException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import android.content.Context;
+
+import android.graphics.Rect;
+import android.graphics.Canvas;
+import android.graphics.Bitmap;
+import android.graphics.PixelFormat;
+
+import android.view.View;
+import android.view.ViewManager;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.InputDevice;
+import android.view.WindowManager;
+
+import android.util.Log;
+
+import android.os.Build;
+
+/* This defines a window, which is a handle.  Windows represent a
+   rectangular subset of the screen with their own contents.
+
+   Windows either have a parent window, in which case their views are
+   attached to the parent's view, or are "floating", in which case
+   their views are attached to the parent activity (if any), else
+   nothing.
+
+   Views are also drawables, meaning they can accept drawing
+   requests.  */
+
+public final class EmacsWindow extends EmacsHandleObject
+  implements EmacsDrawable
+{
+  private static final String TAG = "EmacsWindow";
+
+  private static class Coordinate
+  {
+    /* Integral coordinate.  */
+    int x, y;
+
+    /* Button associated with the coordinate, or 0 if it is a touch
+       event.  */
+    int button;
+
+    /* Pointer ID associated with the coordinate.  */
+    int id;
+
+    public
+    Coordinate (int x, int y, int button, int id)
+    {
+      this.x = x;
+      this.y = y;
+      this.button = button;
+      this.id = id;
+    }
+  };
+
+  /* The view associated with the window.  */
+  public EmacsView view;
+
+  /* The geometry of the window.  */
+  private Rect rect;
+
+  /* The parent window, or null if it is the root window.  */
+  public EmacsWindow parent;
+
+  /* List of all children in stacking order.  This must be kept
+     consistent with their Z order!  */
+  public ArrayList<EmacsWindow> children;
+
+  /* Map between pointer identifiers and last known position.  Used to
+     compute which pointer changed upon a touch event.  */
+  private HashMap<Integer, Coordinate> pointerMap;
+
+  /* The window consumer currently attached, if it exists.  */
+  private EmacsWindowAttachmentManager.WindowConsumer attached;
+
+  /* The window background scratch GC.  foreground is always the
+     window background.  */
+  private EmacsGC scratchGC;
+
+  /* The button state and keyboard modifier mask at the time of the
+     last button press or release event.  */
+  public int lastButtonState;
+
+  /* Whether or not the window is mapped.  */
+  private volatile boolean isMapped;
+
+  /* Whether or not to ask for focus upon being mapped.  */
+  private boolean dontFocusOnMap;
+
+  /* Whether or not the window is override-redirect.  An
+     override-redirect window always has its own system window.  */
+  private boolean overrideRedirect;
+
+  /* The window manager that is the parent of this window.  NULL if
+     there is no such window manager.  */
+  private WindowManager windowManager;
+
+  /* The time of the last KEYCODE_VOLUME_DOWN release.  This is used
+     to quit Emacs upon two rapid clicks of the volume down
+     button.  */
+  private long lastVolumeButtonRelease;
+
+  /* Linked list of character strings which were recently sent as
+     events.  */
+  public LinkedHashMap<Integer, String> eventStrings;
+
+  /* Whether or not this window is fullscreen.  */
+  public boolean fullscreen;
+
+  /* The window background pixel.  This is used by EmacsView when
+     creating new bitmaps.  */
+  public volatile int background;
+
+  /* The position of this window relative to the root window.  */
+  public int xPosition, yPosition;
+
+  public
+  EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
+              int width, int height, boolean overrideRedirect)
+  {
+    super (handle);
+
+    rect = new Rect (x, y, x + width, y + height);
+    pointerMap = new HashMap<Integer, Coordinate> ();
+
+    /* Create the view from the context's UI thread.  The window is
+       unmapped, so the view is GONE.  */
+    view = EmacsService.SERVICE.getEmacsView (this, View.GONE,
+                                             parent == null);
+    this.parent = parent;
+    this.overrideRedirect = overrideRedirect;
+
+    /* Create the list of children.  */
+    children = new ArrayList<EmacsWindow> ();
+
+    if (parent != null)
+      {
+       parent.children.add (this);
+        EmacsService.SERVICE.runOnUiThread (new Runnable () {
+           @Override
+           public void
+           run ()
+           {
+             parent.view.addView (view);
+           }
+         });
+      }
+
+    scratchGC = new EmacsGC ((short) 0);
+
+    /* Create the map of input method-committed strings.  Keep at most
+       ten strings in the map.  */
+
+    eventStrings
+      = new LinkedHashMap<Integer, String> () {
+         @Override
+         protected boolean
+         removeEldestEntry (Map.Entry<Integer, String> entry)
+         {
+           return size () > 10;
+         }
+       };
+  }
+
+  public void
+  changeWindowBackground (int pixel)
+  {
+    /* scratchGC is used as the argument to a FillRectangles req.  */
+    scratchGC.foreground = pixel;
+    scratchGC.markDirty (false);
+
+    /* Make the background known to the view as well.  */
+    background = pixel;
+  }
+
+  public synchronized Rect
+  getGeometry ()
+  {
+    return new Rect (rect);
+  }
+
+  @Override
+  public synchronized void
+  destroyHandle () throws IllegalStateException
+  {
+    if (parent != null)
+      parent.children.remove (this);
+
+    EmacsActivity.invalidateFocus ();
+
+    if (!children.isEmpty ())
+      throw new IllegalStateException ("Trying to destroy window with "
+                                      + "children!");
+
+    /* Remove the view from its parent and make it invisible.  */
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       public void
+       run ()
+       {
+         ViewManager parent;
+         EmacsWindowAttachmentManager manager;
+
+         if (EmacsActivity.focusedWindow == EmacsWindow.this)
+           EmacsActivity.focusedWindow = null;
+
+         manager = EmacsWindowAttachmentManager.MANAGER;
+         view.setVisibility (View.GONE);
+
+         /* If the window manager is set, use that instead.  */
+         if (windowManager != null)
+           parent = windowManager;
+         else
+           parent = (ViewManager) view.getParent ();
+         windowManager = null;
+
+         if (parent != null)
+           parent.removeView (view);
+
+         manager.detachWindow (EmacsWindow.this);
+       }
+      });
+
+    super.destroyHandle ();
+  }
+
+  public void
+  setConsumer (EmacsWindowAttachmentManager.WindowConsumer consumer)
+  {
+    attached = consumer;
+  }
+
+  public EmacsWindowAttachmentManager.WindowConsumer
+  getAttachedConsumer ()
+  {
+    return attached;
+  }
+
+  public synchronized long
+  viewLayout (int left, int top, int right, int bottom)
+  {
+    int rectWidth, rectHeight;
+
+    rect.left = left;
+    rect.top = top;
+    rect.right = right;
+    rect.bottom = bottom;
+
+    rectWidth = right - left;
+    rectHeight = bottom - top;
+
+    /* If parent is null, use xPosition and yPosition instead of the
+       geometry rectangle positions.  */
+
+    if (parent == null)
+      {
+       left = xPosition;
+       top = yPosition;
+      }
+
+    return EmacsNative.sendConfigureNotify (this.handle,
+                                           System.currentTimeMillis (),
+                                           left, top, rectWidth,
+                                           rectHeight);
+  }
+
+  public void
+  requestViewLayout ()
+  {
+    view.explicitlyDirtyBitmap ();
+
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         if (overrideRedirect)
+           /* Set the layout parameters again.  */
+           view.setLayoutParams (getWindowLayoutParams ());
+
+         view.mustReportLayout = true;
+         view.requestLayout ();
+       }
+      });
+  }
+
+  public synchronized void
+  resizeWindow (int width, int height)
+  {
+    rect.right = rect.left + width;
+    rect.bottom = rect.top + height;
+
+    requestViewLayout ();
+  }
+
+  public synchronized void
+  moveWindow (int x, int y)
+  {
+    int width, height;
+
+    width = rect.width ();
+    height = rect.height ();
+
+    rect.left = x;
+    rect.top = y;
+    rect.right = x + width;
+    rect.bottom = y + height;
+
+    requestViewLayout ();
+  }
+
+  private WindowManager.LayoutParams
+  getWindowLayoutParams ()
+  {
+    WindowManager.LayoutParams params;
+    int flags, type;
+    Rect rect;
+
+    flags = 0;
+    rect = getGeometry ();
+    flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+    flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+    type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+
+    params
+      = new WindowManager.LayoutParams (rect.width (), rect.height (),
+                                       rect.left, rect.top,
+                                       type, flags,
+                                       PixelFormat.RGBA_8888);
+    params.gravity = Gravity.TOP | Gravity.LEFT;
+    return params;
+  }
+
+  private Context
+  findSuitableActivityContext ()
+  {
+    /* Find a recently focused activity.  */
+    if (!EmacsActivity.focusedActivities.isEmpty ())
+      return EmacsActivity.focusedActivities.get (0);
+
+    /* Return the service context, which probably won't work.  */
+    return EmacsService.SERVICE;
+  }
+
+  public synchronized void
+  mapWindow ()
+  {
+    final int width, height;
+
+    if (isMapped)
+      return;
+
+    isMapped = true;
+    width = rect.width ();
+    height = rect.height ();
+
+    if (parent == null)
+      {
+        EmacsService.SERVICE.runOnUiThread (new Runnable () {
+           @Override
+           public void
+           run ()
+           {
+             EmacsWindowAttachmentManager manager;
+             WindowManager windowManager;
+             Context ctx;
+             Object tem;
+             WindowManager.LayoutParams params;
+
+             /* Make the view visible, first of all.  */
+             view.setVisibility (View.VISIBLE);
+
+             if (!overrideRedirect)
+               {
+                 manager = EmacsWindowAttachmentManager.MANAGER;
+
+                 /* If parent is the root window, notice that there are new
+                    children available for interested activites to pick
+                    up.  */
+                 manager.registerWindow (EmacsWindow.this);
+
+                 if (!getDontFocusOnMap ())
+                   /* Eventually this should check no-focus-on-map.  */
+                   view.requestFocus ();
+               }
+             else
+               {
+                 /* But if the window is an override-redirect window,
+                    then:
+
+                    - Find an activity that is currently active.
+
+                    - Map the window as a panel on top of that
+                       activity using the system window manager.  */
+
+                 ctx = findSuitableActivityContext ();
+                 tem = ctx.getSystemService (Context.WINDOW_SERVICE);
+                 windowManager = (WindowManager) tem;
+
+                 /* Calculate layout parameters.  */
+                 params = getWindowLayoutParams ();
+                 view.setLayoutParams (params);
+
+                 /* Attach the view.  */
+                 try
+                   {
+                     view.prepareForLayout (width, height);
+                     windowManager.addView (view, params);
+
+                     /* Record the window manager being used in the
+                        EmacsWindow object.  */
+                     EmacsWindow.this.windowManager = windowManager;
+                   }
+                 catch (Exception e)
+                   {
+                     Log.w (TAG,
+                            "failed to attach override-redirect window, " + e);
+                   }
+               }
+           }
+         });
+      }
+    else
+      {
+       /* Do the same thing as above, but don't register this
+          window.  */
+        EmacsService.SERVICE.runOnUiThread (new Runnable () {
+           @Override
+           public void
+           run ()
+           {
+             /* Prior to mapping the view, set its measuredWidth and
+                measuredHeight to some reasonable value, in order to
+                avoid excessive bitmap dirtying.  */
+
+             view.prepareForLayout (width, height);
+             view.setVisibility (View.VISIBLE);
+
+             if (!getDontFocusOnMap ())
+               view.requestFocus ();
+           }
+         });
+      }
+  }
+
+  public synchronized void
+  unmapWindow ()
+  {
+    if (!isMapped)
+      return;
+
+    isMapped = false;
+
+    view.post (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         EmacsWindowAttachmentManager manager;
+
+         manager = EmacsWindowAttachmentManager.MANAGER;
+
+         view.setVisibility (View.GONE);
+
+         /* Detach the view from the window manager if possible.  */
+         if (windowManager != null)
+           windowManager.removeView (view);
+         windowManager = null;
+
+         /* Now that the window is unmapped, unregister it as
+            well.  */
+         manager.detachWindow (EmacsWindow.this);
+       }
+      });
+  }
+
+  @Override
+  public Canvas
+  lockCanvas (EmacsGC gc)
+  {
+    return view.getCanvas (gc);
+  }
+
+  @Override
+  public void
+  damageRect (Rect damageRect)
+  {
+    view.damageRect (damageRect);
+  }
+
+  public void
+  swapBuffers ()
+  {
+    view.swapBuffers ();
+  }
+
+  public void
+  clearWindow ()
+  {
+    EmacsService.SERVICE.fillRectangle (this, scratchGC,
+                                       0, 0, rect.width (),
+                                       rect.height ());
+  }
+
+  public void
+  clearArea (int x, int y, int width, int height)
+  {
+    EmacsService.SERVICE.fillRectangle (this, scratchGC,
+                                       x, y, width, height);
+  }
+
+  @Override
+  public Bitmap
+  getBitmap ()
+  {
+    return view.getBitmap ();
+  }
+
+  /* event.getCharacters is used because older input methods still
+     require it.  */
+  @SuppressWarnings ("deprecation")
+  public int
+  getEventUnicodeChar (KeyEvent event, int state)
+  {
+    String characters;
+
+    if (event.getUnicodeChar (state) != 0)
+      return event.getUnicodeChar (state);
+
+    characters = event.getCharacters ();
+
+    if (characters != null && characters.length () == 1)
+      return characters.charAt (0);
+
+    return characters == null ? 0 : -1;
+  }
+
+  public void
+  saveUnicodeString (int serial, String string)
+  {
+    eventStrings.put (serial, string);
+  }
+
+
+
+  /* Return the modifier mask associated with the specified keyboard
+     input EVENT.  Replace bits corresponding to Left or Right keys
+     with their corresponding general modifier bits.  */
+
+  private int
+  eventModifiers (KeyEvent event)
+  {
+    int state;
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2)
+      state = event.getModifiers ();
+    else
+      {
+       /* Replace this with getMetaState and manual
+          normalization.  */
+       state = event.getMetaState ();
+
+       /* Normalize the state by setting the generic modifier bit if
+          either a left or right modifier is pressed.  */
+
+       if ((state & KeyEvent.META_ALT_LEFT_ON) != 0
+           || (state & KeyEvent.META_ALT_RIGHT_ON) != 0)
+         state |= KeyEvent.META_ALT_MASK;
+
+       if ((state & KeyEvent.META_CTRL_LEFT_ON) != 0
+           || (state & KeyEvent.META_CTRL_RIGHT_ON) != 0)
+         state |= KeyEvent.META_CTRL_MASK;
+      }
+
+    return state;
+  }
+
+  /* event.getCharacters is used because older input methods still
+     require it.  */
+  @SuppressWarnings ("deprecation")
+  public void
+  onKeyDown (int keyCode, KeyEvent event)
+  {
+    int state, state_1;
+    long serial;
+    String characters;
+
+    state = eventModifiers (event);
+
+    /* Ignore meta-state understood by Emacs for now, or key presses
+       such as Ctrl+C and Meta+C will not be recognized as an ASCII
+       key press event.  */
+
+    state_1
+      = state & ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK
+                 | KeyEvent.META_SYM_ON | KeyEvent.META_META_MASK);
+
+    synchronized (eventStrings)
+      {
+       serial
+         = EmacsNative.sendKeyPress (this.handle,
+                                     event.getEventTime (),
+                                     state, keyCode,
+                                     getEventUnicodeChar (event,
+                                                          state_1));
+
+       characters = event.getCharacters ();
+
+       if (characters != null && characters.length () > 1)
+         saveUnicodeString ((int) serial, characters);
+      }
+  }
+
+  public void
+  onKeyUp (int keyCode, KeyEvent event)
+  {
+    int state, state_1;
+    long time;
+
+    /* Compute the event's modifier mask.  */
+    state = eventModifiers (event);
+
+    /* Ignore meta-state understood by Emacs for now, or key presses
+       such as Ctrl+C and Meta+C will not be recognized as an ASCII
+       key press event.  */
+
+    state_1
+      = state & ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK
+                 | KeyEvent.META_SYM_ON | KeyEvent.META_META_MASK);
+
+    EmacsNative.sendKeyRelease (this.handle,
+                               event.getEventTime (),
+                               state, keyCode,
+                               getEventUnicodeChar (event,
+                                                    state_1));
+
+    if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
+      {
+       /* Check if this volume down press should quit Emacs.
+          Most Android devices have no physical keyboard, so it
+          is unreasonably hard to press C-g.  */
+
+       time = event.getEventTime ();
+
+       if (time - lastVolumeButtonRelease < 350)
+         EmacsNative.quit ();
+
+       lastVolumeButtonRelease = time;
+      }
+  }
+
+  public void
+  onFocusChanged (boolean gainFocus)
+  {
+    EmacsActivity.invalidateFocus ();
+  }
+
+  /* Notice that the activity has been detached or destroyed.
+
+     ISFINISHING is set if the activity is not the main activity, or
+     if the activity was not destroyed in response to explicit user
+     action.  */
+
+  public void
+  onActivityDetached (boolean isFinishing)
+  {
+    /* Destroy the associated frame when the activity is detached in
+       response to explicit user action.  */
+
+    if (isFinishing)
+      EmacsNative.sendWindowAction (this.handle, 0);
+  }
+
+
+
+  /* Mouse and touch event handling.
+
+     Android does not conceptually distinguish between mouse events
+     (those coming from a device whose movement affects the on-screen
+     pointer image) and touch screen events.  Each click or touch
+     starts a single pointer gesture sequence, and subsequent motion
+     of the device will result in updates being reported relative to
+     that sequence until the mouse button or touch is released.
+
+     When a touch, click, or pointer motion takes place, several kinds
+     of event can be sent:
+
+     ACTION_DOWN or ACTION_POINTER_DOWN is sent with a new coordinate
+     and an associated ``pointer ID'' identifying the event and its
+     gesture sequence when a click or touch takes place.  Emacs is
+     responsible for recording both the position and pointer ID of
+     this click for the purpose of determining future changes to its
+     position.
+
+     ACTION_UP or ACTION_POINTER_UP is sent with a pointer ID when the
+     click associated with a previous ACTION_DOWN event is released.
+
+     ACTION_CANCEL (or ACTION_POINTER_UP with FLAG_CANCELED) is sent
+     if a similar situation transpires: the window system has chosen
+     to grab the click, and future changes to its position will no
+     longer be reported to Emacs.
+
+     ACTION_MOVE is sent if a coordinate tied to a click that has not
+     been released changes.  Emacs processes this event by comparing
+     each of the coordinates within the event with its recollection of
+     those contained within prior ACTION_DOWN and ACTION_MOVE events;
+     the pointer ID of the differing coordinate is then reported
+     within a touch or pointer motion event along with its new
+     position.
+
+     The events described above are all sent for both touch and mouse
+     click events.  Determining whether an ACTION_DOWN event is
+     associated with a button event is performed by inspecting the
+     mouse button state associated with that event.  If it contains
+     any mouse buttons that were not contained in the button state at
+     the time of the last ACTION_DOWN or ACTION_UP event, the
+     coordinate contained within is assumed to be a mouse click,
+     leading to it and associated motion or ACTION_UP events being
+     reported as mouse button or motion events.  Otherwise, those
+     events are reported as touch screen events, with the touch ID set
+     to the pointer ID.
+
+     In addition to the events illustrated above, Android also sends
+     several other types of event upon select types of activity from a
+     mouse device:
+
+     ACTION_HOVER_MOVE is sent with the coordinate of the mouse
+     pointer if it moves above a frame prior to any click taking
+     place.  Emacs sends a mouse motion event containing the
+     coordinate.
+
+     ACTION_HOVER_ENTER and ACTION_HOVER_LEAVE are respectively sent
+     when the mouse pointer enters and leaves a frame.  Moreover,
+     ACTION_HOVER_LEAVE events are sent immediately before an
+     ACTION_DOWN event associated with a mouse click.  These
+     extraneous events are distinct in that their button states always
+     contain an additional button compared to the button state
+     recorded at the time of the last ACTION_UP event.
+
+     On Android 6.0 and later, ACTION_BUTTON_PRESS is sent with the
+     coordinate of the mouse pointer if a mouse click occurs,
+     alongside a ACTION_DOWN event.  ACTION_BUTTON_RELEASE is sent
+     with the same information upon a mouse click being released, also
+     accompanying an ACTION_UP event.
+
+     However, both types of button events are implemented in a buggy
+     fashion and cannot be used to report button events.  */
+
+  /* Look through the button state to determine what button EVENT was
+     generated from.  DOWN is true if EVENT is a button press event,
+     false otherwise.  Value is the X number of the button.  */
+
+  private int
+  whatButtonWasIt (MotionEvent event, boolean down)
+  {
+    int eventState, notIn;
+
+    /* Obtain the new button state.  */
+    eventState = event.getButtonState ();
+
+    /* Compute which button is now set or no longer set.  */
+
+    notIn = (down ? eventState & ~lastButtonState
+            : lastButtonState & ~eventState);
+
+    if ((notIn & (MotionEvent.BUTTON_PRIMARY
+                 | MotionEvent.BUTTON_SECONDARY
+                 | MotionEvent.BUTTON_TERTIARY)) == 0)
+      /* No buttons have been pressed, so this is a touch event.  */
+      return 0;
+
+    if ((notIn & MotionEvent.BUTTON_PRIMARY) != 0)
+      return 1;
+
+    if ((notIn & MotionEvent.BUTTON_SECONDARY) != 0)
+      return 3;
+
+    if ((notIn & MotionEvent.BUTTON_TERTIARY) != 0)
+      return 2;
+
+    /* Buttons 4, 5, 6 and 7 are actually scroll wheels under X.
+       Thus, report additional buttons starting at 8.  */
+
+    if ((notIn & MotionEvent.BUTTON_BACK) != 0)
+      return 8;
+
+    if ((notIn & MotionEvent.BUTTON_FORWARD) != 0)
+      return 9;
+
+    /* Report stylus events as touch screen events.  */
+
+    if ((notIn & MotionEvent.BUTTON_STYLUS_PRIMARY) != 0)
+      return 0;
+
+    if ((notIn & MotionEvent.BUTTON_STYLUS_SECONDARY) != 0)
+      return 0;
+
+    /* Not a real value.  */
+    return 11;
+  }
+
+  /* Return the mouse button associated with the specified ACTION_DOWN
+     or ACTION_POINTER_DOWN EVENT.
+
+     Value is 0 if no mouse button was pressed, or the X number of
+     that mouse button.  */
+
+  private int
+  buttonForEvent (MotionEvent event)
+  {
+    /* ICS and earlier don't support true mouse button events, so
+       treat all down events as touch screen events.  */
+
+    if (Build.VERSION.SDK_INT
+       < Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+      return 0;
+
+    return whatButtonWasIt (event, true);
+  }
+
+  /* Return the coordinate object associated with the specified
+     EVENT, or null if it is not known.  */
+
+  private Coordinate
+  figureChange (MotionEvent event)
+  {
+    int i, truncatedX, truncatedY, pointerIndex, pointerID, count;
+    Coordinate coordinate;
+
+    /* Initialize this variable now.  */
+    coordinate = null;
+
+    switch (event.getActionMasked ())
+      {
+      case MotionEvent.ACTION_DOWN:
+       /* Primary pointer pressed with index 0.  */
+
+       pointerID = event.getPointerId (0);
+       coordinate = new Coordinate ((int) event.getX (0),
+                                    (int) event.getY (0),
+                                    buttonForEvent (event),
+                                    pointerID);
+       pointerMap.put (pointerID, coordinate);
+       break;
+
+      case MotionEvent.ACTION_UP:
+      case MotionEvent.ACTION_CANCEL:
+       /* Primary pointer released with index 0.  */
+       pointerID = event.getPointerId (0);
+       coordinate = pointerMap.remove (pointerID);
+       break;
+
+      case MotionEvent.ACTION_POINTER_DOWN:
+       /* New pointer.  Find the pointer ID from the index and place
+          it in the map.  */
+       pointerIndex = event.getActionIndex ();
+       pointerID = event.getPointerId (pointerIndex);
+       coordinate = new Coordinate ((int) event.getX (0),
+                                    (int) event.getY (0),
+                                    buttonForEvent (event),
+                                    pointerID);
+       pointerMap.put (pointerID, coordinate);
+       break;
+
+      case MotionEvent.ACTION_POINTER_UP:
+       /* Pointer removed.  Remove it from the map.  */
+       pointerIndex = event.getActionIndex ();
+       pointerID = event.getPointerId (pointerIndex);
+       coordinate = pointerMap.remove (pointerID);
+       break;
+
+      default:
+
+       /* Loop through each pointer in the event.  */
+
+       count = event.getPointerCount ();
+       for (i = 0; i < count; ++i)
+         {
+           pointerID = event.getPointerId (i);
+
+           /* Look up that pointer in the map.  */
+           coordinate = pointerMap.get (pointerID);
+
+           if (coordinate != null)
+             {
+               /* See if coordinates have changed.  */
+               truncatedX = (int) event.getX (i);
+               truncatedY = (int) event.getY (i);
+
+               if (truncatedX != coordinate.x
+                   || truncatedY != coordinate.y)
+                 {
+                   /* The pointer changed.  Update the coordinate and
+                      break out of the loop.  */
+                   coordinate.x = truncatedX;
+                   coordinate.y = truncatedY;
+
+                   break;
+                 }
+             }
+         }
+
+       /* Set coordinate to NULL if the loop failed to find any
+          matching pointer.  */
+
+       if (i == count)
+         coordinate = null;
+      }
+
+    /* Return the pointer ID.  */
+    return coordinate;
+  }
+
+  /* Return the modifier mask associated with the specified motion
+     EVENT.  Replace bits corresponding to Left or Right keys with
+     their corresponding general modifier bits.  */
+
+  private int
+  motionEventModifiers (MotionEvent event)
+  {
+    int state;
+
+    state = event.getMetaState ();
+
+    /* Normalize the state by setting the generic modifier bit if
+       either a left or right modifier is pressed.  */
+
+    if ((state & KeyEvent.META_ALT_LEFT_ON) != 0
+       || (state & KeyEvent.META_ALT_RIGHT_ON) != 0)
+      state |= KeyEvent.META_ALT_MASK;
+
+    if ((state & KeyEvent.META_CTRL_LEFT_ON) != 0
+       || (state & KeyEvent.META_CTRL_RIGHT_ON) != 0)
+      state |= KeyEvent.META_CTRL_MASK;
+
+    return state;
+  }
+
+  /* Process a single ACTION_DOWN, ACTION_POINTER_DOWN, ACTION_UP,
+     ACTION_POINTER_UP, ACTION_CANCEL, or ACTION_MOVE event.
+
+     Ascertain which coordinate changed and send an appropriate mouse
+     or touch screen event.  */
+
+  private void
+  motionEvent (MotionEvent event)
+  {
+    Coordinate coordinate;
+    int modifiers;
+    long time;
+
+    /* Find data associated with this event's pointer.  Namely, its
+       current location, whether or not a change has taken place, and
+       whether or not it is a button event.  */
+
+    coordinate = figureChange (event);
+
+    if (coordinate == null)
+      return;
+
+    time = event.getEventTime ();
+
+    if (coordinate.button != 0)
+      {
+       /* This event is tied to a mouse click, so report mouse motion
+          and button events.  */
+
+       modifiers = motionEventModifiers (event);
+
+       switch (event.getAction ())
+         {
+         case MotionEvent.ACTION_POINTER_DOWN:
+         case MotionEvent.ACTION_DOWN:
+           EmacsNative.sendButtonPress (this.handle, coordinate.x,
+                                        coordinate.y, time, modifiers,
+                                        coordinate.button);
+           break;
+
+         case MotionEvent.ACTION_POINTER_UP:
+         case MotionEvent.ACTION_UP:
+         case MotionEvent.ACTION_CANCEL:
+           EmacsNative.sendButtonRelease (this.handle, coordinate.x,
+                                          coordinate.y, time, modifiers,
+                                          coordinate.button);
+           break;
+
+         case MotionEvent.ACTION_MOVE:
+           EmacsNative.sendMotionNotify (this.handle, coordinate.x,
+                                         coordinate.y, time);
+           break;
+         }
+      }
+    else
+      {
+       /* This event is a touch event, and the touch ID is the
+          pointer ID.  */
+
+       switch (event.getActionMasked ())
+         {
+         case MotionEvent.ACTION_DOWN:
+         case MotionEvent.ACTION_POINTER_DOWN:
+           /* Touch down event.  */
+           EmacsNative.sendTouchDown (this.handle, coordinate.x,
+                                      coordinate.y, time,
+                                      coordinate.id, 0);
+           break;
+
+         case MotionEvent.ACTION_UP:
+         case MotionEvent.ACTION_POINTER_UP:
+           /* Touch up event.  */
+           EmacsNative.sendTouchUp (this.handle, coordinate.x,
+                                    coordinate.y, time,
+                                    coordinate.id, 0);
+           break;
+
+         case MotionEvent.ACTION_CANCEL:
+           /* Touch sequence cancellation event.  */
+           EmacsNative.sendTouchUp (this.handle, coordinate.x,
+                                    coordinate.y, time,
+                                    coordinate.id,
+                                    1 /* ANDROID_TOUCH_SEQUENCE_CANCELED */);
+           break;
+
+         case MotionEvent.ACTION_MOVE:
+           /* Pointer motion event.  */
+           EmacsNative.sendTouchMove (this.handle, coordinate.x,
+                                      coordinate.y, time,
+                                      coordinate.id, 0);
+           break;
+         }
+      }
+
+    if (Build.VERSION.SDK_INT
+       < Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+      return;
+
+    /* Now update the button state.  */
+    lastButtonState = event.getButtonState ();
+    return;
+  }
+
+  public boolean
+  onTouchEvent (MotionEvent event)
+  {
+    switch (event.getActionMasked ())
+      {
+      case MotionEvent.ACTION_DOWN:
+      case MotionEvent.ACTION_POINTER_DOWN:
+      case MotionEvent.ACTION_UP:
+      case MotionEvent.ACTION_POINTER_UP:
+      case MotionEvent.ACTION_CANCEL:
+      case MotionEvent.ACTION_MOVE:
+       motionEvent (event);
+       return true;
+      }
+
+    return false;
+  }
+
+  public boolean
+  onGenericMotionEvent (MotionEvent event)
+  {
+    switch (event.getAction ())
+      {
+      case MotionEvent.ACTION_HOVER_ENTER:
+       EmacsNative.sendEnterNotify (this.handle, (int) event.getX (),
+                                    (int) event.getY (),
+                                    event.getEventTime ());
+       return true;
+
+      case MotionEvent.ACTION_HOVER_MOVE:
+       EmacsNative.sendMotionNotify (this.handle, (int) event.getX (),
+                                     (int) event.getY (),
+                                     event.getEventTime ());
+       return true;
+
+      case MotionEvent.ACTION_HOVER_EXIT:
+
+       /* If the exit event comes from a button press, its button
+          state will have extra bits compared to the last known
+          button state.  Since the exit event will interfere with
+          tool bar button presses, ignore such splurious events.  */
+
+       if ((event.getButtonState () & ~lastButtonState) == 0)
+         EmacsNative.sendLeaveNotify (this.handle, (int) event.getX (),
+                                      (int) event.getY (),
+                                      event.getEventTime ());
+
+       return true;
+
+      case MotionEvent.ACTION_DOWN:
+      case MotionEvent.ACTION_POINTER_DOWN:
+      case MotionEvent.ACTION_UP:
+      case MotionEvent.ACTION_POINTER_UP:
+      case MotionEvent.ACTION_CANCEL:
+      case MotionEvent.ACTION_MOVE:
+       /* MotionEvents may either be sent to onGenericMotionEvent or
+          onTouchEvent depending on if Android thinks it is a mouse
+          event or not, but we detect them ourselves.  */
+       motionEvent (event);
+       return true;
+
+      case MotionEvent.ACTION_SCROLL:
+       /* Send a scroll event with the specified deltas.  */
+       EmacsNative.sendWheel (this.handle, (int) event.getX (),
+                              (int) event.getY (),
+                              event.getEventTime (),
+                              motionEventModifiers (event),
+                              event.getAxisValue (MotionEvent.AXIS_HSCROLL),
+                              event.getAxisValue (MotionEvent.AXIS_VSCROLL));
+       return true;
+      }
+
+    return false;
+  }
+
+
+
+  public synchronized void
+  reparentTo (final EmacsWindow otherWindow, int x, int y)
+  {
+    int width, height;
+
+    /* Reparent this window to the other window.  */
+
+    if (parent != null)
+      parent.children.remove (this);
+
+    if (otherWindow != null)
+      otherWindow.children.add (this);
+
+    parent = otherWindow;
+
+    /* Move this window to the new location.  */
+    width = rect.width ();
+    height = rect.height ();
+    rect.left = x;
+    rect.top = y;
+    rect.right = x + width;
+    rect.bottom = y + height;
+
+    /* Now do the work necessary on the UI thread to reparent the
+       window.  */
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         EmacsWindowAttachmentManager manager;
+         ViewManager parent;
+
+         /* First, detach this window if necessary.  */
+         manager = EmacsWindowAttachmentManager.MANAGER;
+         manager.detachWindow (EmacsWindow.this);
+
+         /* Also unparent this view.  */
+
+         /* If the window manager is set, use that instead.  */
+         if (windowManager != null)
+           parent = windowManager;
+         else
+           parent = (ViewManager) view.getParent ();
+         windowManager = null;
+
+         if (parent != null)
+           parent.removeView (view);
+
+         /* Next, either add this window as a child of the new
+            parent's view, or make it available again.  */
+         if (otherWindow != null)
+           otherWindow.view.addView (view);
+         else if (EmacsWindow.this.isMapped)
+           manager.registerWindow (EmacsWindow.this);
+
+         /* Request relayout.  */
+         view.requestLayout ();
+       }
+      });
+  }
+
+  public void
+  makeInputFocus (long time)
+  {
+    /* TIME is currently ignored.  Request the input focus now.  */
+
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         view.requestFocus ();
+       }
+      });
+  }
+
+  public synchronized void
+  raise ()
+  {
+    /* This does nothing here.  */
+    if (parent == null)
+      return;
+
+    /* Remove and add this view again.  */
+    parent.children.remove (this);
+    parent.children.add (this);
+
+    /* Request a relayout.  */
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         view.raise ();
+       }
+      });
+  }
+
+  public synchronized void
+  lower ()
+  {
+    /* This does nothing here.  */
+    if (parent == null)
+      return;
+
+    /* Remove and add this view again.  */
+    parent.children.remove (this);
+    parent.children.add (this);
+
+    /* Request a relayout.  */
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         view.lower ();
+       }
+      });
+  }
+
+  public synchronized int[]
+  getWindowGeometry ()
+  {
+    int[] array;
+
+    array = new int[4];
+
+    array[0] = parent != null ? rect.left : xPosition;
+    array[1] = parent != null ? rect.top : yPosition;
+    array[2] = rect.width ();
+    array[3] = rect.height ();
+
+    return array;
+  }
+
+  public void
+  noticeIconified ()
+  {
+    EmacsNative.sendIconified (this.handle);
+  }
+
+  public void
+  noticeDeiconified ()
+  {
+    EmacsNative.sendDeiconified (this.handle);
+  }
+
+  public synchronized void
+  setDontAcceptFocus (final boolean dontAcceptFocus)
+  {
+    /* Update the view's focus state.  */
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         view.setFocusable (!dontAcceptFocus);
+         view.setFocusableInTouchMode (!dontAcceptFocus);
+       }
+      });
+  }
+
+  public synchronized void
+  setDontFocusOnMap (final boolean dontFocusOnMap)
+  {
+    this.dontFocusOnMap = dontFocusOnMap;
+  }
+
+  public synchronized boolean
+  getDontFocusOnMap ()
+  {
+    return dontFocusOnMap;
+  }
+
+  public int[]
+  translateCoordinates (int x, int y)
+  {
+    int[] array;
+
+    /* This is supposed to translate coordinates to the root
+       window.  */
+    array = new int[2];
+    EmacsService.SERVICE.getLocationOnScreen (view, array);
+
+    /* Now, the coordinates of the view should be in array.  Offset X
+       and Y by them.  */
+    array[0] += x;
+    array[1] += y;
+
+    /* Return the resulting coordinates.  */
+    return array;
+  }
+
+  public void
+  toggleOnScreenKeyboard (final boolean on)
+  {
+    /* Even though InputMethodManager functions are thread safe,
+       `showOnScreenKeyboard' etc must be called from the UI thread in
+       order to avoid deadlocks if the calls happen in tandem with a
+       call to a synchronizing function within
+       `onCreateInputConnection'.  */
+
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         if (on)
+           view.showOnScreenKeyboard ();
+         else
+           view.hideOnScreenKeyboard ();         
+       }
+      });
+  }
+
+  public String
+  lookupString (int eventSerial)
+  {
+    String any;
+
+    synchronized (eventStrings)
+      {
+       any = eventStrings.remove (eventSerial);
+      }
+
+    return any;
+  }
+
+  public void
+  setFullscreen (final boolean isFullscreen)
+  {
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         EmacsActivity activity;
+         Object tem;
+
+         fullscreen = isFullscreen;
+         tem = getAttachedConsumer ();
+
+         if (tem != null)
+           {
+             activity = (EmacsActivity) tem;
+             activity.syncFullscreenWith (EmacsWindow.this);
+           }
+       }
+      });
+  }
+
+  public void
+  defineCursor (final EmacsCursor cursor)
+  {
+    /* Don't post this message if pointer icons aren't supported.  */
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+      view.post (new Runnable () {
+         @Override
+         public void
+         run ()
+         {
+           if (cursor != null)
+             view.setPointerIcon (cursor.icon);
+           else
+             view.setPointerIcon (null);
+         }
+       });
+  }
+
+  public synchronized void
+  notifyContentRectPosition (int xPosition, int yPosition)
+  {
+    Rect geometry;
+
+    /* Ignore these notifications if not a child of the root
+       window.  */
+    if (parent != null)
+      return;
+
+    /* xPosition and yPosition are the position of this window
+       relative to the screen.  Set them and request a ConfigureNotify
+       event.  */
+
+    if (this.xPosition != xPosition
+       || this.yPosition != yPosition)
+      {
+       this.xPosition = xPosition;
+       this.yPosition = yPosition;
+
+       EmacsNative.sendConfigureNotify (this.handle,
+                                        System.currentTimeMillis (),
+                                        xPosition, yPosition,
+                                        rect.width (), rect.height ());
+      }
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java 
b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
new file mode 100644
index 00000000000..bc96de7fe1a
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
@@ -0,0 +1,230 @@
+/* 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 java.util.ArrayList;
+import java.util.List;
+
+import android.app.ActivityOptions;
+import android.content.Intent;
+import android.os.Build;
+import android.util.Log;
+
+/* Code to paper over the differences in lifecycles between
+   "activities" and windows.  There are four interfaces to an instance
+   of this class:
+
+     registerWindowConsumer (WindowConsumer)
+     registerWindow (EmacsWindow)
+     removeWindowConsumer (WindowConsumer)
+     removeWindow (EmacsWindow)
+
+   A WindowConsumer is expected to allow an EmacsWindow to be attached
+   to it, and be created or destroyed.
+
+   Every time a window is created, registerWindow checks the list of
+   window consumers.  If a consumer exists and does not currently have
+   a window of its own attached, it gets the new window.  Otherwise,
+   the window attachment manager starts a new consumer.
+
+   Every time a consumer is registered, registerWindowConsumer checks
+   the list of available windows.  If a window exists and is not
+   currently attached to a consumer, then the consumer gets it.
+
+   Finally, every time a window is removed, the consumer is
+   destroyed.  */
+
+public final class EmacsWindowAttachmentManager
+{
+  private final static String TAG = "EmacsWindowAttachmentManager";
+
+  /* The single window attachment manager ``object''.  */
+  public static final EmacsWindowAttachmentManager MANAGER;
+
+  static
+  {
+    MANAGER = new EmacsWindowAttachmentManager ();
+  };
+
+  public interface WindowConsumer
+  {
+    public void attachWindow (EmacsWindow window);
+    public EmacsWindow getAttachedWindow ();
+    public void detachWindow ();
+    public void destroy ();
+  };
+
+  /* List of currently attached window consumers.  */
+  public List<WindowConsumer> consumers;
+
+  /* List of currently attached windows.  */
+  public List<EmacsWindow> windows;
+
+  public
+  EmacsWindowAttachmentManager ()
+  {
+    consumers = new ArrayList<WindowConsumer> ();
+    windows = new ArrayList<EmacsWindow> ();
+  }
+
+  public void
+  registerWindowConsumer (WindowConsumer consumer)
+  {
+    Log.d (TAG, "registerWindowConsumer " + consumer);
+
+    consumers.add (consumer);
+
+    for (EmacsWindow window : windows)
+      {
+       if (window.getAttachedConsumer () == null)
+         {
+           Log.d (TAG, "registerWindowConsumer: attaching " + window);
+           consumer.attachWindow (window);
+           return;
+         }
+      }
+
+    Log.d (TAG, "registerWindowConsumer: sendWindowAction 0, 0");
+    EmacsNative.sendWindowAction ((short) 0, 0);
+  }
+
+  public synchronized void
+  registerWindow (EmacsWindow window)
+  {
+    Intent intent;
+    ActivityOptions options;
+
+    Log.d (TAG, "registerWindow (maybe): " + window);
+
+    if (windows.contains (window))
+      /* The window is already registered.  */
+      return;
+
+    Log.d (TAG, "registerWindow: " + window);
+
+    windows.add (window);
+
+    for (WindowConsumer consumer : consumers)
+      {
+       if (consumer.getAttachedWindow () == null)
+         {
+           Log.d (TAG, "registerWindow: attaching " + consumer);
+           consumer.attachWindow (window);
+           return;
+         }
+      }
+
+    intent = new Intent (EmacsService.SERVICE,
+                        EmacsMultitaskActivity.class);
+    intent.addFlags (Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+                    | Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
+      EmacsService.SERVICE.startActivity (intent);
+    else
+      {
+       /* Specify the desired window size.  */
+       options = ActivityOptions.makeBasic ();
+       options.setLaunchBounds (window.getGeometry ());
+       EmacsService.SERVICE.startActivity (intent,
+                                           options.toBundle ());
+      }
+
+    Log.d (TAG, "registerWindow: startActivity");
+  }
+
+  public void
+  removeWindowConsumer (WindowConsumer consumer, boolean isFinishing)
+  {
+    EmacsWindow window;
+
+    Log.d (TAG, "removeWindowConsumer " + consumer);
+
+    window = consumer.getAttachedWindow ();
+
+    if (window != null)
+      {
+       Log.d (TAG, "removeWindowConsumer: detaching " + window);
+
+       consumer.detachWindow ();
+       window.onActivityDetached (isFinishing);
+      }
+
+    Log.d (TAG, "removeWindowConsumer: removing " + consumer);
+    consumers.remove (consumer);
+  }
+
+  public synchronized void
+  detachWindow (EmacsWindow window)
+  {
+    WindowConsumer consumer;
+
+    Log.d (TAG, "detachWindow " + window);
+
+    if (window.getAttachedConsumer () != null)
+      {
+       consumer = window.getAttachedConsumer ();
+
+       Log.d (TAG, "detachWindow: removing" + consumer);
+
+       consumers.remove (consumer);
+       consumer.destroy ();
+      }
+
+    windows.remove (window);
+  }
+
+  public void
+  noticeIconified (WindowConsumer consumer)
+  {
+    EmacsWindow window;
+
+    Log.d (TAG, "noticeIconified " + consumer);
+
+    /* If a window is attached, send the appropriate iconification
+       events.  */
+    window = consumer.getAttachedWindow ();
+
+    if (window != null)
+      window.noticeIconified ();
+  }
+
+  public void
+  noticeDeiconified (WindowConsumer consumer)
+  {
+    EmacsWindow window;
+
+    Log.d (TAG, "noticeDeiconified " + consumer);
+
+    /* If a window is attached, send the appropriate iconification
+       events.  */
+    window = consumer.getAttachedWindow ();
+
+    if (window != null)
+      window.noticeDeiconified ();
+  }
+
+  public synchronized List<EmacsWindow>
+  copyWindows ()
+  {
+    return new ArrayList<EmacsWindow> (windows);
+  }
+};
diff --git a/java/res/drawable/emacs.png b/java/res/drawable/emacs.png
new file mode 100644
index 00000000000..9ab43d704be
Binary files /dev/null and b/java/res/drawable/emacs.png differ
diff --git a/java/res/drawable/emacs_wrench.png 
b/java/res/drawable/emacs_wrench.png
new file mode 100644
index 00000000000..50572d3bed1
Binary files /dev/null and b/java/res/drawable/emacs_wrench.png differ
diff --git a/java/res/values-v11/style.xml b/java/res/values-v11/style.xml
new file mode 100644
index 00000000000..b114758bf0d
--- /dev/null
+++ b/java/res/values-v11/style.xml
@@ -0,0 +1,24 @@
+<!-- Style resources for GNU Emacs on Android.
+
+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/>. -->
+
+<resources>
+  <!-- Style used for popup menus and relatives on Android 3.x.  -->
+  <style name="EmacsStyle" parent="@android:style/Theme.Holo.NoActionBar"/>
+  <style name="EmacsStyleOpen" parent="@android:style/Theme.Holo"/>
+</resources>
diff --git a/java/res/values-v14/style.xml b/java/res/values-v14/style.xml
new file mode 100644
index 00000000000..2cb54dc301b
--- /dev/null
+++ b/java/res/values-v14/style.xml
@@ -0,0 +1,25 @@
+<!-- Style resources for GNU Emacs on Android.
+
+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/>. -->
+
+<resources>
+  <!-- Style used for popup menus and relatives between Android 4.0
+       and Android 10.  -->
+  <style name="EmacsStyle" 
parent="@android:style/Theme.DeviceDefault.NoActionBar"/>
+  <style name="EmacsStyleOpen" parent="@android:style/Theme.DeviceDefault"/>
+</resources>
diff --git a/java/res/values-v19/bool.xml b/java/res/values-v19/bool.xml
new file mode 100644
index 00000000000..a4e3a87ae71
--- /dev/null
+++ b/java/res/values-v19/bool.xml
@@ -0,0 +1,22 @@
+<!-- Boolean resources for GNU Emacs on Android 4.4 or later.
+
+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/>. -->
+
+<resources>
+  <bool name="isAtLeastKitKat">true</bool>
+</resources>
diff --git a/java/res/values-v24/bool.xml b/java/res/values-v24/bool.xml
new file mode 100644
index 00000000000..37f07992995
--- /dev/null
+++ b/java/res/values-v24/bool.xml
@@ -0,0 +1,22 @@
+<!-- Boolean resources for GNU Emacs on Android.
+
+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/>. -->
+
+<resources>
+  <bool name="isBeforeNougat">false</bool>
+</resources>
diff --git a/java/res/values-v29/style.xml b/java/res/values-v29/style.xml
new file mode 100644
index 00000000000..ec7b8d14554
--- /dev/null
+++ b/java/res/values-v29/style.xml
@@ -0,0 +1,32 @@
+<!-- Style resources for GNU Emacs on Android.
+
+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/>. -->
+
+<resources>
+  <!-- Style used for popup menus and relatives from Android 10.0
+       onwards-->
+  <style name="EmacsStyle" 
parent="@android:style/Theme.DeviceDefault.DayNight">
+    <item name="android:windowActionBar">false</item>
+    <item name="android:windowNoTitle">true</item>
+
+    <!-- Required to make sure the status bar text remains legible.  -->
+    <item name="android:statusBarColor">@android:color/black</item>
+  </style>
+  <style name="EmacsStyleOpen"
+        parent="@android:style/Theme.DeviceDefault.DayNight"/>
+</resources>
diff --git a/java/res/values/bool.xml b/java/res/values/bool.xml
new file mode 100644
index 00000000000..2b253824e29
--- /dev/null
+++ b/java/res/values/bool.xml
@@ -0,0 +1,23 @@
+<!-- Boolean resources for GNU Emacs on Android.
+
+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/>. -->
+
+<resources>
+  <bool name="isAtLeastKitKat">false</bool>
+  <bool name="isBeforeNougat">true</bool>
+</resources>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
new file mode 100644
index 00000000000..8a11cb007ee
--- /dev/null
+++ b/java/res/values/strings.xml
@@ -0,0 +1,45 @@
+<!-- String resources used by GNU Emacs on Android.
+
+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/>. -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string name="start_quick_title">
+    Restart Emacs with -Q
+  </string>
+  <string name="start_quick_caption">
+    Restart Emacs, but do not load site lisp or init files.
+  </string>
+  <string name="start_debug_init_title">
+    Restart Emacs with --debug-init
+  </string>
+  <string name="start_debug_init_caption">
+    Restart Emacs, and display the debugger should an error occur while 
loading initialization files.
+  </string>
+  <string name="erase_dump_title">
+    Delete dump file
+  </string>
+  <string name="erase_dump_caption">
+    Remove the dumped state created when Emacs was installed.
+  </string>
+
+  <!-- This resource describes the purpose of any `sharedUserId'
+       specified at configure-time.  -->
+  <string name="shared_user_name">
+    Emacs shared user
+  </string>
+</resources>
diff --git a/java/res/values/style.xml b/java/res/values/style.xml
new file mode 100644
index 00000000000..498e844fda0
--- /dev/null
+++ b/java/res/values/style.xml
@@ -0,0 +1,26 @@
+<!-- Style resources for GNU Emacs on Android.
+
+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/>. -->
+
+<resources>
+  <!-- Style used for popup menus and relatives on Android 2.2 and
+       2.3.  Styles used for newer Android versions are defined in
+       the res/values- directories for their respective API levels. -->
+  <style name="EmacsStyle" parent="@android:style/Theme.NoTitleBar"/>
+  <style name="EmacsStyleOpen" parent="@android:style/Theme"/>
+</resources>
diff --git a/java/res/xml/preferences.xml b/java/res/xml/preferences.xml
new file mode 100644
index 00000000000..d52d28816e5
--- /dev/null
+++ b/java/res/xml/preferences.xml
@@ -0,0 +1,30 @@
+<!-- Descriptions for the preferences screen for GNU Emacs on Android.
+
+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/>. -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android";>
+    <Preference android:key="start_quick"
+               android:title="@string/start_quick_title"
+               android:summary="@string/start_quick_caption"/>
+    <Preference android:key="start_debug_init"
+               android:title="@string/start_debug_init_title"
+               android:summary="@string/start_debug_init_caption"/>
+    <Preference android:key="erase_dump"
+               android:title="@string/erase_dump_title"
+               android:summary="@string/erase_dump_caption"/>
+</PreferenceScreen>
diff --git a/lib-src/Makefile.in b/lib-src/Makefile.in
index 5b82cf1151c..8a5ce019ca2 100644
--- a/lib-src/Makefile.in
+++ b/lib-src/Makefile.in
@@ -96,6 +96,14 @@ localstatedir=@localstatedir@
 srcdir=@srcdir@
 VPATH=@srcdir@
 
+# Cross-compilation setup
+
+XCONFIGURE=@XCONFIGURE@
+
+ifneq ($(XCONFIGURE),)
+vpath $(srcdir)
+endif
+
 # The top-level source directory, also set by configure.
 top_srcdir=@top_srcdir@
 # MinGW CPPFLAGS may use this.
@@ -140,6 +148,9 @@ HAVE_BE_APP=@HAVE_BE_APP@
 HAIKU_LIBS=@HAIKU_LIBS@
 HAIKU_CFLAGS=@HAIKU_CFLAGS@
 
+## Android build-time support
+ANDROID=@ANDROID@
+
 # emacsclientw.exe for MinGW, empty otherwise
 CLIENTW = @CLIENTW@
 
@@ -156,8 +167,13 @@ UTILITIES = hexl${EXEEXT}                           \
 ifeq ($(HAVE_BE_APP),yes)
 DONT_INSTALL= make-docfile${EXEEXT} make-fingerprint${EXEEXT} be-resources
 else
+ifeq ($(XCONFIGURE)$(HAVE_ANDROID),yes)
+DONT_INSTALL = make-docfile${EXEEXT} make-fingerprint${EXEEXT} \
+       asset-directory-tool${EXEEXT}
+else
 DONT_INSTALL= make-docfile${EXEEXT} make-fingerprint${EXEEXT}
 endif
+endif
 
 # Like UTILITIES, but they're not system-dependent, and should not be
 #  deleted by the distclean target.
@@ -374,7 +390,7 @@ clean: mostlyclean
        rm -f ${EXE_FILES}
 
 distclean: clean
-       rm -f TAGS Makefile blessmail
+       rm -f TAGS Makefile blessmail Makefile.android
 
 bootstrap-clean maintainer-clean: distclean
 
@@ -406,6 +422,9 @@ etags${EXEEXT}: ${etags_deps}
 ctags${EXEEXT}: ${srcdir}/ctags.c ${etags_deps}
        $(AM_V_CCLD)$(CC) ${ALL_CFLAGS} -o $@ $< $(etags_libs)
 
+asset-directory-tool${EXEEXT}: ${srcdir}/asset-directory-tool.c $(config_h)
+       $(AM_V_CCLD)$(CC) ${ALL_CFLAGS} $< $(LOADLIBES) -o $@
+
 ebrowse${EXEEXT}: ${srcdir}/ebrowse.c ${srcdir}/../lib/min-max.h $(NTLIB) \
                    $(config_h)
        $(AM_V_CCLD)$(CC) ${ALL_CFLAGS} -o $@ $< $(NTLIB) $(LOADLIBES)
@@ -462,7 +481,7 @@ emacsclient.res: ../nt/emacsclient.rc 
$(NTINC)/../icons/emacs.ico
 ifeq ($(SECCOMP_FILTER),1)
 seccomp-filter$(EXEEXT): $(srcdir)/seccomp-filter.c $(config_h)
        $(AM_V_CCLD)$(CC) $(ALL_CFLAGS) $(LIBSECCOMP_CFLAGS) $< \
-         $(LIBSECCOMP_LIBS) -o $@
+         $(LIBSECCOMP_LIBS) $(LOADLIBES) -o $@
 
 seccomp-filter.bpf seccomp-filter.pfc seccomp-filter-exec.bpf 
seccomp-filter-exec.pfc: seccomp-filter$(EXEEXT)
        $(AM_V_GEN)./seccomp-filter$(EXEEXT) \
diff --git a/lib-src/asset-directory-tool.c b/lib-src/asset-directory-tool.c
new file mode 100644
index 00000000000..99c954a3922
--- /dev/null
+++ b/lib-src/asset-directory-tool.c
@@ -0,0 +1,289 @@
+/* Android asset directory tool.
+
+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 <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/stat.h>
+
+/* This program takes a directory as input, and generates a
+   ``directory-tree'' file suitable for inclusion in an Android
+   application package.
+
+   Such a file records the layout of the `assets' directory in the
+   package.  Emacs records this information itself and uses it in the
+   Android emulation of readdir, because the system asset manager APIs
+   are routinely buggy, and are often unable to locate directories or
+   files.
+
+   The file is packed, with no data alignment guarantees made.  The
+   file starts with the bytes "EMACS", following which is the name of
+   the first file or directory, a NULL byte and an unsigned int
+   indicating the offset from the start of the file to the start of
+   the next sibling.  Following that is a list of subdirectories or
+   files in the same format.  The long is stored LSB first.
+
+   Directories can be distinguished from ordinary files through the
+   last bytes of their file names (immediately previous to their
+   terminating NULL bytes), which are set to the directory separator
+   character `/'.  */
+
+
+
+struct directory_tree
+{
+  /* The offset to the next sibling.  */
+  size_t offset;
+
+  /* The name of this directory or file.  */
+  char *name;
+
+  /* Subdirectories and files inside this directory.  */
+  struct directory_tree *children, *next;
+};
+
+/* Exit with EXIT_FAILURE, after printing a description of a failing
+   function WHAT along with the details of the error.  */
+
+static _Noreturn void
+croak (const char *what)
+{
+  perror (what);
+  exit (EXIT_FAILURE);
+}
+
+/* Like malloc, but aborts on failure.  */
+
+static void *
+xmalloc (size_t size)
+{
+  void *ptr;
+
+  ptr = malloc (size);
+
+  if (!ptr)
+    croak ("malloc");
+
+  return ptr;
+}
+
+/* Recursively build a struct directory_tree structure for each
+   subdirectory or file in DIR, in preparation for writing it out to
+   disk.  PARENT should be the directory tree associated with the
+   parent directory, or else PARENT->offset must be initialized to
+   5.  */
+
+static void
+main_1 (DIR *dir, struct directory_tree *parent)
+{
+  struct dirent *dirent;
+  int dir_fd, fd;
+  struct stat statb;
+  struct directory_tree *this, **last;
+  size_t length;
+  DIR *otherdir;
+
+  dir_fd = dirfd (dir);
+  last = &parent->children;
+
+  while ((dirent = readdir (dir)))
+    {
+      /* Determine what kind of file DIRENT is.  */
+
+      if (fstatat (dir_fd, dirent->d_name, &statb,
+                  AT_SYMLINK_NOFOLLOW) == -1)
+       croak ("fstatat");
+
+      /* Ignore . and ...  */
+
+      if (!strcmp (dirent->d_name, ".")
+         || !strcmp (dirent->d_name, ".."))
+       continue;
+
+      length = strlen (dirent->d_name);
+
+      if (statb.st_mode & S_IFDIR)
+       {
+         /* This is a directory.  Write its name followed by a
+            trailing slash, then a NULL byte, and the offset to the
+            next sibling.  */
+         this = xmalloc (sizeof *this);
+         this->children = NULL;
+         this->next = NULL;
+         *last = this;
+         last = &this->next;
+         this->name = xmalloc (length + 2);
+         strcpy (this->name, dirent->d_name);
+
+         /* Now record the offset to the end of this directory.  This
+            is length + 1, for the file name, and 5 more bytes for
+            the trailing NULL and long.  */
+         this->offset = parent->offset + length + 6;
+
+         /* Terminate that with a slash and trailing NULL byte.  */
+         this->name[length] = '/';
+         this->name[length + 1] = '\0';
+
+         /* Open and build that directory recursively.  */
+
+         fd = openat (dir_fd, dirent->d_name, O_DIRECTORY,
+                      O_RDONLY);
+         if (fd < 0)
+           croak ("openat");
+         otherdir = fdopendir (fd);
+         if (!otherdir)
+           croak ("fdopendir");
+
+         main_1 (otherdir, this);
+
+         /* Close this directory.  */
+         closedir (otherdir);
+
+         /* Finally, set parent->offset to this->offset as well.  */
+         parent->offset = this->offset;
+       }
+      else if (statb.st_mode & S_IFREG)
+       {
+         /* This is a regular file.  */
+         this = xmalloc (sizeof *this);
+         this->children = NULL;
+         this->next = NULL;
+         *last = this;
+         last = &this->next;
+         this->name = xmalloc (length + 1);
+         strcpy (this->name, dirent->d_name);
+
+         /* This is one byte shorter because there is no trailing
+            slash.  */
+         this->offset = parent->offset + length + 5;
+         parent->offset = this->offset;
+       }
+    }
+}
+
+/* Write the struct directory_tree TREE and all of is children to the
+   file descriptor FD.  OFFSET is the offset of TREE and may be
+   modified; it is only used for checking purposes.  */
+
+static void
+main_2 (int fd, struct directory_tree *tree, size_t *offset)
+{
+  ssize_t size;
+  struct directory_tree *child;
+  unsigned int output;
+
+  /* Write tree->name with the trailing NULL byte.  */
+  size = strlen (tree->name) + 1;
+  if (write (fd, tree->name, size) < size)
+    croak ("write");
+
+  /* Write the offset.  */
+#ifdef WORDS_BIGENDIAN
+  output = bswap_32 (tree->offset);
+#else
+  output = tree->offset;
+#endif
+  if (write (fd, &output, 4) < 1)
+    croak ("write");
+  size += 4;
+
+  /* Now update offset.  */
+  *offset += size;
+
+  /* Write out each child.  */
+  for (child = tree->children; child; child = child->next)
+    main_2 (fd, child, offset);
+
+  /* Verify the offset is correct.  */
+  if (tree->offset != *offset)
+    {
+      fprintf (stderr,
+              "asset-directory-tool: invalid offset: expected %tu, "
+              "got %tu.\n"
+              "Please report this bug to bug-gnu-emacs@gnu.org, along\n"
+              "with an archive containing the contents of the java/inst"
+              "all_temp directory.\n",
+              tree->offset, *offset);
+      abort ();
+    }
+}
+
+int
+main (int argc, char **argv)
+{
+  int fd;
+  DIR *indir;
+  struct directory_tree tree;
+  size_t offset;
+
+  if (argc != 3)
+    {
+      fprintf (stderr, "usage: %s directory output-file\n",
+              argv[0]);
+      return EXIT_FAILURE;
+    }
+
+  fd = open (argv[2], O_CREAT | O_TRUNC | O_RDWR,
+            S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+
+  if (fd < 0)
+    {
+      perror ("open");
+      return EXIT_FAILURE;
+    }
+
+  indir = opendir (argv[1]);
+
+  if (!indir)
+    {
+      perror ("opendir");
+      return EXIT_FAILURE;
+    }
+
+  /* Write the first 5 byte header to FD.  */
+
+  if (write (fd, "EMACS", 5) < 5)
+    {
+      perror ("write");
+      return EXIT_FAILURE;
+    }
+
+  /* Now iterate through children of INDIR, building the directory
+     tree.  */
+  tree.offset = 5;
+  tree.children = NULL;
+
+  main_1 (indir, &tree);
+  closedir (indir);
+
+  /* Finally, write the directory tree to the output file.  */
+  offset = 5;
+  for (; tree.children; tree.children = tree.children->next)
+    main_2 (fd, tree.children, &offset);
+
+  return 0;
+}
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index 698bf9b50ae..a72fced1bf2 100644
--- a/lib-src/emacsclient.c
+++ b/lib-src/emacsclient.c
@@ -626,6 +626,8 @@ decode_options (int argc, char **argv)
       alt_display = "w32";
 #elif defined (HAVE_HAIKU)
       alt_display = "be";
+#elif defined (HAVE_ANDROID)
+      alt_display = "android";
 #endif
 
 #ifdef HAVE_PGTK
diff --git a/lib/Makefile.in b/lib/Makefile.in
index 71199c32277..6752f68c50e 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -20,6 +20,15 @@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
+# This is not empty if this is a Makefile that will be copied to
+# cross/lib.
+XCONFIGURE = @XCONFIGURE@
+
+# This is required to make sure symbol visibility is correct and
+# functions like readlinkat do not end up replacing their OS
+# counterparts.
+ANDROID_BUILD_CFLAGS = @ANDROID_BUILD_CFLAGS@
+
 # Variables substituted by 'configure', and not autogenerated in gnulib.mk,
 # or needed before gnulib.mk is included.
 abs_top_srcdir = @abs_top_srcdir@
@@ -33,11 +42,11 @@ all:
 
 HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@
 
-ALL_CFLAGS= \
+ALL_CFLAGS = \
   $(C_SWITCH_SYSTEM) $(C_SWITCH_MACHINE) $(DEPFLAGS) \
   $(GNULIB_WARN_CFLAGS) $(WERROR_CFLAGS) $(PROFILING_CFLAGS) $(CFLAGS) \
-  -I. -I../src -I$(srcdir) -I$(srcdir)/../src \
-  $(if $(patsubst e-%,,$(notdir $<)),,-Demacs)
+  -I. -I../src -I$(srcdir) -I$(top_srcdir)/src \
+  $(if $(patsubst e-%,,$(notdir $<)),,-Demacs) $(ANDROID_BUILD_CFLAGS)
 
 ifeq ($(HAVE_NATIVE_COMP),yes)
 ALL_CFLAGS += -DGL_COMPILE_CRYPTO_STREAM
@@ -52,6 +61,12 @@ ifneq ($(SYSTEM_TYPE),windows-nt)
   libgnu_a_SOURCES += openat-die.c save-cwd.c
 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)/cross -I.
+endif
+
 DEPDIR = deps
 ifeq ($(AUTO_DEPEND),yes)
   DEPFLAGS = -MMD -MF $(DEPDIR)/$*.d -MP
@@ -60,11 +75,14 @@ else
   DEPFLAGS =
 endif
 
+# This piece of code interferes with cross compilation
+ifeq ($(XCONFIGURE),)
 .PRECIOUS: ../config.status Makefile
 ../config.status: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4
        $(MAKE) -C .. $(notdir $@)
 Makefile: ../config.status $(srcdir)/Makefile.in
        $(MAKE) -C .. lib/$@
+endif
 
 # Object modules that need not be built for Emacs.
 # Emacs does not need e-regex.o (it has its own regex-emacs.c),
@@ -111,10 +129,10 @@ clean:
 mostlyclean: clean
        rm -f $(filter-out %-t,$(MOSTLYCLEANFILES))
 distclean bootstrap-clean: mostlyclean
-       rm -f Makefile
+       rm -f Makefile Makefile.android
        rm -fr $(DEPDIR)
 maintainer-clean: distclean
-       rm -f TAGS gnulib.mk
+       rm -f TAGS gnulib.mk gnulib.mk.android
        -rmdir malloc sys 2>/dev/null || true
 
 .PHONY: mostlyclean clean distclean bootstrap-clean maintainer-clean
diff --git a/lib/getdelim.c b/lib/getdelim.c
new file mode 100644
index 00000000000..e414df648f6
--- /dev/null
+++ b/lib/getdelim.c
@@ -0,0 +1,143 @@
+/* getdelim.c --- Implementation of replacement getdelim function.
+   Copyright (C) 1994, 1996-1998, 2001, 2003, 2005-2023 Free Software
+   Foundation, Inc.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   This file 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Ported from glibc by Simon Josefsson. */
+
+/* Don't use __attribute__ __nonnull__ in this compilation unit.  Otherwise gcc
+   optimizes away the lineptr == NULL || n == NULL || fp == NULL tests below.  
*/
+#define _GL_ARG_NONNULL(params)
+
+#include <config.h>
+
+#include <stdio.h>
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#if USE_UNLOCKED_IO
+# include "unlocked-io.h"
+# define getc_maybe_unlocked(fp)        getc(fp)
+#elif !HAVE_FLOCKFILE || !HAVE_FUNLOCKFILE || !HAVE_DECL_GETC_UNLOCKED
+# undef flockfile
+# undef funlockfile
+# define flockfile(x) ((void) 0)
+# define funlockfile(x) ((void) 0)
+# define getc_maybe_unlocked(fp)        getc(fp)
+#else
+# define getc_maybe_unlocked(fp)        getc_unlocked(fp)
+#endif
+
+static void
+alloc_failed (void)
+{
+#if defined _WIN32 && ! defined __CYGWIN__
+  /* Avoid errno problem without using the realloc module; see:
+     https://lists.gnu.org/r/bug-gnulib/2016-08/msg00025.html  */
+  errno = ENOMEM;
+#endif
+}
+
+/* Read up to (and including) a DELIMITER from FP into *LINEPTR (and
+   NUL-terminate it).  *LINEPTR is a pointer returned from malloc (or
+   NULL), pointing to *N characters of space.  It is realloc'ed as
+   necessary.  Returns the number of characters read (not including
+   the null terminator), or -1 on error or EOF.  */
+
+ssize_t
+getdelim (char **lineptr, size_t *n, int delimiter, FILE *fp)
+{
+  ssize_t result;
+  size_t cur_len = 0;
+
+  if (lineptr == NULL || n == NULL || fp == NULL)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  flockfile (fp);
+
+  if (*lineptr == NULL || *n == 0)
+    {
+      char *new_lineptr;
+      *n = 120;
+      new_lineptr = (char *) realloc (*lineptr, *n);
+      if (new_lineptr == NULL)
+        {
+          alloc_failed ();
+          result = -1;
+          goto unlock_return;
+        }
+      *lineptr = new_lineptr;
+    }
+
+  for (;;)
+    {
+      int i;
+
+      i = getc_maybe_unlocked (fp);
+      if (i == EOF)
+        {
+          result = -1;
+          break;
+        }
+
+      /* Make enough space for len+1 (for final NUL) bytes.  */
+      if (cur_len + 1 >= *n)
+        {
+          size_t needed_max =
+            SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX;
+          size_t needed = 2 * *n + 1;   /* Be generous. */
+          char *new_lineptr;
+
+          if (needed_max < needed)
+            needed = needed_max;
+          if (cur_len + 1 >= needed)
+            {
+              result = -1;
+              errno = EOVERFLOW;
+              goto unlock_return;
+            }
+
+          new_lineptr = (char *) realloc (*lineptr, needed);
+          if (new_lineptr == NULL)
+            {
+              alloc_failed ();
+              result = -1;
+              goto unlock_return;
+            }
+
+          *lineptr = new_lineptr;
+          *n = needed;
+        }
+
+      (*lineptr)[cur_len] = i;
+      cur_len++;
+
+      if (i == delimiter)
+        break;
+    }
+  (*lineptr)[cur_len] = '\0';
+  result = cur_len ? cur_len : result;
+
+ unlock_return:
+  funlockfile (fp); /* doesn't set errno */
+
+  return result;
+}
diff --git a/lib/getline.c b/lib/getline.c
new file mode 100644
index 00000000000..85f16ab8bac
--- /dev/null
+++ b/lib/getline.c
@@ -0,0 +1,27 @@
+/* getline.c --- Implementation of replacement getline function.
+   Copyright (C) 2005-2007, 2009-2023 Free Software Foundation, Inc.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   This file 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Simon Josefsson. */
+
+#include <config.h>
+
+#include <stdio.h>
+
+ssize_t
+getline (char **lineptr, size_t *n, FILE *stream)
+{
+  return getdelim (lineptr, n, '\n', stream);
+}
diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in
index 3ef36bf8af6..1bfa1daa0b3 100644
--- a/lib/gnulib.mk.in
+++ b/lib/gnulib.mk.in
@@ -110,6 +110,7 @@
 #  fsusage \
 #  fsync \
 #  futimens \
+#  getline \
 #  getloadavg \
 #  getopt-gnu \
 #  getrandom \
@@ -151,6 +152,7 @@
 #  stddef \
 #  stdio \
 #  stpcpy \
+#  stpncpy \
 #  strnlen \
 #  strtoimax \
 #  symlink \
@@ -174,11 +176,28 @@
 
 MOSTLYCLEANFILES += core *.stackdump
 # Start of GNU Make output.
+AAPT = @AAPT@
 ALLOCA = @ALLOCA@
 ALLOCA_H = @ALLOCA_H@
 ALSA_CFLAGS = @ALSA_CFLAGS@
 ALSA_LIBS = @ALSA_LIBS@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+ANDROID = @ANDROID@
+ANDROID_ABI = @ANDROID_ABI@
+ANDROID_BUILD_CFLAGS = @ANDROID_BUILD_CFLAGS@
+ANDROID_CC = @ANDROID_CC@
+ANDROID_CFLAGS = @ANDROID_CFLAGS@
+ANDROID_DEBUGGABLE = @ANDROID_DEBUGGABLE@
+ANDROID_JAR = @ANDROID_JAR@
+ANDROID_LDFLAGS = @ANDROID_LDFLAGS@
+ANDROID_LIBS = @ANDROID_LIBS@
+ANDROID_MIN_SDK = @ANDROID_MIN_SDK@
+ANDROID_OBJ = @ANDROID_OBJ@
+ANDROID_SDK_18_OR_EARLIER = @ANDROID_SDK_18_OR_EARLIER@
+ANDROID_SDK_8_OR_EARLIER = @ANDROID_SDK_8_OR_EARLIER@
+ANDROID_SHARED_USER_ID = @ANDROID_SHARED_USER_ID@
+ANDROID_SHARED_USER_NAME = @ANDROID_SHARED_USER_NAME@
+APKSIGNER = @APKSIGNER@
 APPLE_UNIVERSAL_BUILD = @APPLE_UNIVERSAL_BUILD@
 AR = @AR@
 ARFLAGS = @ARFLAGS@
@@ -218,6 +237,7 @@ CYGWIN_OBJ = @CYGWIN_OBJ@
 C_SWITCH_MACHINE = @C_SWITCH_MACHINE@
 C_SWITCH_SYSTEM = @C_SWITCH_SYSTEM@
 C_SWITCH_X_SITE = @C_SWITCH_X_SITE@
+D8 = @D8@
 DBUS_CFLAGS = @DBUS_CFLAGS@
 DBUS_LIBS = @DBUS_LIBS@
 DBUS_OBJ = @DBUS_OBJ@
@@ -262,6 +282,7 @@ GETOPT_H = @GETOPT_H@
 GETRANDOM_LIB = @GETRANDOM_LIB@
 GFILENOTIFY_CFLAGS = @GFILENOTIFY_CFLAGS@
 GFILENOTIFY_LIBS = @GFILENOTIFY_LIBS@
+GIF_CFLAGS = @GIF_CFLAGS@
 GL_CFLAG_ALLOW_WARNINGS = @GL_CFLAG_ALLOW_WARNINGS@
 GL_CFLAG_GNULIB_WARNINGS = @GL_CFLAG_GNULIB_WARNINGS@
 GL_COND_LIBTOOL_CONDITION = @GL_COND_LIBTOOL_CONDITION@
@@ -281,8 +302,10 @@ GL_COND_OBJ_FSTATAT_CONDITION = 
@GL_COND_OBJ_FSTATAT_CONDITION@
 GL_COND_OBJ_FSUSAGE_CONDITION = @GL_COND_OBJ_FSUSAGE_CONDITION@
 GL_COND_OBJ_FSYNC_CONDITION = @GL_COND_OBJ_FSYNC_CONDITION@
 GL_COND_OBJ_FUTIMENS_CONDITION = @GL_COND_OBJ_FUTIMENS_CONDITION@
+GL_COND_OBJ_GETDELIM_CONDITION = @GL_COND_OBJ_GETDELIM_CONDITION@
 GL_COND_OBJ_GETDTABLESIZE_CONDITION = @GL_COND_OBJ_GETDTABLESIZE_CONDITION@
 GL_COND_OBJ_GETGROUPS_CONDITION = @GL_COND_OBJ_GETGROUPS_CONDITION@
+GL_COND_OBJ_GETLINE_CONDITION = @GL_COND_OBJ_GETLINE_CONDITION@
 GL_COND_OBJ_GETLOADAVG_CONDITION = @GL_COND_OBJ_GETLOADAVG_CONDITION@
 GL_COND_OBJ_GETOPT_CONDITION = @GL_COND_OBJ_GETOPT_CONDITION@
 GL_COND_OBJ_GETRANDOM_CONDITION = @GL_COND_OBJ_GETRANDOM_CONDITION@
@@ -308,6 +331,7 @@ GL_COND_OBJ_SIGDESCR_NP_CONDITION = 
@GL_COND_OBJ_SIGDESCR_NP_CONDITION@
 GL_COND_OBJ_STDIO_READ_CONDITION = @GL_COND_OBJ_STDIO_READ_CONDITION@
 GL_COND_OBJ_STDIO_WRITE_CONDITION = @GL_COND_OBJ_STDIO_WRITE_CONDITION@
 GL_COND_OBJ_STPCPY_CONDITION = @GL_COND_OBJ_STPCPY_CONDITION@
+GL_COND_OBJ_STPNCPY_CONDITION = @GL_COND_OBJ_STPNCPY_CONDITION@
 GL_COND_OBJ_STRNLEN_CONDITION = @GL_COND_OBJ_STRNLEN_CONDITION@
 GL_COND_OBJ_STRTOIMAX_CONDITION = @GL_COND_OBJ_STRTOIMAX_CONDITION@
 GL_COND_OBJ_STRTOLL_CONDITION = @GL_COND_OBJ_STRTOLL_CONDITION@
@@ -892,6 +916,9 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@
 INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INT32_MAX_LT_INTMAX_MAX = @INT32_MAX_LT_INTMAX_MAX@
 INT64_MAX_EQ_LONG_MAX = @INT64_MAX_EQ_LONG_MAX@
+JARSIGNER = @JARSIGNER@
+JAVAC = @JAVAC@
+JPEG_CFLAGS = @JPEG_CFLAGS@
 JSON_CFLAGS = @JSON_CFLAGS@
 JSON_LIBS = @JSON_LIBS@
 JSON_OBJ = @JSON_OBJ@
@@ -910,6 +937,7 @@ LIBGCCJIT_CFLAGS = @LIBGCCJIT_CFLAGS@
 LIBGCCJIT_LIBS = @LIBGCCJIT_LIBS@
 LIBGIF = @LIBGIF@
 LIBGMP = @LIBGMP@
+LIBGMP_CFLAGS = @LIBGMP_CFLAGS@
 LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@
 LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@
 LIBGNU_LIBDEPS = @LIBGNU_LIBDEPS@
@@ -927,6 +955,7 @@ LIBRESOLV = @LIBRESOLV@
 LIBS = @LIBS@
 LIBSECCOMP_CFLAGS = @LIBSECCOMP_CFLAGS@
 LIBSECCOMP_LIBS = @LIBSECCOMP_LIBS@
+LIBSELINUX_CFLAGS = @LIBSELINUX_CFLAGS@
 LIBSELINUX_LIBS = @LIBSELINUX_LIBS@
 LIBSOUND = @LIBSOUND@
 LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
@@ -974,6 +1003,18 @@ MODULES_SECONDARY_SUFFIX = @MODULES_SECONDARY_SUFFIX@
 MODULES_SUFFIX = @MODULES_SUFFIX@
 NANOSLEEP_LIB = @NANOSLEEP_LIB@
 NATIVE_COMPILATION_AOT = @NATIVE_COMPILATION_AOT@
+NDK_BUILD_ABI = @NDK_BUILD_ABI@
+NDK_BUILD_ANDROID_MK = @NDK_BUILD_ANDROID_MK@
+NDK_BUILD_ANY_CXX_MODULE = @NDK_BUILD_ANY_CXX_MODULE@
+NDK_BUILD_AR = @NDK_BUILD_AR@
+NDK_BUILD_ARCH = @NDK_BUILD_ARCH@
+NDK_BUILD_CC = @NDK_BUILD_CC@
+NDK_BUILD_CFLAGS = @NDK_BUILD_CFLAGS@
+NDK_BUILD_CXX = @NDK_BUILD_CXX@
+NDK_BUILD_CXX_SHARED = @NDK_BUILD_CXX_SHARED@
+NDK_BUILD_MODULES = @NDK_BUILD_MODULES@
+NDK_BUILD_NASM = @NDK_BUILD_NASM@
+NDK_BUILD_SDK = @NDK_BUILD_SDK@
 NEXT_ASSERT_H = @NEXT_ASSERT_H@
 NEXT_AS_FIRST_DIRECTIVE_ASSERT_H = @NEXT_AS_FIRST_DIRECTIVE_ASSERT_H@
 NEXT_AS_FIRST_DIRECTIVE_DIRENT_H = @NEXT_AS_FIRST_DIRECTIVE_DIRENT_H@
@@ -1242,6 +1283,7 @@ REPLACE_WRITE = @REPLACE_WRITE@
 REPLACE__EXIT = @REPLACE__EXIT@
 RSVG_CFLAGS = @RSVG_CFLAGS@
 RSVG_LIBS = @RSVG_LIBS@
+SDK_BUILD_TOOLS = @SDK_BUILD_TOOLS@
 SEPCHAR = @SEPCHAR@
 SETFATTR = @SETFATTR@
 SETTINGS_CFLAGS = @SETTINGS_CFLAGS@
@@ -1251,6 +1293,7 @@ SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@
 SIZEOF_LONG = @SIZEOF_LONG@
 SIZE_T_SUFFIX = @SIZE_T_SUFFIX@
 SMALL_JA_DIC = @SMALL_JA_DIC@
+SQLITE3_CFLAGS = @SQLITE3_CFLAGS@
 SQLITE3_LIBS = @SQLITE3_LIBS@
 STDCKDINT_H = @STDCKDINT_H@
 STDDEF_H = @STDDEF_H@
@@ -1259,6 +1302,7 @@ SUBDIR_MAKEFILES_IN = @SUBDIR_MAKEFILES_IN@
 SYSTEM_TYPE = @SYSTEM_TYPE@
 SYS_TIME_H_DEFINES_STRUCT_TIMESPEC = @SYS_TIME_H_DEFINES_STRUCT_TIMESPEC@
 TERMCAP_OBJ = @TERMCAP_OBJ@
+TIFF_CFLAGS = @TIFF_CFLAGS@
 TIMER_TIME_LIB = @TIMER_TIME_LIB@
 TIME_H_DEFINES_STRUCT_TIMESPEC = @TIME_H_DEFINES_STRUCT_TIMESPEC@
 TIME_H_DEFINES_TIME_UTC = @TIME_H_DEFINES_TIME_UTC@
@@ -1280,6 +1324,7 @@ W32_LIBS = @W32_LIBS@
 W32_OBJ = @W32_OBJ@
 W32_RES_LINK = @W32_RES_LINK@
 WARN_CFLAGS = @WARN_CFLAGS@
+WARN_JAVAFLAGS = @WARN_JAVAFLAGS@
 WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@
 WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
 WEBKIT_LIBS = @WEBKIT_LIBS@
@@ -1298,6 +1343,7 @@ XARGS_LIMIT = @XARGS_LIMIT@
 XCB_LIBS = @XCB_LIBS@
 XCOMPOSITE_CFLAGS = @XCOMPOSITE_CFLAGS@
 XCOMPOSITE_LIBS = @XCOMPOSITE_LIBS@
+XCONFIGURE = @XCONFIGURE@
 XCRUN = @XCRUN@
 XDBE_CFLAGS = @XDBE_CFLAGS@
 XDBE_LIBS = @XDBE_LIBS@
@@ -1322,6 +1368,7 @@ XSYNC_CFLAGS = @XSYNC_CFLAGS@
 XSYNC_LIBS = @XSYNC_LIBS@
 XWIDGETS_OBJ = @XWIDGETS_OBJ@
 X_TOOLKIT_TYPE = @X_TOOLKIT_TYPE@
+ZIPALIGN = @ZIPALIGN@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_OBJC = @ac_ct_OBJC@
@@ -1344,6 +1391,7 @@ datarootdir = @datarootdir@
 docdir = @docdir@
 dvidir = @dvidir@
 emacs_major_version = @emacs_major_version@
+emacs_use_mailutils = @emacs_use_mailutils@
 etcdir = @etcdir@
 etcdocdir = @etcdocdir@
 exec_prefix = @exec_prefix@
@@ -1367,6 +1415,7 @@ 
gl_GNULIB_ENABLED_e80bf6f757095d2e5fc94dafb8f8fc8b_CONDITION = @gl_GNULIB_ENABLE
 gl_GNULIB_ENABLED_ef455225c00f5049c808c2eda3e76866_CONDITION = 
@gl_GNULIB_ENABLED_ef455225c00f5049c808c2eda3e76866_CONDITION@
 gl_GNULIB_ENABLED_euidaccess_CONDITION = 
@gl_GNULIB_ENABLED_euidaccess_CONDITION@
 gl_GNULIB_ENABLED_fd38c7e463b54744b77b98aeafb4fa7c_CONDITION = 
@gl_GNULIB_ENABLED_fd38c7e463b54744b77b98aeafb4fa7c_CONDITION@
+gl_GNULIB_ENABLED_getdelim_CONDITION = @gl_GNULIB_ENABLED_getdelim_CONDITION@
 gl_GNULIB_ENABLED_getdtablesize_CONDITION = 
@gl_GNULIB_ENABLED_getdtablesize_CONDITION@
 gl_GNULIB_ENABLED_getgroups_CONDITION = @gl_GNULIB_ENABLED_getgroups_CONDITION@
 gl_GNULIB_ENABLED_lchmod_CONDITION = @gl_GNULIB_ENABLED_lchmod_CONDITION@
@@ -2130,6 +2179,18 @@ gl_V_at = $(AM_V_GEN)
 endif
 ## end   gnulib module gen-header
 
+## begin gnulib module getdelim
+ifeq (,$(OMIT_GNULIB_MODULE_getdelim))
+
+ifneq (,$(gl_GNULIB_ENABLED_getdelim_CONDITION))
+ifneq (,$(GL_COND_OBJ_GETDELIM_CONDITION))
+libgnu_a_SOURCES += getdelim.c
+endif
+
+endif
+endif
+## end   gnulib module getdelim
+
 ## begin gnulib module getdtablesize
 ifeq (,$(OMIT_GNULIB_MODULE_getdtablesize))
 
@@ -2154,6 +2215,16 @@ endif
 endif
 ## end   gnulib module getgroups
 
+## begin gnulib module getline
+ifeq (,$(OMIT_GNULIB_MODULE_getline))
+
+ifneq (,$(GL_COND_OBJ_GETLINE_CONDITION))
+libgnu_a_SOURCES += getline.c
+endif
+
+endif
+## end   gnulib module getline
+
 ## begin gnulib module getloadavg
 ifeq (,$(OMIT_GNULIB_MODULE_getloadavg))
 
@@ -3124,8 +3195,9 @@ stdio.h: stdio.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) $(ARG_NONNULL_H)
              -e 's/@''GNULIB_MDA_GETW''@/$(GL_GNULIB_MDA_GETW)/g' \
              -e 's/@''GNULIB_MDA_PUTW''@/$(GL_GNULIB_MDA_PUTW)/g' \
              -e 's/@''GNULIB_MDA_TEMPNAM''@/$(GL_GNULIB_MDA_TEMPNAM)/g' \
-             < $(srcdir)/stdio.in.h | \
-         sed -e 's|@''HAVE_DECL_FCLOSEALL''@|$(HAVE_DECL_FCLOSEALL)|g' \
+             < $(srcdir)/stdio.in.h > $@-t1
+       $(AM_V_at)sed \
+             -e 's|@''HAVE_DECL_FCLOSEALL''@|$(HAVE_DECL_FCLOSEALL)|g' \
              -e 's|@''HAVE_DECL_FPURGE''@|$(HAVE_DECL_FPURGE)|g' \
              -e 's|@''HAVE_DECL_FSEEKO''@|$(HAVE_DECL_FSEEKO)|g' \
              -e 's|@''HAVE_DECL_FTELLO''@|$(HAVE_DECL_FTELLO)|g' \
@@ -3144,6 +3216,8 @@ stdio.h: stdio.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) $(ARG_NONNULL_H)
              -e 's|@''HAVE_RENAMEAT''@|$(HAVE_RENAMEAT)|g' \
              -e 's|@''HAVE_VASPRINTF''@|$(HAVE_VASPRINTF)|g' \
              -e 's|@''HAVE_VDPRINTF''@|$(HAVE_VDPRINTF)|g' \
+             < $@-t1 > $@-t2
+       $(AM_V_at)sed \
              -e 's|@''REPLACE_DPRINTF''@|$(REPLACE_DPRINTF)|g' \
              -e 's|@''REPLACE_FCLOSE''@|$(REPLACE_FCLOSE)|g' \
              -e 's|@''REPLACE_FDOPEN''@|$(REPLACE_FDOPEN)|g' \
@@ -3181,9 +3255,10 @@ stdio.h: stdio.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) $(ARG_NONNULL_H)
              -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
              -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
              -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
-             > $@-t
-       $(AM_V_at)mv $@-t $@
-MOSTLYCLEANFILES += stdio.h stdio.h-t
+             < $@-t2 > $@-t3
+       $(AM_V_at)rm -f $@-t1 $@-t2
+       $(AM_V_at)mv $@-t3 $@
+MOSTLYCLEANFILES += stdio.h stdio.h-t1 stdio.h-t2 stdio.h-t3
 
 ifneq (,$(GL_COND_OBJ_STDIO_READ_CONDITION))
 libgnu_a_SOURCES += stdio-read.c
@@ -3262,8 +3337,9 @@ stdlib.h: stdlib.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) \
              -e 's/@''GNULIB_MDA_GCVT''@/$(GL_GNULIB_MDA_GCVT)/g' \
              -e 's/@''GNULIB_MDA_MKTEMP''@/$(GL_GNULIB_MDA_MKTEMP)/g' \
              -e 's/@''GNULIB_MDA_PUTENV''@/$(GL_GNULIB_MDA_PUTENV)/g' \
-             < $(srcdir)/stdlib.in.h | \
-         sed -e 's|@''HAVE__EXIT''@|$(HAVE__EXIT)|g' \
+             < $(srcdir)/stdlib.in.h > $@-t1
+       $(AM_V_at)sed \
+             -e 's|@''HAVE__EXIT''@|$(HAVE__EXIT)|g' \
              -e 's|@''HAVE_ALIGNED_ALLOC''@|$(HAVE_ALIGNED_ALLOC)|g' \
              -e 's|@''HAVE_ATOLL''@|$(HAVE_ATOLL)|g' \
              -e 
's|@''HAVE_CANONICALIZE_FILE_NAME''@|$(HAVE_CANONICALIZE_FILE_NAME)|g' \
@@ -3308,6 +3384,8 @@ stdlib.h: stdlib.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) \
              -e 's|@''HAVE_SYS_LOADAVG_H''@|$(HAVE_SYS_LOADAVG_H)|g' \
              -e 's|@''HAVE_UNLOCKPT''@|$(HAVE_UNLOCKPT)|g' \
              -e 's|@''HAVE_DECL_UNSETENV''@|$(HAVE_DECL_UNSETENV)|g' \
+             < $@-t1 > $@-t2
+       $(AM_V_at)sed \
              -e 's|@''REPLACE__EXIT''@|$(REPLACE__EXIT)|g' \
              -e 's|@''REPLACE_ALIGNED_ALLOC''@|$(REPLACE_ALIGNED_ALLOC)|g' \
              -e 
's|@''REPLACE_CALLOC_FOR_CALLOC_GNU''@|$(REPLACE_CALLOC_FOR_CALLOC_GNU)|g' \
@@ -3352,9 +3430,10 @@ stdlib.h: stdlib.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) \
              -e '/definition of _Noreturn/r $(_NORETURN_H)' \
              -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
              -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
-             > $@-t
-       $(AM_V_at)mv $@-t $@
-MOSTLYCLEANFILES += stdlib.h stdlib.h-t
+             < $@-t2 > $@-t3
+       $(AM_V_at)rm -f $@-t1 $@-t2
+       $(AM_V_at)mv $@-t3 $@
+MOSTLYCLEANFILES += stdlib.h stdlib.h-t1 stdlib.h-t2 stdlib.h-t3
 
 EXTRA_DIST += stdlib.in.h
 
@@ -3371,6 +3450,16 @@ endif
 endif
 ## end   gnulib module stpcpy
 
+## begin gnulib module stpncpy
+ifeq (,$(OMIT_GNULIB_MODULE_stpncpy))
+
+ifneq (,$(GL_COND_OBJ_STPNCPY_CONDITION))
+libgnu_a_SOURCES += stpncpy.c
+endif
+
+endif
+## end   gnulib module stpncpy
+
 ## begin gnulib module string
 ifeq (,$(OMIT_GNULIB_MODULE_string))
 
@@ -3430,8 +3519,9 @@ string.h: string.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) $(ARG_NONNULL_H
              -e 's/@''GNULIB_MDA_MEMCCPY''@/$(GL_GNULIB_MDA_MEMCCPY)/g' \
              -e 's/@''GNULIB_MDA_STRDUP''@/$(GL_GNULIB_MDA_STRDUP)/g' \
              -e 's/@''GNULIB_FREE_POSIX''@/$(GL_GNULIB_FREE_POSIX)/g' \
-             < $(srcdir)/string.in.h | \
-         sed -e 's|@''HAVE_EXPLICIT_BZERO''@|$(HAVE_EXPLICIT_BZERO)|g' \
+             < $(srcdir)/string.in.h > $@-t1
+       $(AM_V_at)sed \
+             -e 's|@''HAVE_EXPLICIT_BZERO''@|$(HAVE_EXPLICIT_BZERO)|g' \
              -e 's|@''HAVE_FFSL''@|$(HAVE_FFSL)|g' \
              -e 's|@''HAVE_FFSLL''@|$(HAVE_FFSLL)|g' \
              -e 's|@''HAVE_MBSLEN''@|$(HAVE_MBSLEN)|g' \
@@ -3479,9 +3569,10 @@ string.h: string.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) $(ARG_NONNULL_H
              -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
              -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
              -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
-             > $@-t
-       $(AM_V_at)mv $@-t $@
-MOSTLYCLEANFILES += string.h string.h-t
+             < $@-t1 > $@-t2
+       $(AM_V_at)rm -f $@-t1
+       $(AM_V_at)mv $@-t2 $@
+MOSTLYCLEANFILES += string.h string.h-t1 string.h-t2
 
 EXTRA_DIST += string.in.h
 
@@ -3918,6 +4009,8 @@ unistd.h: unistd.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) $(ARG_NONNULL_H
              -e 's/@''GNULIB_FDATASYNC''@/$(GL_GNULIB_FDATASYNC)/g' \
              -e 's/@''GNULIB_FSYNC''@/$(GL_GNULIB_FSYNC)/g' \
              -e 's/@''GNULIB_FTRUNCATE''@/$(GL_GNULIB_FTRUNCATE)/g' \
+             < $(srcdir)/unistd.in.h > $@-t1
+       $(AM_V_at)sed \
              -e 's/@''GNULIB_GETCWD''@/$(GL_GNULIB_GETCWD)/g' \
              -e 's/@''GNULIB_GETDOMAINNAME''@/$(GL_GNULIB_GETDOMAINNAME)/g' \
              -e 's/@''GNULIB_GETDTABLESIZE''@/$(GL_GNULIB_GETDTABLESIZE)/g' \
@@ -3979,8 +4072,9 @@ unistd.h: unistd.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) $(ARG_NONNULL_H
              -e 's/@''GNULIB_MDA_SWAB''@/$(GL_GNULIB_MDA_SWAB)/g' \
              -e 's/@''GNULIB_MDA_UNLINK''@/$(GL_GNULIB_MDA_UNLINK)/g' \
              -e 's/@''GNULIB_MDA_WRITE''@/$(GL_GNULIB_MDA_WRITE)/g' \
-             < $(srcdir)/unistd.in.h | \
-         sed -e 's|@''HAVE_CHOWN''@|$(HAVE_CHOWN)|g' \
+             < $@-t1 > $@-t2
+       $(AM_V_at)sed \
+             -e 's|@''HAVE_CHOWN''@|$(HAVE_CHOWN)|g' \
              -e 's|@''HAVE_COPY_FILE_RANGE''@|$(HAVE_COPY_FILE_RANGE)|g' \
              -e 's|@''HAVE_DUP3''@|$(HAVE_DUP3)|g' \
              -e 's|@''HAVE_EUIDACCESS''@|$(HAVE_EUIDACCESS)|g' \
@@ -4027,8 +4121,9 @@ unistd.h: unistd.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) $(ARG_NONNULL_H
              -e 's|@''HAVE_DECL_TTYNAME_R''@|$(HAVE_DECL_TTYNAME_R)|g' \
              -e 's|@''HAVE_OS_H''@|$(HAVE_OS_H)|g' \
              -e 's|@''HAVE_SYS_PARAM_H''@|$(HAVE_SYS_PARAM_H)|g' \
-         | \
-         sed -e 's|@''REPLACE_ACCESS''@|$(REPLACE_ACCESS)|g' \
+             < $@-t2 > $@-t3
+       $(AM_V_at)sed \
+             -e 's|@''REPLACE_ACCESS''@|$(REPLACE_ACCESS)|g' \
              -e 's|@''REPLACE_CHOWN''@|$(REPLACE_CHOWN)|g' \
              -e 's|@''REPLACE_CLOSE''@|$(REPLACE_CLOSE)|g' \
              -e 's|@''REPLACE_COPY_FILE_RANGE''@|$(REPLACE_COPY_FILE_RANGE)|g' 
\
@@ -4083,9 +4178,10 @@ unistd.h: unistd.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) $(ARG_NONNULL_H
              -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
              -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
              -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
-             > $@-t
-       $(AM_V_at)mv $@-t $@
-MOSTLYCLEANFILES += unistd.h unistd.h-t
+             < $@-t3 > $@-t4
+       $(AM_V_at)rm -f $@-t1 $@-t2 $@-t3
+       $(AM_V_at)mv $@-t4 $@
+MOSTLYCLEANFILES += unistd.h unistd.h-t1 unistd.h-t2 unistd.h-t3 unistd.h-t4
 
 EXTRA_DIST += unistd.in.h
 
diff --git a/lib/mini-gmp.c b/lib/mini-gmp.c
index ea037b801dc..69a72bfd460 100644
--- a/lib/mini-gmp.c
+++ b/lib/mini-gmp.c
@@ -172,12 +172,19 @@ see https://www.gnu.org/licenses/.  */
     }                                                                  \
   } while (0)
 
+/* If mp_limb_t is of size smaller than int, plain u*v implies
+   automatic promotion to *signed* int, and then multiply may overflow
+   and cause undefined behavior. Explicitly cast to unsigned int for
+   that case. */
+#define gmp_umullo_limb(u, v) \
+  ((sizeof(mp_limb_t) >= sizeof(int)) ? (u)*(v) : (unsigned int)(u) * (v))
+
 #define gmp_udiv_qrnnd_preinv(q, r, nh, nl, d, di)                     \
   do {                                                                 \
     mp_limb_t _qh, _ql, _r, _mask;                                     \
     gmp_umul_ppmm (_qh, _ql, (nh), (di));                              \
     gmp_add_ssaaaa (_qh, _ql, _qh, _ql, (nh) + 1, (nl));               \
-    _r = (nl) - _qh * (d);                                             \
+    _r = (nl) - gmp_umullo_limb (_qh, (d));                            \
     _mask = -(mp_limb_t) (_r > _ql); /* both > and >= are OK */                
\
     _qh += _mask;                                                      \
     _r += _mask & (d);                                                 \
@@ -198,7 +205,7 @@ see https://www.gnu.org/licenses/.  */
     gmp_add_ssaaaa ((q), _q0, (q), _q0, (n2), (n1));                   \
                                                                        \
     /* Compute the two most significant limbs of n - q'd */            \
-    (r1) = (n1) - (d1) * (q);                                          \
+    (r1) = (n1) - gmp_umullo_limb ((d1), (q));                         \
     gmp_sub_ddmmss ((r1), (r0), (r1), (n0), (d1), (d0));               \
     gmp_umul_ppmm (_t1, _t0, (d0), (q));                               \
     gmp_sub_ddmmss ((r1), (r0), (r1), (r0), _t1, _t0);                 \
diff --git a/lib/stdalign.in.h b/lib/stdalign.in.h
new file mode 100644
index 00000000000..b616c100fdc
--- /dev/null
+++ b/lib/stdalign.in.h
@@ -0,0 +1,49 @@
+/* A substitute for ISO C11 <stdalign.h>.
+
+   Copyright 2011-2023 Free Software Foundation, Inc.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   This file 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Paul Eggert and Bruno Haible.  */
+
+/* Define two obsolescent C11 macros, assuming alignas and alignof are
+   either keywords or alignasof-defined macros.  */
+
+#ifndef _@GUARD_PREFIX@_STDALIGN_H
+
+#if __GNUC__ >= 3
+@PRAGMA_SYSTEM_HEADER@
+#endif
+@PRAGMA_COLUMNS@
+
+/* We need to include the system's <stdalign.h> when it exists, because it 
might
+   define 'alignof' as a macro when it's not a keyword or compiler built-in.  
*/
+#if @HAVE_STDALIGN_H@
+/* The include_next requires a split double-inclusion guard.  */
+# @INCLUDE_NEXT@ @NEXT_STDALIGN_H@
+#endif
+
+#ifndef _@GUARD_PREFIX@_STDALIGN_H
+#define _@GUARD_PREFIX@_STDALIGN_H
+
+#if (defined alignas \
+     || (defined __STDC_VERSION__ && 202311 <= __STDC_VERSION__) \
+     || (defined __cplusplus && (201103 <= __cplusplus || defined _MSC_VER)))
+# define __alignas_is_defined 1
+#endif
+
+#define __alignof_is_defined 1
+
+#endif /* _@GUARD_PREFIX@_STDALIGN_H */
+#endif /* _@GUARD_PREFIX@_STDALIGN_H */
diff --git a/lib/stpncpy.c b/lib/stpncpy.c
new file mode 100644
index 00000000000..d1422a927df
--- /dev/null
+++ b/lib/stpncpy.c
@@ -0,0 +1,92 @@
+/* Copyright (C) 1993, 1995-1997, 2002-2003, 2005-2007, 2009-2023 Free Software
+ * Foundation, Inc.
+
+   NOTE: The canonical source of this file is maintained with the GNU C 
Library.
+   Bugs can be reported to bug-glibc@gnu.org.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   This file 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* This is almost copied from strncpy.c, written by Torbjorn Granlund.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <string.h>
+
+#ifndef weak_alias
+# define __stpncpy stpncpy
+#endif
+
+/* Copy no more than N bytes of SRC to DST, returning a pointer past the
+   last non-NUL byte written into DST.  */
+char *
+(__stpncpy) (char *dest, const char *src, size_t n)
+{
+  char c;
+  char *s = dest;
+
+  if (n >= 4)
+    {
+      size_t n4 = n >> 2;
+
+      for (;;)
+        {
+          c = *src++;
+          *dest++ = c;
+          if (c == '\0')
+            break;
+          c = *src++;
+          *dest++ = c;
+          if (c == '\0')
+            break;
+          c = *src++;
+          *dest++ = c;
+          if (c == '\0')
+            break;
+          c = *src++;
+          *dest++ = c;
+          if (c == '\0')
+            break;
+          if (--n4 == 0)
+            goto last_chars;
+        }
+      n -= dest - s;
+      goto zero_fill;
+    }
+
+ last_chars:
+  n &= 3;
+  if (n == 0)
+    return dest;
+
+  for (;;)
+    {
+      c = *src++;
+      --n;
+      *dest++ = c;
+      if (c == '\0')
+        break;
+      if (n == 0)
+        return dest;
+    }
+
+ zero_fill:
+  while (n-- > 0)
+    dest[n] = '\0';
+
+  return dest - 1;
+}
+#ifdef weak_alias
+weak_alias (__stpncpy, stpncpy)
+#endif
diff --git a/lisp/ChangeLog.16 b/lisp/ChangeLog.16
index 6dda3703e6d..f7dcda87466 100644
--- a/lisp/ChangeLog.16
+++ b/lisp/ChangeLog.16
@@ -10665,8 +10665,7 @@
        * textmodes/rst.el: Add comments.
        (rst-transition, rst-adornment): New faces.
        (rst-adornment-faces-alist): Make default safe to reevaluate.
-       Fixes
-       
http://sourceforge.net/tracker/?func=detail&atid=422030&aid=3479603&group_id=38414.
+       Fixes https://sourceforge.net/p/docutils/bugs/180/.
        Improve customization tags.
        (rst-define-level-faces): Clarify meaning.
 
diff --git a/lisp/align.el b/lisp/align.el
index 79a75dcec79..bc8ccbd599c 100644
--- a/lisp/align.el
+++ b/lisp/align.el
@@ -164,12 +164,14 @@ values may cause unexpected behavior at times."
   :group 'align)
 
 (defcustom align-highlight-change-face 'highlight
-  "The face to highlight with if changes are necessary."
+  "The face to highlight with if changes are necessary.
+Used by the `align-highlight-rule' command."
   :type 'face
   :group 'align)
 
 (defcustom align-highlight-nochange-face 'secondary-selection
-  "The face to highlight with if no changes are necessary."
+  "The face to highlight with if no changes are necessary.
+Used by the `align-highlight-rule' command."
   :type 'face
   :group 'align)
 
@@ -209,20 +211,20 @@ If nil, then no messages will ever be printed to the 
minibuffer."
 
 (defcustom align-dq-string-modes
   (append align-lisp-modes align-c++-modes align-perl-modes
-          '(python-mode vhdl-mode))
+          '(python-base-mode vhdl-mode))
   "A list of modes where double quoted strings should be excluded."
   :type '(repeat symbol)
   :group 'align)
 
 (defcustom align-sq-string-modes
-  (append align-perl-modes '(python-mode))
+  (append align-perl-modes '(python-base-mode))
   "A list of modes where single quoted strings should be excluded."
   :type '(repeat symbol)
   :group 'align)
 
 (defcustom align-open-comment-modes
   (append align-lisp-modes align-c++-modes align-perl-modes
-          '(python-mode makefile-mode vhdl-mode))
+          '(python-base-mode makefile-mode vhdl-mode))
   "A list of modes with a single-line comment syntax.
 These are comments as in Lisp, which have a beginning, but end with
 the line (i.e., `comment-end' is an empty string)."
@@ -448,7 +450,7 @@ The possible settings for `align-region-separate' are:
      (regexp   . ,(concat "[^=!<> \t\n]\\(\\s-*\\)="
                          "\\(\\s-*\\)\\([^>= \t\n]\\|$\\)"))
      (group    . (1 2))
-     (modes    . '(python-mode))
+     (modes    . '(python-base-mode))
      (tab-stop . nil))
 
     (make-assignment
@@ -476,7 +478,7 @@ The possible settings for `align-region-separate' are:
     (basic-comma-delimiter
      (regexp   . ",\\(\\s-*\\)[^# \t\n]")
      (repeat   . t)
-     (modes    . (append align-perl-modes '(python-mode)))
+     (modes    . (append align-perl-modes '(python-base-mode)))
      (run-if   . ,(lambda () current-prefix-arg)))
 
     (c++-comment
@@ -506,7 +508,7 @@ The possible settings for `align-region-separate' are:
 
     (python-chain-logic
      (regexp   . "\\(\\s-*\\)\\(\\<and\\>\\|\\<or\\>\\)")
-     (modes    . '(python-mode))
+     (modes    . '(python-base-mode))
      (valid    . ,(lambda ()
                     (save-excursion
                       (goto-char (match-end 2))
@@ -523,7 +525,7 @@ The possible settings for `align-region-separate' are:
 
     (basic-line-continuation
      (regexp   . "\\(\\s-*\\)\\\\$")
-     (modes    . '(python-mode makefile-mode)))
+     (modes    . '(python-base-mode makefile-mode)))
 
     (tex-record-separator
      (regexp . ,(lambda (end reverse)
@@ -568,7 +570,14 @@ The possible settings for `align-region-separate' are:
     (css-declaration
      (regexp . "^\\s-*\\(?:\\w-?\\)+:\\(\\s-*\\).*;")
      (group . (1))
-     (modes . '(css-mode html-mode))))
+     (modes . '(css-base-mode html-mode)))
+
+    (toml-assignment
+     (regexp . ,(rx (group (zero-or-more (syntax whitespace)))
+                    "="
+                    (group (zero-or-more (syntax whitespace)))))
+     (group . (1 2))
+     (modes . '(conf-toml-mode toml-ts-mode))))
   "A list describing all of the available alignment rules.
 The format is:
 
diff --git a/lisp/battery.el b/lisp/battery.el
index 4306d5b2058..c55fcbbee8c 100644
--- a/lisp/battery.el
+++ b/lisp/battery.el
@@ -29,9 +29,11 @@
 ;; - The `/sys/class/power_supply/' files of Linux >= 2.6.39.
 ;; - The `/proc/acpi/' directory structure of Linux 2.4.20 and 2.6.
 ;; - The `/proc/apm' file format of Linux version 1.3.58 or newer.
+;; - The Haiku ACPI battery driver.
 ;; - BSD by using the `apm' program.
 ;; - Darwin (macOS) by using the `pmset' program.
 ;; - Windows via the GetSystemPowerStatus API call.
+;; - Android 5 or later via the BatteryManager APIs.
 
 ;;; Code:
 
@@ -106,6 +108,12 @@ Value does not include \".\" or \"..\"."
               (file-readable-p "/proc/")
               (file-readable-p "/proc/apm"))
          #'battery-linux-proc-apm)
+        ;; Now try the Android battery status function.
+        ;; Note that even though the Linux kernel APIs are sometimes
+        ;; available on Android, they are badly implemented by Android
+        ;; kernels, so avoid using those.
+        ((eq system-type 'android)
+         #'battery-android)
        ((and (eq system-type 'berkeley-unix)
              (file-executable-p "/usr/sbin/apm"))
         #'battery-bsd-apm)
@@ -1072,6 +1080,78 @@ The following %-sequences are provided:
          (cons ?t (or remaining-time "N/A")))))
 
 
+;;; `BatteryManager' interface for Android.
+
+(declare-function android-query-battery "androidfns.c")
+
+(defun battery-android ()
+  "Get battery status information using Android.
+
+The following %-sequences are provided:
+%c Current capacity (mAh)
+%r Current rate of charge or discharge (mA)
+%L AC line status (verbose).
+%B Battery status (verbose)
+%b Battery status, empty means high, `-' means low,
+  `+' means charging and `?' means unknown.
+%d Temperature (in degrees Celsius)
+%p Battery load percentage.
+%m Remaining time (to charge) in minutes.
+%h Remaining time (to charge) in hours.
+%t Remaining time (to charge) in the form `h:min'."
+  (when-let* ((status (android-query-battery)))
+    (let* ((percentage nil)
+           (capacity nil)
+           (sym-status nil)
+           (symbol nil)
+           (rate nil)
+           (remaining nil)
+           (hours nil)
+           (minutes nil))
+      ;; Figure out the percentage.
+      (setq percentage (number-to-string (car status)))
+      ;; Figure out the capacity
+      (setq capacity (number-to-string (/ (cadr status) 1000)))
+      ;; Figure out the battery status.
+      (let ((percentage (car status)))
+        (cl-ecase (nth 4 status)
+          (2 (setq sym-status "charging" symbol "+"))
+          (3 (setq sym-status "discharging"
+                   symbol (if (< percentage 15) "-" " ")))
+          (5 (setq sym-status "full" symbol " "))
+          (4 (setq sym-status "not charging"
+                   symbol (if (< percentage 15) "-" " ")))
+          (1 (setq sym-status "unknown" symbol "?"))))
+      ;; Figure out the rate of charge.
+      (setq rate (/ (nth 3 status) 1000))
+      ;; Figure out the remaining time.
+      (let* ((time (nth 5 status))
+             (mins (/ time (* 1000 60)))
+             (hours-left (/ mins 60))
+             (mins (mod mins 60)))
+        (unless (eq time -1)
+          (setq remaining (format "%d:%d" hours-left mins)
+                hours (number-to-string hours-left)
+                minutes (number-to-string mins))))
+      ;; Return results.
+      (list (cons ?c capacity)
+            (cons ?p percentage)
+            (cons ?r rate)
+            (cons ?B sym-status)
+            (cons ?b symbol)
+            (cons ?m (or minutes "N/A"))
+            (cons ?h (or hours "N/A"))
+            (cons ?t (or remaining "N/A"))
+            (cons ?L (cl-case (nth 6 status)
+                       (0 "off-line")
+                       (1 "on-line")
+                       (2 "on-line (dock)")
+                       (3 "on-line (USB)")
+                       (4 "on-line (wireless)")
+                       (t "unknown")))
+            (cons ?t (/ (or (nth 7 status) 0) 10.0))))))
+
+
 ;;; Private functions.
 
 (defun battery-format (format alist)
diff --git a/lisp/bindings.el b/lisp/bindings.el
index 22f05939235..68aa0a78099 100644
--- a/lisp/bindings.el
+++ b/lisp/bindings.el
@@ -449,7 +449,7 @@ Keymap to display on minor modes.")
 
 (defvar mode-line-modes
   (let ((recursive-edit-help-echo
-         "Recursive edit, type M-C-c to get out"))
+         "Recursive edit, type C-M-c to get out"))
     (list (propertize "%[" 'help-echo recursive-edit-help-echo)
          "("
          `(:propertize ("" mode-name)
@@ -1269,6 +1269,10 @@ if `inhibit-field-text-motion' is non-nil."
 (define-key global-map [insertchar]    'overwrite-mode)
 (define-key global-map [C-insertchar]  'kill-ring-save)
 (define-key global-map [S-insertchar]  'yank)
+;; The next three keys are used on MS Windows and Android.
+(define-key global-map [copy]          'kill-ring-save)
+(define-key global-map [paste]         'yank)
+(define-key global-map [cut]           'kill-region)
 (define-key global-map [undo]          'undo)
 (define-key global-map [redo]          'repeat-complex-command)
 (define-key global-map [again]         'repeat-complex-command) ; Sun keyboard
@@ -1588,6 +1592,9 @@ if `inhibit-field-text-motion' is non-nil."
 (define-key special-event-map [sigusr1] 'ignore)
 (define-key special-event-map [sigusr2] 'ignore)
 
+;; Text conversion
+(define-key global-map [text-conversion] 'analyze-text-conversion)
+
 ;; Don't look for autoload cookies in this file.
 ;; Local Variables:
 ;; no-update-autoloads: t
diff --git a/lisp/button.el b/lisp/button.el
index 002064fbea0..b01595943fc 100644
--- a/lisp/button.el
+++ b/lisp/button.el
@@ -72,7 +72,12 @@ Mode-specific keymaps may want to use this as their parent 
keymap."
   ;; mode-line or header-line, the `mode-line' or `header-line' prefix
   ;; shouldn't be necessary!
   "<mode-line> <mouse-2>" #'push-button
-  "<header-line> <mouse-2>" #'push-button)
+  "<header-line> <mouse-2>" #'push-button
+  ;; `push-button' will automatically dispatch to
+  ;; `touch-screen-track-tap'.
+  "<mode-line> <touchscreen-down>" #'push-button
+  "<header-line> <touchscreen-down>" #'push-button
+  "<touchscreen-down>" #'push-button)
 
 (define-minor-mode button-mode
   "A minor mode for navigating to buttons with the TAB key."
@@ -454,18 +459,22 @@ instead of starting at the next button."
 
 (defun push-button (&optional pos use-mouse-action)
   "Perform the action specified by a button at location POS.
-POS may be either a buffer position or a mouse-event.  If
-USE-MOUSE-ACTION is non-nil, invoke the button's `mouse-action'
-property instead of its `action' property; if the button has no
-`mouse-action', the value of `action' is used instead.
+POS may be either a buffer position, a mouse-event, or a
+`touchscreen-down' event.  If USE-MOUSE-ACTION is non-nil, invoke
+the button's `mouse-action' property instead of its `action'
+property; if the button has no `mouse-action', the value of
+`action' is used instead.
+
+If POS is a `touchscreen-down' event, wait for the corresponding
+`touchscreen-up' event before calling `push-button'.
 
 The action in both cases may be either a function to call or a
 marker to display and is invoked using `button-activate' (which
 see).
 
 POS defaults to point, except when `push-button' is invoked
-interactively as the result of a mouse-event, in which case, the
-mouse event is used.
+interactively as the result of a mouse-event or touchscreen
+event, in which case, the position in the event event is used.
 
 If there's no button at POS, do nothing and return nil, otherwise
 return t.
@@ -483,7 +492,12 @@ pushing a button, use the `button-describe' command."
            (if str-button
                ;; mode-line, header-line, or display string event.
                (button-activate str t)
-             (push-button (posn-point posn) t)))))
+              (if (eq (car pos) 'touchscreen-down)
+                  ;; If touch-screen-track tap returns nil, then the
+                  ;; tap was cancelled.
+                  (when (touch-screen-track-tap pos)
+                    (push-button (posn-point posn) t))
+                (push-button (posn-point posn) t))))))
     ;; POS is just normal position
     (let ((button (button-at (or pos (point)))))
       (when button
diff --git a/lisp/calc/calc.el b/lisp/calc/calc.el
index a1545edba19..652cb8c1a88 100644
--- a/lisp/calc/calc.el
+++ b/lisp/calc/calc.el
@@ -1357,7 +1357,10 @@ Notations:  3.14e6     3.14 * 10^6
   (calc-set-mode-line)
   (calc-check-defines)
   (if calc-buffer-list (setq calc-stack (copy-sequence calc-stack)))
-  (add-to-list 'calc-buffer-list (current-buffer) t))
+  (add-to-list 'calc-buffer-list (current-buffer) t)
+  ;; While Calc buffers are read only, the on screen keyboard should
+  ;; be displayed in order to accept user input.
+  (setq-local touch-screen-display-keyboard t))
 
 (defvar calc-check-defines 'calc-check-defines)  ; Suitable for run-hooks.
 (defun calc-check-defines ()
@@ -1451,49 +1454,54 @@ See `window-dedicated-p' for what that means."
            (calc-grab-region (region-beginning) (region-end) nil)
          (when (= (prefix-numeric-value arg) -2)
            (calc-keypad))))
-    (when (get-buffer-window "*Calc Keypad*")
-      (calc-keypad)
-      (set-buffer (window-buffer)))
-    (if (derived-mode-p 'calc-mode)
-       (calc-quit)
-      (calc-create-buffer)
-      (setq calc-was-keypad-mode nil)
-      (if (or (eq full-display t)
-              (and (null full-display) calc-full-mode))
-          (switch-to-buffer (current-buffer) t)
-        (if (get-buffer-window (current-buffer))
-            (select-window (get-buffer-window (current-buffer)))
-          (if calc-window-hook
-              (run-hooks 'calc-window-hook)
-            (let ((w (get-largest-window)))
-              (if (and pop-up-windows
-                       (> (window-height w)
-                          (+ window-min-height calc-window-height 2)))
-                  (progn
-                    (setq w (split-window w
-                                          (- (window-height w)
-                                             calc-window-height 2)
-                                          nil))
-                    (set-window-buffer w (current-buffer))
-                    (select-window w))
-                (pop-to-buffer (current-buffer)))))))
-      (with-current-buffer (calc-trail-buffer)
-        (and calc-display-trail
-             (calc-trail-display 1 t)))
-      (message (substitute-command-keys
-                (concat "Welcome to the GNU Emacs Calculator!  
\\<calc-mode-map>"
-                        "Press \\[calc-help] or \\[calc-help-prefix] for help, 
\\[calc-quit] to quit")))
-      (run-hooks 'calc-start-hook)
-      (and (windowp full-display)
-           (window-point full-display)
-           (select-window full-display))
-      (and calc-make-windows-dedicated
-           (set-window-dedicated-p nil t))
-      (calc-check-defines)
-      (when (and calc-said-hello interactive)
-        (sit-for 2)
-        (message ""))
-      (setq calc-said-hello t))))
+    ;; If the selected window changes here, Emacs may think that the
+    ;; selected window is read only, and no on screen keyboard should
+    ;; be displayed.  Make sure that any active on screen keyboard is
+    ;; not hidden by accident.
+    (let ((touch-screen-display-keyboard t))
+      (when (get-buffer-window "*Calc Keypad*")
+        (calc-keypad)
+        (set-buffer (window-buffer)))
+      (if (derived-mode-p 'calc-mode)
+         (calc-quit)
+        (calc-create-buffer)
+        (setq calc-was-keypad-mode nil)
+        (if (or (eq full-display t)
+                (and (null full-display) calc-full-mode))
+            (switch-to-buffer (current-buffer) t)
+          (if (get-buffer-window (current-buffer))
+              (select-window (get-buffer-window (current-buffer)))
+            (if calc-window-hook
+                (run-hooks 'calc-window-hook)
+              (let ((w (get-largest-window)))
+                (if (and pop-up-windows
+                         (> (window-height w)
+                            (+ window-min-height calc-window-height 2)))
+                    (progn
+                      (setq w (split-window w
+                                            (- (window-height w)
+                                               calc-window-height 2)
+                                            nil))
+                      (set-window-buffer w (current-buffer))
+                      (select-window w))
+                  (pop-to-buffer (current-buffer)))))))
+        (with-current-buffer (calc-trail-buffer)
+          (and calc-display-trail
+               (calc-trail-display 1 t)))
+        (message (substitute-command-keys
+                  (concat "Welcome to the GNU Emacs Calculator!  
\\<calc-mode-map>"
+                          "Press \\[calc-help] or \\[calc-help-prefix] for 
help, \\[calc-quit] to quit")))
+        (run-hooks 'calc-start-hook)
+        (and (windowp full-display)
+             (window-point full-display)
+             (select-window full-display))
+        (and calc-make-windows-dedicated
+             (set-window-dedicated-p nil t))
+        (calc-check-defines)
+        (when (and calc-said-hello interactive)
+          (sit-for 2)
+          (message ""))
+        (setq calc-said-hello t)))))
 
 ;;;###autoload
 (defun full-calc (&optional interactive)
diff --git a/lisp/cedet/semantic/db-ebrowse.el 
b/lisp/cedet/semantic/db-ebrowse.el
index f8ea73cbdde..3e54b9e76cf 100644
--- a/lisp/cedet/semantic/db-ebrowse.el
+++ b/lisp/cedet/semantic/db-ebrowse.el
@@ -158,7 +158,8 @@ is specified by `semanticdb-default-save-directory'."
       ;; Call the EBROWSE command.
       (message "Creating ebrowse file: %s ..." savein)
       (call-process-region (point-min) (point-max)
-                          "ebrowse" nil "*EBROWSE OUTPUT*" nil
+                          ebrowse-program-name
+                           nil "*EBROWSE OUTPUT*" nil
                           (concat "--output-file=" savein)
                           "--very-verbose")
       )
diff --git a/lisp/comint.el b/lisp/comint.el
index 718a972a582..5161d01515c 100644
--- a/lisp/comint.el
+++ b/lisp/comint.el
@@ -723,6 +723,9 @@ Entry to this mode runs the hooks on `comint-mode-hook'."
   (setq-local comint-last-input-start (point-min-marker))
   (setq-local comint-last-input-end (point-min-marker))
   (setq-local comint-last-output-start (make-marker))
+  ;; It is ok to let the input method edit prompt text, but RET must
+  ;; be processed by Emacs.
+  (setq text-conversion-style 'action)
   (make-local-variable 'comint-last-prompt)
   (make-local-variable 'comint-prompt-regexp)        ; Don't set; default
   (make-local-variable 'comint-input-ring-size)      ; ...to global val.
diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el
index 0c62dd09744..f5143bdb53f 100644
--- a/lisp/cus-edit.el
+++ b/lisp/cus-edit.el
@@ -2209,7 +2209,7 @@ and `face'."
 ;;; The `custom' Widget.
 
 (defface custom-button
-  '((((type x w32 ns haiku pgtk) (class color))        ; Like default mode line
+  '((((type x w32 ns haiku pgtk android) (class color))        ; Like default 
mode line
      :box (:line-width 2 :style released-button)
      :background "lightgrey" :foreground "black"))
   "Face for custom buffer buttons if `custom-raised-buttons' is non-nil."
@@ -2217,7 +2217,7 @@ and `face'."
   :group 'custom-faces)
 
 (defface custom-button-mouse
-  '((((type x w32 ns haiku pgtk) (class color))
+  '((((type x w32 ns haiku pgtk android) (class color))
      :box (:line-width 2 :style released-button)
      :background "grey90" :foreground "black")
     (t
@@ -2242,7 +2242,7 @@ and `face'."
       (if custom-raised-buttons 'custom-button-mouse 'highlight))
 
 (defface custom-button-pressed
-  '((((type x w32 ns haiku pgtk) (class color))
+  '((((type x w32 ns haiku pgtk android) (class color))
      :box (:line-width 2 :style pressed-button)
      :background "lightgrey" :foreground "black")
     (t :inverse-video t))
@@ -3553,6 +3553,10 @@ Pure-GTK interface.")
                                           :sibling-args (:help-echo "\
 Haiku interface.")
                                           haiku)
+                                    (const :format "Android "
+                                          :sibling-args (:help-echo "\
+Android interface.")
+                                          android)
                                    (const :format "DOS "
                                           :sibling-args (:help-echo "\
 Plain MS-DOS.")
diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index a07406e4c0d..3e8b4c3c8fc 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -480,7 +480,8 @@ List has a form of (file-name full-file-name 
(attribute-list))."
     (if failures
        (dired-log-summary
         (format "%s: error" operation)
-        nil))))
+        nil)))
+  (dired-post-do-command))
 
 ;;;###autoload
 (defun dired-do-chmod (&optional arg)
@@ -531,7 +532,8 @@ has no effect on MS-Windows."
        (if num-modes num-modes
         (file-modes-symbolic-to-number modes (file-modes file 'nofollow)))
        'nofollow))
-    (dired-do-redisplay arg)))
+    (dired-do-redisplay arg))
+  (dired-post-do-command))
 
 ;;;###autoload
 (defun dired-do-chgrp (&optional arg)
@@ -634,7 +636,8 @@ Uses the shell command coming from variables `lpr-command' 
and
                                      lpr-switches))
                              " ")
                   'print arg file-list)))
-    (dired-run-shell-command (dired-shell-stuff-it command file-list nil))))
+    (dired-run-shell-command (dired-shell-stuff-it command file-list nil)))
+  (dired-post-do-command))
 
 (defun dired-mark-read-string (prompt initial op-symbol arg files
                                      &optional default-value collection)
@@ -918,7 +921,8 @@ Also see the `dired-confirm-shell-command' variable."
                                  nil file-list)
             ;; execute the shell command
             (dired-run-shell-command
-              (dired-shell-stuff-it command file-list nil arg)))))))
+              (dired-shell-stuff-it command file-list nil arg))))))
+  (dired-post-do-command))
 
 ;; Might use {,} for bash or csh:
 (defvar dired-mark-prefix ""
@@ -1547,7 +1551,8 @@ and `dired-compress-files-alist'."
                                "Compressed %d files to %s"
                                (length in-files))
                       (length in-files)
-                      (file-name-nondirectory out-file)))))))
+                      (file-name-nondirectory out-file))))))
+  (dired-post-do-command))
 
 ;;;###autoload
 (defun dired-compress-file (file)
@@ -2554,7 +2559,8 @@ Optional arg HOW-TO determines how to treat the target.
                   (and (functionp dired-do-revert-buffer)
                        (funcall dired-do-revert-buffer target)))
           (dired-fun-in-all-buffers (file-name-directory target) nil
-                                    #'revert-buffer))))))
+                                    #'revert-buffer)))))
+  (dired-post-do-command))
 
 ;; Read arguments for a marked-files command that wants a file name,
 ;; perhaps popping up the list of marked files.
@@ -2887,7 +2893,8 @@ Also see `dired-do-revert-buffer'."
                   (dired-get-marked-files nil arg))
     (user-error "Can't rename \".\" or \"..\" files"))
   (dired-do-create-files 'move #'dired-rename-file
-                        "Move" arg dired-keep-marker-rename "Rename"))
+                        "Move" arg dired-keep-marker-rename "Rename")
+  (dired-post-do-command))
 
 
 ;;; Operate on files matched by regexp
@@ -3579,14 +3586,18 @@ It's intended to override the default search function."
   "Search for a string through all marked files using Isearch."
   (interactive)
   (multi-isearch-files
-   (dired-get-marked-files nil nil #'dired-nondirectory-p nil t)))
+   (prog1 (dired-get-marked-files nil nil
+                                  #'dired-nondirectory-p nil t)
+     (dired-post-do-command))))
 
 ;;;###autoload
 (defun dired-do-isearch-regexp ()
   "Search for a regexp through all marked files using Isearch."
   (interactive)
-  (multi-isearch-files-regexp
-   (dired-get-marked-files nil nil 'dired-nondirectory-p nil t)))
+  (prog1 (multi-isearch-files-regexp
+          (dired-get-marked-files nil nil
+                                  'dired-nondirectory-p nil t))
+    (dired-post-do-command)))
 
 (declare-function fileloop-continue "fileloop" ())
 
@@ -3603,6 +3614,7 @@ To continue searching for next match, use command 
\\[fileloop-continue]."
    regexp
    (dired-get-marked-files nil nil #'dired-nondirectory-p)
    'default)
+  (dired-post-do-command)
   (fileloop-continue))
 
 ;;;###autoload
@@ -3626,6 +3638,7 @@ resume the query replace with the command 
\\[fileloop-continue]."
       (if (and buffer (with-current-buffer buffer
                        buffer-read-only))
          (error "File `%s' is visited read-only" file))))
+  (dired-post-do-command)
   (fileloop-initialize-replace
    from to (dired-get-marked-files nil nil #'dired-nondirectory-p)
    (if (equal from (downcase from)) nil 'default)
@@ -3675,6 +3688,7 @@ REGEXP should use constructs supported by your local 
`grep' command."
                 (user-error "No matches for: %s" regexp))
               (message "Searching...done")
               xrefs))))
+    (dired-post-do-command)
     (xref-show-xrefs fetcher nil)))
 
 ;;;###autoload
@@ -3767,6 +3781,7 @@ case, the VERBOSE argument is ignored."
                           (file-name-as-directory file)
                         file))
                     marked-files))))
+    (dired-post-do-command)
     (if mark-files
         (let ((transient-hook (make-symbol "vc-dir-mark-files")))
           (fset transient-hook
diff --git a/lisp/dired.el b/lisp/dired.el
index 90342069154..255d79fa42c 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -346,7 +346,7 @@ with the buffer narrowed to the listing."
   :type 'boolean)
 
 (defcustom dired-initial-position-hook nil
-  "This hook is used to position the point.
+  "Hook used to position point in a new Dired listing display.
 It is run by the function `dired-initial-position'."
   :group 'dired
   :type 'hook
@@ -1872,6 +1872,9 @@ other marked file as well.  Otherwise, unmark all files."
                                      keymap)
   "Keymap applied to file names when `dired-mouse-drag-files' is enabled.")
 
+(defvar dired-click-to-select-mode)
+(defvar dired-click-to-select-map)
+
 (defun dired-insert-set-properties (beg end)
   "Add various text properties to the lines in the region, from BEG to END."
   (save-excursion
@@ -1893,30 +1896,30 @@ other marked file as well.  Otherwise, unmark all 
files."
                 (when (member (cl-incf i) dired-hide-details-preserved-columns)
                   (put-text-property opoint (point) 'invisible nil))
                 (setq opoint (point)))))
-          (when (and dired-mouse-drag-files (fboundp 'x-begin-drag))
-            (put-text-property (point)
-                              (save-excursion
-                                (dired-move-to-end-of-filename)
-                                 (backward-char)
-                                (point))
-                               'keymap
-                               dired-mouse-drag-files-map))
-         (add-text-properties
-          (point)
-          (progn
-            (dired-move-to-end-of-filename)
-            (point))
-          `(mouse-face
-            highlight
-            dired-filename t
-            help-echo ,(if (and dired-mouse-drag-files
-                                 (fboundp 'x-begin-drag))
-                            "down-mouse-1: drag this file to another program
+          (let ((beg (point)) (end (save-excursion
+                                    (dired-move-to-end-of-filename)
+                                    (1- (point)))))
+            (if dired-click-to-select-mode
+                (put-text-property beg end 'keymap
+                                   dired-click-to-select-map)
+              (when (and dired-mouse-drag-files (fboundp 'x-begin-drag))
+                (put-text-property beg end 'keymap
+                                   dired-mouse-drag-files-map)))
+           (add-text-properties
+            beg (1+ end)
+            `(mouse-face
+              highlight
+              dired-filename t
+              help-echo ,(if dired-click-to-select-mode
+                              "mouse-2: mark or unmark this file"
+                            (if (and dired-mouse-drag-files
+                                     (fboundp 'x-begin-drag))
+                                "down-mouse-1: drag this file to another 
program
 mouse-2: visit this file in other window"
-                          "mouse-2: visit this file in other window")))
-         (when (< (+ (point) 4) (line-end-position))
-           (put-text-property (+ (point) 4) (line-end-position)
-                              'invisible 'dired-hide-details-link))))
+                              "mouse-2: visit this file in other window"))))
+           (when (< (+ end 5) (line-end-position))
+             (put-text-property (+ end 5) (line-end-position)
+                                'invisible 'dired-hide-details-link)))))
       (forward-line 1))))
 
 (defun dired--make-directory-clickable ()
@@ -2287,7 +2290,9 @@ Do so according to the former subdir alist 
OLD-SUBDIR-ALIST."
   ": d"     #'epa-dired-do-decrypt
   ": v"     #'epa-dired-do-verify
   ": s"     #'epa-dired-do-sign
-  ": e"     #'epa-dired-do-encrypt)
+  ": e"     #'epa-dired-do-encrypt
+  ;; Click-to-select.
+  "<touchscreen-hold>" #'dired-enable-click-to-select-mode)
 
 (put 'dired-find-file :advertised-binding (kbd "RET"))
 
@@ -3552,9 +3557,9 @@ as returned by `dired-get-filename'.  LIMIT is the search 
limit."
 
 ;; FIXME document whatever dired-x is doing.
 (defun dired-initial-position (dirname)
-  "Where point should go in a new listing of DIRNAME.
-Point is assumed to be at the beginning of new subdir line.
-It runs the hook `dired-initial-position-hook'."
+  "Return position of point in a new listing of DIRNAME.
+Point is assumed to be at the beginning of a new subdir line.
+Runs the hook `dired-initial-position-hook'."
   (end-of-line)
   (and (featurep 'dired-x) dired-find-subdir
        (dired-goto-subdir dirname))
@@ -3700,6 +3705,11 @@ non-empty directories is allowed."
       (or nomessage
          (message "(No deletions requested)")))))
 
+(defun dired-post-do-command ()
+  "Disable `dired-click-to-select-mode' after an operation."
+  (when dired-click-to-select-mode
+    (dired-click-to-select-mode -1)))
+
 (defun dired-do-delete (&optional arg)
   "Delete all marked (or next ARG) files.
 `dired-recursive-deletes' controls whether deletion of
@@ -3717,7 +3727,8 @@ non-empty directories is allowed."
                                     m))
                             arg))
      arg t)
-    (dolist (m markers) (set-marker m nil))))
+    (dolist (m markers) (set-marker m nil)))
+  (dired-post-do-command))
 
 (defvar dired-deletion-confirmer 'yes-or-no-p) ; or y-or-n-p?
 
@@ -4938,6 +4949,97 @@ Interactively with prefix argument, read FILE-NAME."
   (interactive nil dired-mode)
   (eww-open-file (dired-get-file-for-visit)))
 
+
+;;; Click-To-Select mode
+
+(defvar dired-click-to-select-map (make-sparse-keymap)
+  "Keymap placed on files under `dired-click-to-select' mode.")
+
+(define-key dired-click-to-select-map [mouse-2]
+            #'dired-mark-for-click)
+
+(defun dired-mark-for-click (event)
+  "Mark or unmark the file underneath the mouse click at EVENT.
+See `dired-click-to-select-mode' for more details."
+  (interactive "e")
+  (let ((posn (event-start event))
+        (inhibit-read-only t))
+    (with-selected-window (posn-window posn)
+      (goto-char (posn-point posn))
+      (save-excursion
+        (dired-repeat-over-lines
+         1 (lambda ()
+             (let ((char (char-after)))
+               (when (or (not (looking-at-p dired-re-dot))
+                         (not (equal dired-marker-char dired-del-marker)))
+                 (delete-char 1)
+                 (insert (if (eq char dired-marker-char)
+                             ;; Insert a space to unmark the file if
+                             ;; it's already marked.
+                             ?\s
+                           ;; Otherwise mark the file.
+                           dired-marker-char))))))))))
+
+(defun dired-enable-click-to-select-mode (event)
+  "Enable `dired-click-to-select-mode' and mark the file under EVENT.
+If there is no file under EVENT, call `touch-screen-hold' with
+EVENT instead."
+  (interactive "e")
+  (let* ((posn (event-start event))
+         (window (posn-window posn))
+         (point (posn-point posn)))
+    (if (and window point
+             (get-text-property point 'dired-filename
+                                (window-buffer window)))
+        (progn (beep)
+               (touch-screen-inhibit-drag)
+               (with-selected-window window
+                 (goto-char point)
+                 (save-excursion (dired-mark 1))
+                 (dired-click-to-select-mode 1)))
+      (touch-screen-hold event))))
+
+(define-minor-mode dired-click-to-select-mode
+  "Toggle click-to-select inside this Dired buffer.
+When this minor mode is enabled, using `mouse-2' on a file name
+within a Dired buffer will toggle its mark instead of going to it
+within another window.
+
+Disabling this minor mode will unmark all files within the Dired
+buffer.
+
+`dired-click-to-select-mode' is automatically disabled after any
+Dired operation (command whose name starts with `dired-do')
+completes."
+  :group 'dired
+  :lighter " Click-To-Select"
+  (unless (derived-mode-p 'dired-mode 'wdired-mode)
+    (error "Not a Dired buffer"))
+  (if dired-click-to-select-mode
+      (setq-local tool-bar-map
+                  `(keymap (exit-click-to-select menu-item
+                            "Exit Click To Select Mode"
+                            dired-click-to-select-mode
+                            :help "Exit `dired-click-to-select-mode'."
+                            :image ,(tool-bar--image-expression "close")
+                            :enable t)))
+    ;; Reset the default tool bar.
+    (kill-local-variable 'tool-bar-map)
+    (dired-unmark-all-marks))
+  ;; Repropertize this Dired buffer.
+  (let ((inhibit-read-only t))
+    (remove-text-properties (point-min) (point-max)
+                            '(invisible nil
+                              keymap nil
+                              dired-filename nil
+                              help-echo nil
+                              mouse-face nil))
+    (when dired-make-directory-clickable
+      (dired--make-directory-clickable))
+    (dired-insert-set-properties (point-min) (point-max)))
+  ;; Redisplay the tool bar.
+  (force-mode-line-update))
+
 (provide 'dired)
 
 (run-hooks 'dired-load-hook)           ; for your customizations
diff --git a/lisp/doc-view.el b/lisp/doc-view.el
index 002b74e8898..c7edbd6e150 100644
--- a/lisp/doc-view.el
+++ b/lisp/doc-view.el
@@ -645,7 +645,7 @@ Typically \"page-%s.png\".")
       :help                     "Reset the current slice"
       :enabled                  (image-mode-window-get 'slice)])
     "---"
-    ["New Search"               (doc-view-search t)
+    ["New Search"               doc-view-new-search
      :help                      "Initiate a new search"]
     ["Search Forward"           doc-view-search
      :help                      "Jump to the next match or initiate a new 
search"]
@@ -668,6 +668,45 @@ Typically \"page-%s.png\".")
       :style radio :selected    (eq major-mode 'doc-view-mode)])
     ["Exit DocView Mode" doc-view-minor-mode]))
 
+(defvar doc-view-tool-bar-map
+  (let ((map (make-sparse-keymap)))
+    ;; Most of these items are the same as in the default tool bar
+    ;; map, but with extraneous items removed, and with extra search
+    ;; and navigation items.
+    (tool-bar-local-item-from-menu 'find-file "new" map
+                                   nil :label "New File"
+                                  :vert-only t)
+    (tool-bar-local-item-from-menu 'menu-find-file-existing "open" map
+                                   nil :label "Open" :vert-only t)
+    (tool-bar-local-item-from-menu 'dired "diropen" map nil :vert-only t)
+    (tool-bar-local-item-from-menu 'kill-this-buffer "close" map nil
+                                   :vert-only t)
+    (define-key-after map [separator-1] menu-bar-separator)
+    (tool-bar-local-item-from-menu 'doc-view-new-search "search"
+                                  map doc-view-mode-map :vert-only t
+                                   :help "Start a new search query.")
+    (tool-bar-local-item-from-menu 'doc-view-search-backward "left-arrow"
+                                  map doc-view-mode-map
+                                   :vert-only t
+                                   :enable 'doc-view--current-search-matches
+                                   :help "Move to the last search result.")
+    (tool-bar-local-item-from-menu 'doc-view-search "right-arrow"
+                                  map doc-view-mode-map :vert-only t
+                                   :enable 'doc-view--current-search-matches
+                                   :help "Move to the next search result.")
+    (define-key-after map [separator-2] menu-bar-separator)
+    (tool-bar-local-item-from-menu 'doc-view-previous-page "last-page"
+                                   map doc-view-mode-map :vert-only t
+                                   :enable '(> (doc-view-current-page) 1)
+                                   :help "Move to the next page.")
+    (tool-bar-local-item-from-menu 'doc-view-next-page "next-page"
+                                   map doc-view-mode-map :vert-only t
+                                   :enable '(< (doc-view-current-page)
+                                               (doc-view-last-page-number))
+                                   :help "Move to the next page.")
+    map)
+  "Like the default `tool-bar-map', but with additions for DocView.")
+
 ;;;; Navigation Commands
 
 (defun doc-view-last-page-number ()
@@ -1865,7 +1904,16 @@ If BACKWARD is non-nil, jump to the previous match."
        ;; We must convert to TXT first!
        (if doc-view--current-converter-processes
            (message "DocView: please wait till conversion finished.")
-         (doc-view-doc->txt txt (lambda () (doc-view-search nil))))))))
+         (doc-view-doc->txt txt (lambda () (doc-view-search nil))))))
+    ;; Update the tool bar items.
+    (force-mode-line-update)))
+
+(defun doc-view-new-search ()
+  "Initiate a new search query.
+Prompt for a string, then search for its appearances within
+the document text."
+  (interactive)
+  (doc-view-search t nil))
 
 (defun doc-view-search-next-match (arg)
   "Go to the ARGth next matching page."
@@ -2244,6 +2292,8 @@ toggle between displaying the document or editing it as 
text.
       (imenu-unavailable (message "imenu support unavailable: %s"
                                   (cadr imenu-error))))
     (doc-view-initiate-display)
+    ;; Replace the tool bar map with `doc-view-tool-bar-map'.
+    (setq-local tool-bar-map doc-view-tool-bar-map)
     ;; Switch off view-mode explicitly, because doc-view-mode is the
     ;; canonical view mode for PDF/PS/DVI files.  This could be
     ;; switched on automatically depending on the value of
diff --git a/lisp/elec-pair.el b/lisp/elec-pair.el
index 416c95e7a34..e101cbc9e6f 100644
--- a/lisp/elec-pair.el
+++ b/lisp/elec-pair.el
@@ -162,6 +162,20 @@ Before attempting a skip, if 
`electric-pair-skip-whitespace' is
 non-nil, this function is called.  It move point to a new buffer
 position, presumably skipping only whitespace in between.")
 
+(defun electric-pair-analyze-conversion (string)
+  "Notice that STRING has been deleted by an input method.
+If the last character of STRING is an electric pair character,
+and the character after point is too, then delete that other
+character."
+  (let* ((prev (aref string (1- (length string))))
+         (next (char-after))
+         (syntax-info (electric-pair-syntax-info prev))
+         (syntax (car syntax-info))
+         (pair (cadr syntax-info)))
+    (when (and next pair (memq syntax '(?\( ?\" ?\$))
+               (eq pair next))
+      (delete-char 1))))
+
 (defun electric-pair--skip-whitespace ()
   "Skip whitespace forward, not crossing comment or string boundaries."
   (let ((saved (point))
diff --git a/lisp/emacs-lisp/advice.el b/lisp/emacs-lisp/advice.el
index 56f0ae2212c..3265809f592 100644
--- a/lisp/emacs-lisp/advice.el
+++ b/lisp/emacs-lisp/advice.el
@@ -3131,6 +3131,7 @@ usage: (defadvice FUNCTION (CLASS NAME [POSITION] 
[ARGLIST] FLAG...)
           [DOCSTRING] [INTERACTIVE-FORM]
           BODY...)"
   (declare (doc-string 3) (indent 2)
+           (obsolete "use advice-add or define-advice" "30.1")
            (debug (&define name  ;; thing being advised.
                            (name ;; class is [&or "before" "around" "after"
                                  ;;               "activation" "deactivation"]
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index c7d8531a870..12c2bc51b92 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -2141,7 +2141,7 @@ See Info node `(elisp) Integer Basics'."
    '(byte-constant byte-dup byte-stack-ref byte-stack-set byte-discard
      byte-discardN byte-discardN-preserve-tos
      byte-symbolp byte-consp byte-stringp byte-listp byte-numberp byte-integerp
-     byte-eq byte-not
+     byte-not
      byte-cons byte-list1 byte-list2 byte-list3 byte-list4 byte-listN
      byte-interactive-p)
    ;; How about other side-effect-free-ops?  Is it safe to move an
@@ -2149,6 +2149,11 @@ See Info node `(elisp) Integer Basics'."
    ;; No, it is not, because the unwind-protect forms can alter
    ;; the inside of the object to which nth would apply.
    ;; For the same reason, byte-equal was deleted from this list.
+   ;;
+   ;; In particular, `byte-eq' isn't here despite `eq' being nominally
+   ;; pure because it is currently affected by `symbols-with-pos-enabled'
+   ;; and so cannot be sunk past an unwind op that might end a binding of
+   ;; that variable.  Yes, this is unsatisfactory.
    "Byte-codes that can be moved past an unbind.")
 
 (defconst byte-compile-side-effect-and-error-free-ops
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index 960debd1660..ddc1c316bc7 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -1975,6 +1975,10 @@ also be compiled."
       (emacs-lisp-compilation-mode))
     (let ((directories (list default-directory))
          (default-directory default-directory)
+          (ignore-files-regexp
+           (if byte-compile-ignore-files
+               (mapconcat #'identity byte-compile-ignore-files "\\|")
+             regexp-unmatchable))
          (skip-count 0)
          (fail-count 0)
          (file-count 0)
@@ -1995,9 +1999,7 @@ also be compiled."
                      (or (null arg) (eq 0 arg)
                          (y-or-n-p (concat "Check " source "? ")))
                       ;; Directory is requested to be ignored
-                      (not (string-match-p
-                            (regexp-opt byte-compile-ignore-files)
-                            source))
+                      (not (string-match-p ignore-files-regexp source))
                       (setq directories (nconc directories (list source))))
                ;; It is an ordinary file.  Decide whether to compile it.
                (if (and (string-match emacs-lisp-file-regexp source)
@@ -2007,9 +2009,7 @@ also be compiled."
                         (not (auto-save-file-name-p source))
                         (not (member source (dir-locals--all-files directory)))
                         ;; File is requested to be ignored
-                        (not (string-match-p
-                              (regexp-opt byte-compile-ignore-files)
-                              source)))
+                        (not (string-match-p ignore-files-regexp source)))
                    (progn (cl-incf
                            (pcase (byte-recompile-file source force arg)
                              ('no-byte-compile skip-count)
diff --git a/lisp/emacs-lisp/checkdoc.el b/lisp/emacs-lisp/checkdoc.el
index c5e69d5ef56..aadd6480086 100644
--- a/lisp/emacs-lisp/checkdoc.el
+++ b/lisp/emacs-lisp/checkdoc.el
@@ -2382,7 +2382,7 @@ Code:, and others referenced in the style guide."
        err
        (or
        ;; * Commentary Section
-        (if (and (not (lm-commentary-mark))
+        (if (and (not (lm-commentary-start))
                  ;; No need for a commentary section in test files.
                  (not (string-match
                        (rx (or (seq (or "-test.el" "-tests.el") string-end)
@@ -2419,10 +2419,10 @@ Code:, and others referenced in the style guide."
        (if (or (not checkdoc-force-history-flag)
                (file-exists-p "ChangeLog")
                (file-exists-p "../ChangeLog")
-                (lm-history-mark))
+                (lm-history-start))
            nil
          (progn
-           (goto-char (or (lm-commentary-mark) (point-min)))
+            (goto-char (or (lm-commentary-start) (point-min)))
            (cond
             ((re-search-forward
               "write\\s-+to\\s-+the\\s-+Free Software Foundation, Inc."
@@ -2443,7 +2443,7 @@ Code:, and others referenced in the style guide."
        err
        (or
        ;; * Code section
-       (if (not (lm-code-mark))
+        (if (not (lm-code-start))
            (let ((cont t)
                  pos)
              (goto-char (point-min))
@@ -2494,7 +2494,7 @@ Code:, and others referenced in the style guide."
       ;; Let's spellcheck the commentary section.  This is the only
       ;; section that is easy to pick out, and it is also the most
       ;; visible section (with the finder).
-      (let ((cm (lm-commentary-mark)))
+      (let ((cm (lm-commentary-start)))
         (when cm
           (save-excursion
             (goto-char cm)
diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el
index 18d3eb37af3..3a786bb321b 100644
--- a/lisp/emacs-lisp/eldoc.el
+++ b/lisp/emacs-lisp/eldoc.el
@@ -991,7 +991,8 @@ the docstrings eventually produced, using
  "mark-paragraph" "mouse-set-point" "move-" "move-beginning-of-"
  "move-end-of-" "newline" "next-" "other-window" "pop-global-mark"
  "previous-" "recenter" "right-" "scroll-" "self-insert-command"
- "split-window-" "up-list")
+ "split-window-" "up-list" "touch-screen-handle-touch"
+ "analyze-text-conversion")
 
 (provide 'eldoc)
 
diff --git a/lisp/emacs-lisp/lisp-mnt.el b/lisp/emacs-lisp/lisp-mnt.el
index 1fa1297e787..67c9db29b7f 100644
--- a/lisp/emacs-lisp/lisp-mnt.el
+++ b/lisp/emacs-lisp/lisp-mnt.el
@@ -1,7 +1,6 @@
 ;;; lisp-mnt.el --- utility functions for Emacs Lisp maintainers  -*- 
lexical-binding:t -*-
 
-;; Copyright (C) 1992, 1994, 1997, 2000-2023 Free Software Foundation,
-;; Inc.
+;; Copyright (C) 1992-2023 Free Software Foundation, Inc.
 
 ;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
 ;; Maintainer: emacs-devel@gnu.org
@@ -52,7 +51,7 @@
 ;;
 ;;    * Copyright line, which looks more or less like this:
 ;;
-;;       ;; Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+;;       ;; Copyright (C) 1999-2001 Free Software Foundation, Inc.
 ;;
 ;;    * A blank line
 ;;
@@ -187,7 +186,6 @@ If the given section does not exist, return nil."
       (goto-char (point-min))
       (if (re-search-forward (lm-get-header-re header 'section) nil t)
           (line-beginning-position (if after 2))))))
-(defalias 'lm-section-mark 'lm-section-start)
 
 (defun lm-section-end (header)
   "Return the buffer location of the end of a given section.
@@ -230,12 +228,10 @@ a section."
 (defun lm-code-start ()
   "Return the buffer location of the `Code' start marker."
   (lm-section-start "Code"))
-(defalias 'lm-code-mark 'lm-code-start)
 
 (defun lm-commentary-start ()
   "Return the buffer location of the `Commentary' start marker."
   (lm-section-start lm-commentary-header))
-(defalias 'lm-commentary-mark 'lm-commentary-start)
 
 (defun lm-commentary-end ()
   "Return the buffer location of the `Commentary' section end."
@@ -244,7 +240,6 @@ a section."
 (defun lm-history-start ()
   "Return the buffer location of the `History' start marker."
   (lm-section-start lm-history-header))
-(defalias 'lm-history-mark 'lm-history-start)
 
 (defun lm-copyright-mark ()
   "Return the buffer location of the `Copyright' line."
@@ -258,7 +253,7 @@ a section."
   "Return the contents of the header named HEADER."
   (goto-char (point-min))
   (let ((case-fold-search t))
-    (when (and (re-search-forward (lm-get-header-re header) (lm-code-mark) t)
+    (when (and (re-search-forward (lm-get-header-re header) (lm-code-start) t)
               ;;   RCS ident likes format "$identifier: data$"
               (looking-at
                (if (save-excursion
@@ -402,7 +397,7 @@ ISO-DATE non-nil means return the date in ISO 8601 format."
     (when (progn (goto-char (point-min))
                 (re-search-forward
                  "\\$[I]d: [^ ]+ [^ ]+ \\([^/]+\\)/\\([^/]+\\)/\\([^ ]+\\) "
-                 (lm-code-mark) t))
+                  (lm-code-start) t))
       (let ((dd (match-string 3))
            (mm (match-string 2))
            (yyyy (match-string 1)))
@@ -420,7 +415,7 @@ ISO-DATE non-nil means return the date in ISO 8601 format."
 This can be found in an RCS or SCCS header."
   (lm-with-file file
     (or (lm-header "version")
-       (let ((header-max (lm-code-mark)))
+        (let ((header-max (lm-code-start)))
          (goto-char (point-min))
          (cond
           ;; Look for an RCS header
@@ -557,11 +552,11 @@ copyright notice is allowed."
                "`Keywords:' tag missing")
               ((not (lm-keywords-finder-p))
                "`Keywords:' has no valid finder keywords (see 
`finder-known-keywords')")
-              ((not (lm-commentary-mark))
+               ((not (lm-commentary-start))
                "Can't find a `Commentary' section marker")
-              ((not (lm-history-mark))
+               ((not (lm-history-start))
                "Can't find a `History' section marker")
-              ((not (lm-code-mark))
+               ((not (lm-code-start))
                "Can't find a `Code' section marker")
               ((progn
                  (goto-char (point-max))
@@ -631,6 +626,11 @@ Prompts for bug subject TOPIC.  Leaves you in a mail 
buffer."
     (message "%s"
      (substitute-command-keys "Type \\[mail-send] to send bug report."))))
 
+(define-obsolete-function-alias 'lm-section-mark #'lm-section-start "30.1")
+(define-obsolete-function-alias 'lm-code-mark #'lm-code-start "30.1")
+(define-obsolete-function-alias 'lm-commentary-mark #'lm-commentary-start 
"30.1")
+(define-obsolete-function-alias 'lm-history-mark #'lm-history-start "30.1")
+
 (provide 'lisp-mnt)
 
 ;;; lisp-mnt.el ends here
diff --git a/lisp/emacs-lisp/loaddefs-gen.el b/lisp/emacs-lisp/loaddefs-gen.el
index 5db9af21508..d7b9b131bc8 100644
--- a/lisp/emacs-lisp/loaddefs-gen.el
+++ b/lisp/emacs-lisp/loaddefs-gen.el
@@ -431,7 +431,8 @@ don't include."
                     ;; have an autoload cookie on the first column of a
                     ;; doc string or the like.  (The Emacs tree
                     ;; shouldn't contain any such instances.)
-                    (not (ppss-string-terminator (syntax-ppss))))
+                    (not (ppss-string-terminator
+                          (save-match-data (syntax-ppss)))))
             ;; ... and if we have one of these names, then alter outfile.
             (let* ((aname (match-string 2))
                    (to-file (if aname
diff --git a/lisp/emacs-lisp/oclosure.el b/lisp/emacs-lisp/oclosure.el
index 40f1f54eed0..466822e0e06 100644
--- a/lisp/emacs-lisp/oclosure.el
+++ b/lisp/emacs-lisp/oclosure.el
@@ -50,7 +50,7 @@
 ;; - coercion wrappers, as in "Threesomes, with and without blame"
 ;;   https://dl.acm.org/doi/10.1145/1706299.1706342, or
 ;;   "On the Runtime Complexity of Type-Directed Unboxing"
-;;   http://sv.c.titech.ac.jp/minamide/papers.html
+;;   https://sv.c.titech.ac.jp/minamide/papers.html
 ;; - An efficient `negate' operation such that
 ;;   (negate (negate f)) returns just `f' and (negate #'<) returns #'>=.
 ;; - Autoloads (tho currently our bytecode functions (and hence OClosures)
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index 392f8fafa79..6ce00bf4d6d 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -609,7 +609,7 @@ package."
   (package-archive-priority (package-desc-archive pkg-desc)))
 
 (defun package--parse-elpaignore (pkg-desc)
-  "Return the of regular expression to match files ignored by PKG-DESC."
+  "Return a list of regular expressions to match files ignored by PKG-DESC."
   (let* ((pkg-dir (file-name-as-directory (package-desc-dir pkg-desc)))
          (ignore (expand-file-name ".elpaignore" pkg-dir))
          files)
diff --git a/lisp/emacs-lisp/rx.el b/lisp/emacs-lisp/rx.el
index 19c82d9b23d..d46d0ca5a98 100644
--- a/lisp/emacs-lisp/rx.el
+++ b/lisp/emacs-lisp/rx.el
@@ -35,8 +35,43 @@
 ;; Olin Shivers's SRE, with concessions to Emacs regexp peculiarities,
 ;; and the older Emacs package Sregex.
 
+;;; Legacy syntax still accepted by rx:
+;;
+;; These are constructs from earlier rx and sregex implementations
+;; that were mistakes, accidents or just not very good ideas in hindsight.
+
+;; Obsolete: accepted but not documented
+;;
+;; Obsolete                     Preferred
+;; --------------------------------------------------------
+;; (not word-boundary)          not-word-boundary
+;; (not-syntax X)               (not (syntax X))
+;; not-wordchar                 (not wordchar)
+;; (not-char ...)               (not (any ...))
+;; any                          nonl, not-newline
+;; (repeat N FORM)              (= N FORM)
+;; (syntax CHARACTER)           (syntax NAME)
+;; (syntax CHAR-SYM)      [1]   (syntax NAME)
+;; (category chinse-two-byte)   (category chinese-two-byte)
+;; unibyte                      ascii
+;; multibyte                    nonascii
+;; --------------------------------------------------------
+;; [1]  where CHAR-SYM is a symbol with single-character name
+
+;; Obsolescent: accepted and documented but discouraged
+;;
+;; Obsolescent                    Preferred
+;; --------------------------------------------------------
+;; (and ...)                      (seq ...), (: ...), (sequence ...)
+;; anything                       anychar
+;; minimal-match, maximal-match   lazy ops: ??, *?, +?
+
+;; FIXME: Prepare a phase-out by emitting compile-time warnings about
+;; at least some of the legacy constructs above.
+
 ;;; Code:
 
+
 ;; The `rx--translate...' functions below return (REGEXP . PRECEDENCE),
 ;; where REGEXP is a list of string expressions that will be
 ;; concatenated into a regexp, and PRECEDENCE is one of
@@ -167,7 +202,7 @@ Each entry is:
     ('not-word-boundary           (cons (list "\\B") t))
     ('symbol-start                (cons (list "\\_<") t))
     ('symbol-end                  (cons (list "\\_>") t))
-    ('not-wordchar                (cons (list "\\W") t))
+    ('not-wordchar                (rx--translate '(not wordchar)))
     (_
      (cond
       ((let ((class (cdr (assq sym rx--char-classes))))
@@ -419,86 +454,96 @@ a list of named character classes in the order they occur 
in BODY."
 If NEGATED is non-nil, negate the result; INTERVALS is a sorted
 list of disjoint intervals and CLASSES a list of named character
 classes."
-  (let ((items (append intervals classes)))
-    ;; Move lone ] and range ]-x to the start.
-    (let ((rbrac-l (assq ?\] items)))
-      (when rbrac-l
-        (setq items (cons rbrac-l (delq rbrac-l items)))))
-
-    ;; Split x-] and move the lone ] to the start.
-    (let ((rbrac-r (rassq ?\] items)))
-      (when (and rbrac-r (not (eq (car rbrac-r) ?\])))
-        (setcdr rbrac-r ?\\)
-        (setq items (cons '(?\] . ?\]) items))))
-
-    ;; Split ,-- (which would end up as ,- otherwise).
-    (let ((dash-r (rassq ?- items)))
-      (when (eq (car dash-r) ?,)
-        (setcdr dash-r ?,)
-        (setq items (nconc items '((?- . ?-))))))
-
-    ;; Remove - (lone or at start of interval)
-    (let ((dash-l (assq ?- items)))
-      (when dash-l
-        (if (eq (cdr dash-l) ?-)
-            (setq items (delq dash-l items))   ; Remove lone -
-          (setcar dash-l ?.))                  ; Reduce --x to .-x
-        (setq items (nconc items '((?- . ?-))))))
-
-    ;; Deal with leading ^ and range ^-x in non-negated set.
-    (when (and (eq (car-safe (car items)) ?^)
-               (not negated))
-      (if (eq (cdar items) ?^)
-          ;; single leading ^
-          (when (cdr items)
-            ;; Move the ^ to second place.
-            (setq items (cons (cadr items)
-                              (cons (car items) (cddr items)))))
-        ;; Split ^-x to _-x^
-        (setq items (cons (cons ?_ (cdar items))
-                          (cons '(?^ . ?^)
-                                (cdr items))))))
-
-    (cond
-     ;; Empty set: if negated, any char, otherwise match-nothing.
-     ((null items)
+  ;; No, this is not pretty code.  You try doing it in a way that is both
+  ;; elegant and efficient.  Or just one of the two.  I dare you.
+  (cond
+   ;; Single character.
+   ((and intervals (eq (caar intervals) (cdar intervals))
+         (null (cdr intervals))
+         (null classes))
+    (let ((ch (caar intervals)))
       (if negated
-          (rx--translate-symbol 'anything)
-        (rx--empty)))
-     ;; Single non-negated character.
-     ((and (null (cdr items))
-           (consp (car items))
-           (eq (caar items) (cdar items))
-           (not negated))
-      (cons (list (regexp-quote (char-to-string (caar items))))
-            t))
-     ;; Negated newline.
-     ((and (equal items '((?\n . ?\n)))
-           negated)
-      (rx--translate-symbol 'nonl))
-     ;; At least one character or class, possibly negated.
-     (t
+          (if (eq ch ?\n)
+              ;; Single negated newline.
+              (rx--translate-symbol 'nonl)
+            ;; Single negated character (other than newline).
+            (cons (list (string ?\[ ?^ ch ?\])) t))
+        ;; Single literal character.
+        (cons (list (regexp-quote (char-to-string ch))) t))))
+
+   ;; Empty set (or any char).
+   ((and (null intervals) (null classes))
+    (if negated
+        (rx--translate-symbol 'anything)
+      (rx--empty)))
+
+   ;; More than one character, or at least one class.
+   (t
+    (let ((dash nil) (caret nil))
+      ;; Move ] and range ]-x to the start.
+      (let ((rbrac-l (assq ?\] intervals)))
+        (when rbrac-l
+          (setq intervals (cons rbrac-l (remq rbrac-l intervals)))))
+
+      ;; Split x-] and move the lone ] to the start.
+      (let ((rbrac-r (rassq ?\] intervals)))
+        (when (and rbrac-r (not (eq (car rbrac-r) ?\])))
+          (setcdr rbrac-r ?\\)
+          (setq intervals (cons '(?\] . ?\]) intervals))))
+
+      ;; Split ,-- (which would end up as ,- otherwise).
+      (let ((dash-r (rassq ?- intervals)))
+        (when (eq (car dash-r) ?,)
+          (setcdr dash-r ?,)
+          (setq dash "-")))
+
+      ;; Remove - (lone or at start of interval)
+      (let ((dash-l (assq ?- intervals)))
+        (when dash-l
+          (if (eq (cdr dash-l) ?-)
+              (setq intervals (remq dash-l intervals))   ; Remove lone -
+            (setcar dash-l ?.))                          ; Reduce --x to .-x
+          (setq dash "-")))
+
+      ;; Deal with leading ^ and range ^-x in non-negated set.
+      (when (and (eq (caar intervals) ?^)
+                 (not negated))
+        (if (eq (cdar intervals) ?^)
+            ;; single leading ^
+            (if (or (cdr intervals) classes)
+                ;; something else to put before the ^
+                (progn
+                  (setq intervals (cdr intervals))   ; remove lone ^
+                  (setq caret "^"))                  ; put ^ (almost) last
+              ;; nothing else but a lone -
+              (setq intervals (cons '(?- . ?-) intervals))  ; move - first
+              (setq dash nil))
+          ;; split ^-x to _-x^
+          (setq intervals `((?_ . ,(cdar intervals)) (?^ . ?^)
+                            . ,(cdr intervals)))))
+
       (cons
        (list
         (concat
          "["
          (and negated "^")
-         (mapconcat (lambda (item)
-                      (cond ((symbolp item)
-                             (format "[:%s:]" item))
-                            ((eq (car item) (cdr item))
-                             (char-to-string (car item)))
-                            ((eq (1+ (car item)) (cdr item))
-                             (string (car item) (cdr item)))
+         (mapconcat (lambda (iv)
+                      (cond ((eq (car iv) (cdr iv))
+                             (char-to-string (car iv)))
+                            ((eq (1+ (car iv)) (cdr iv))
+                             (string (car iv) (cdr iv)))
                             ;; Ranges that go between normal chars and raw 
bytes
                             ;; must be split to avoid being mutilated
                             ;; by Emacs's regexp parser.
-                            ((<= (car item) #x3fff7f (cdr item))
-                             (string (car item) ?- #x3fff7f
-                                     #x3fff80 ?- (cdr item)))
+                            ((<= (car iv) #x3fff7f (cdr iv))
+                             (string (car iv) ?- #x3fff7f
+                                     #x3fff80 ?- (cdr iv)))
                             (t
-                             (string (car item) ?- (cdr item)))))
-                    items nil)
+                             (string (car iv) ?- (cdr iv)))))
+                    intervals)
+         (mapconcat (lambda (cls) (format "[:%s:]" cls)) classes)
+         caret                          ; ^ or nothing
+         dash                           ; - or nothing
          "]"))
        t)))))
 
@@ -602,10 +647,28 @@ If NEGATED, negate the sense (thus making it positive)."
 
 (defun rx--union-intervals (ivs-a ivs-b)
   "Union of the interval lists IVS-A and IVS-B."
-  (rx--complement-intervals
-   (rx--intersect-intervals
-    (rx--complement-intervals ivs-a)
-    (rx--complement-intervals ivs-b))))
+  (let ((union nil))
+    (while (and ivs-a ivs-b)
+      (let ((a (car ivs-a))
+            (b (car ivs-b)))
+        (cond
+         ((< (1+ (cdr a)) (car b))      ; a before b, not adacent
+          (push a union)
+          (setq ivs-a (cdr ivs-a)))
+         ((< (1+ (cdr b)) (car a))      ; b before a, not adacent
+          (push b union)
+          (setq ivs-b (cdr ivs-b)))
+         (t                             ; a and b adjacent or overlap
+          (setq ivs-a (cdr ivs-a))
+          (setq ivs-b (cdr ivs-b))
+          (if (< (cdr a) (cdr b))
+              (push (cons (min (car a) (car b))
+                          (cdr b))
+                    ivs-b)
+            (push (cons (min (car a) (car b))
+                        (cdr a))
+                  ivs-a))))))
+    (nconc (nreverse union) (or ivs-a ivs-b))))
 
 (defun rx--charset-intervals (charset)
   "Return a sorted list of non-adjacent disjoint intervals from CHARSET.
@@ -789,7 +852,10 @@ Return (REGEXP . PRECEDENCE)."
                 (setq syntax char)))))))
       (unless syntax
         (error "Unknown rx syntax name `%s'" sym)))
-    (cons (list (string ?\\ (if negated ?S ?s) syntax))
+    ;; Produce \w and \W instead of \sw and \Sw, for smaller size.
+    (cons (list (if (eq syntax ?w)
+                    (string ?\\ (if negated ?W ?w))
+                  (string ?\\ (if negated ?S ?s) syntax)))
           t)))
 
 (defconst rx--categories
diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el
index e23db8e999c..dbc061d8a70 100644
--- a/lisp/emacs-lisp/shortdoc.el
+++ b/lisp/emacs-lisp/shortdoc.el
@@ -153,14 +153,14 @@ A FUNC form can have any number of `:no-eval' (or 
`:no-value'),
    :eval (let* ((old '((foo . bar)))
                 (new (copy-alist old)))
            (eq old new)))
-  ;; FIXME: Outputs "\.rose" for the symbol `.rose'.
-  ;; (let-alist
-  ;;     :eval (let ((colors '((rose . red)
-  ;;                           (lily . white))))
-  ;;             (let-alist colors
-  ;;               (if (eq .rose 'red)
-  ;;                   .lily))))
-  )
+  ;; FIXME: Outputs "\.rose" for the symbol `.rose'.  It would be
+  ;; better if that could be cleaned up.
+  (let-alist
+      :eval (let ((colors '((rose . red)
+                            (lily . white))))
+              (let-alist colors
+                (if (eq .rose 'red)
+                    .lily)))))
 
 (define-short-documentation-group string
   "Making Strings"
@@ -642,6 +642,8 @@ A FUNC form can have any number of `:no-eval' (or 
`:no-value'),
   (delete
    :eval (delete 2 (list 1 2 3 4))
    :eval (delete "a" (list "a" "b" "c" "d")))
+  (remq
+   :eval (remq 'b '(a b c)))
   (remove
    :eval (remove 2 '(1 2 3 4))
    :eval (remove "a" '("a" "b" "c" "d")))
@@ -686,8 +688,6 @@ A FUNC form can have any number of `:no-eval' (or 
`:no-value'),
   (member
    :eval (member 2 '(1 2 3))
    :eval (member "b" '("a" "b" "c")))
-  (remq
-   :eval (remq 'b '(a b c)))
   (member-ignore-case
    :eval (member-ignore-case "foo" '("bar" "Foo" "zot")))
   "Association Lists"
diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el
index 9e906930b92..78dc58e0bcd 100644
--- a/lisp/emacs-lisp/subr-x.el
+++ b/lisp/emacs-lisp/subr-x.el
@@ -337,12 +337,15 @@ as the new values of the bound variables in the recursive 
invocation."
     ;; Keeping a work buffer around is more efficient than creating a
     ;; new temporary buffer.
     (with-current-buffer (get-buffer-create " *string-pixel-width*")
-      ;; `display-line-numbers-mode' is enabled in internal buffers
-      ;; that breaks width calculation, so need to disable (bug#59311)
+      ;; If `display-line-numbers-mode' is enabled in internal
+      ;; buffers, it breaks width calculation, so disable it (bug#59311)
       (when (bound-and-true-p display-line-numbers-mode)
         (display-line-numbers-mode -1))
       (delete-region (point-min) (point-max))
-      (insert string)
+      ;; Disable line-prefix and wrap-prefix, for the same reason.
+      (setq line-prefix nil
+           wrap-prefix nil)
+      (insert (propertize string 'line-prefix nil 'wrap-prefix nil))
       (car (buffer-text-pixel-size nil nil t)))))
 
 ;;;###autoload
diff --git a/lisp/emulation/viper-cmd.el b/lisp/emulation/viper-cmd.el
index c0aa9dd7b46..78114db0972 100644
--- a/lisp/emulation/viper-cmd.el
+++ b/lisp/emulation/viper-cmd.el
@@ -4724,15 +4724,15 @@ Please, specify your level now: "))
 (defun viper-submit-report ()
   "Submit bug report on Viper."
   (interactive)
-  (defvar x-display-color-p)
+  (defvar display-color-p)
   (defvar viper-frame-parameters)
   (defvar viper-minibuffer-emacs-face)
   (defvar viper-minibuffer-vi-face)
   (defvar viper-minibuffer-insert-face)
   (let ((reporter-prompt-for-summary-p t)
-        (x-display-color-p (if (viper-window-display-p)
-                              (x-display-color-p)
-                             'non-x))
+        (display-color-p (if (viper-window-display-p)
+                             (display-color-p)
+                           'non-x))
         (viper-frame-parameters (frame-parameters (selected-frame)))
        (viper-minibuffer-emacs-face (if (viper-has-face-support-p)
                                          (facep
@@ -4790,7 +4790,7 @@ Please, specify your level now: "))
                        'viper-expert-level
                        'major-mode
                        'window-system
-                        'x-display-color-p
+                        'display-color-p
                        'viper-frame-parameters
                        'viper-minibuffer-vi-face
                        'viper-minibuffer-insert-face
diff --git a/lisp/erc/erc-nicks.el b/lisp/erc/erc-nicks.el
index 67f513f8d3e..a7d0b0769f2 100644
--- a/lisp/erc/erc-nicks.el
+++ b/lisp/erc/erc-nicks.el
@@ -61,7 +61,7 @@
 ;; 2007/09 - erc-highlight-nicknames.el
 ;;           Initial release by by André Riemann
 
-;; [1] <http://www.github.com/leathekd/erc-hl-nicks>
+;; [1] <https://www.github.com/leathekd/erc-hl-nicks>
 ;; [2] <https://www.emacswiki.org/emacs/ErcHighlightNicknames>
 
 ;;; Code:
diff --git a/lisp/eshell/em-hist.el b/lisp/eshell/em-hist.el
index 2c199ec160f..79232d8e9b5 100644
--- a/lisp/eshell/em-hist.el
+++ b/lisp/eshell/em-hist.el
@@ -534,7 +534,7 @@ See also `eshell-read-history'."
          (forward-line 3)
          (while (search-backward "completion" nil 'move)
            (replace-match "history reference")))
-       (eshell-redisplay)
+        (redisplay)
        (message "Hit space to flush")
        (let ((ch (read-event)))
          (if (eq ch ?\ )
diff --git a/lisp/eshell/em-smart.el b/lisp/eshell/em-smart.el
index d8b7fadc2c2..d5002a59d14 100644
--- a/lisp/eshell/em-smart.el
+++ b/lisp/eshell/em-smart.el
@@ -214,7 +214,7 @@ The options are `begin', `after' or `end'."
      0 frame)
     (if affected
        (let (window-scroll-functions) ;;FIXME: Why?
-         (eshell-redisplay)))))
+          (redisplay)))))
 
 (defun eshell-smart-display-setup ()
   "Set the point to somewhere in the beginning of the last command."
@@ -261,7 +261,7 @@ and the end of the buffer are still visible."
        (recenter -1)
        ;; trigger the redisplay now, so that we catch any attempted
        ;; point motion; this is to cover for a redisplay bug
-       (eshell-redisplay))
+        (redisplay))
     (let ((top-point (point)))
       (and (memq 'eshell-smart-display-move pre-command-hook)
           (>= (point) eshell-last-input-start)
diff --git a/lisp/eshell/em-term.el b/lisp/eshell/em-term.el
index ab26da857b7..3f76c349a7e 100644
--- a/lisp/eshell/em-term.el
+++ b/lisp/eshell/em-term.el
@@ -55,10 +55,11 @@ which commands are considered visual in nature."
   :type 'hook)
 
 (defcustom eshell-visual-commands
-  '("vi" "vim"                          ; what is going on??
+  '("vi" "vim" "nvim"                   ; what is going on??
     "screen" "tmux" "top" "htop"        ; ok, a valid program...
     "less" "more"                       ; M-x view-file
     "lynx" "links" "ncftp"              ; eww, ange-ftp
+    "ncmpcpp"                           ; M-x mpc
     "mutt" "pine" "tin" "trn" "elm")    ; GNUS!!
   "A list of commands that present their output in a visual fashion.
 
@@ -66,7 +67,7 @@ Commands listed here are run in a term buffer.
 
 See also `eshell-visual-subcommands' and `eshell-visual-options'."
   :type '(repeat string)
-  :version "29.1")
+  :version "30.1")
 
 (defcustom eshell-visual-subcommands
   nil
diff --git a/lisp/eshell/esh-util.el b/lisp/eshell/esh-util.el
index 3608c78ba2b..87cd1f5dcb2 100644
--- a/lisp/eshell/esh-util.el
+++ b/lisp/eshell/esh-util.el
@@ -433,37 +433,21 @@ Prepend remote identification of `default-directory', if 
any."
 (defun eshell-printable-size (filesize &optional human-readable
                                       block-size use-colors)
   "Return a printable FILESIZE."
+  (when (and human-readable
+             (not (= human-readable 1000))
+             (not (= human-readable 1024)))
+    (error "human-readable must be 1000 or 1024"))
   (let ((size (float (or filesize 0))))
     (if human-readable
-       (if (< size human-readable)
-           (if (= (round size) 0)
-               "0"
-             (if block-size
-                 "1.0k"
-               (format "%.0f" size)))
-         (setq size (/ size human-readable))
-         (if (< size human-readable)
-             (if (<= size 9.94)
-                 (format "%.1fk" size)
-               (format "%.0fk" size))
-           (setq size (/ size human-readable))
-           (if (< size human-readable)
-               (let ((str (if (<= size 9.94)
-                              (format "%.1fM" size)
-                            (format "%.0fM" size))))
-                 (if use-colors
-                     (put-text-property 0 (length str)
-                                        'face 'bold str))
-                 str)
-             (setq size (/ size human-readable))
-             (if (< size human-readable)
-                 (let ((str (if (<= size 9.94)
-                                (format "%.1fG" size)
-                              (format "%.0fG" size))))
-                   (if use-colors
-                       (put-text-property 0 (length str)
-                                          'face 'bold-italic str))
-                   str)))))
+        (let* ((flavor (and (= human-readable 1000) 'si))
+               (str (file-size-human-readable size flavor)))
+          (if (not use-colors)
+              str
+            (cond ((> size (expt human-readable 3))
+                   (propertize str 'face 'bold-italic))
+                  ((> size (expt human-readable 2))
+                   (propertize str 'face 'bold))
+                  (t str))))
       (if block-size
          (setq size (/ size block-size)))
       (format "%.0f" size))))
@@ -492,16 +476,6 @@ list."
        (cadr flist)
       (cdr flist))))
 
-(defsubst eshell-redisplay ()
-  "Allow Emacs to redisplay buffers."
-  ;; for some strange reason, Emacs 21 is prone to trigger an
-  ;; "args out of range" error in `sit-for', if this function
-  ;; runs while point is in the minibuffer and the users attempt
-  ;; to use completion.  Don't ask me.
-  (condition-case nil
-      (sit-for 0)
-    (error nil)))
-
 (defun eshell-user-login-name ()
   "Return the connection-aware value of the user's login name.
 See also `user-login-name'."
@@ -628,8 +602,6 @@ See also `user-login-name'."
          (setq host-users (cdr host-users))
          (cdr (assoc user host-users))))))
 
-(autoload 'parse-time-string "parse-time")
-
 (eval-when-compile
   (require 'ange-ftp))         ; ange-ftp-parse-filename
 
@@ -813,6 +785,8 @@ If N or M is nil, it means the end of the list."
   (declare (obsolete seq-subseq "28.1"))
   (seq-subseq l n (1+ m)))
 
+(define-obsolete-function-alias 'eshell-redisplay #'redisplay "30.1")
+
 (provide 'esh-util)
 
 ;;; esh-util.el ends here
diff --git a/lisp/faces.el b/lisp/faces.el
index 44d64c743ba..8017f5fa65e 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -1340,10 +1340,11 @@ of a global face.  Value is the new attribute value."
                       (format "%s" old-value))))
             (setq new-value
                    (if (memq attribute '(:foreground :background))
-                       (let ((color
-                              (read-color
-                               (format-prompt "%s for face `%s'"
-                                              default attribute-name face))))
+                       (let* ((prompt (format-prompt
+                                       "%s for face `%s'"
+                                       default attribute-name face))
+                              (fg (eq attribute ':foreground))
+                              (color (read-color prompt nil nil nil fg face)))
                          (if (equal (string-trim color) "")
                              default
                            color))
@@ -1849,7 +1850,6 @@ If there is neither a user setting nor a default for 
FACE, return nil."
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; Frame-type independent color support.
-;;; We keep the old x-* names as aliases for back-compatibility.
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (defun defined-colors (&optional frame)
@@ -1861,7 +1861,6 @@ If FRAME is nil, that stands for the selected frame."
   (if (display-graphic-p frame)
       (xw-defined-colors frame)
     (mapcar 'car (tty-color-alist frame))))
-(defalias 'x-defined-colors 'defined-colors)
 
 (defun defined-colors-with-face-attributes (&optional frame foreground)
   "Return a list of colors supported for a particular FRAME.
@@ -1870,15 +1869,26 @@ to `defined-colors' the elements of the returned list 
are color
 strings with text properties, that make the color names render
 with the color they represent as background color (if FOREGROUND
 is nil; otherwise use the foreground color)."
-  (mapcar
-   (lambda (color-name)
-     (let ((color (copy-sequence color-name)))
-       (propertize color 'face
-                  (if foreground
-                      (list :foreground color)
-                    (list :foreground (readable-foreground-color color-name)
-                           :background color)))))
-   (defined-colors frame)))
+  (mapcar (lambda (color-name)
+            (faces--string-with-color color-name color-name foreground))
+          (defined-colors frame)))
+
+(defun faces--string-with-color (string color &optional foreground face)
+  "Return a copy of STRING with face attributes for COLOR.
+Set the :background or :foreground attribute to COLOR, depending
+on the argument FOREGROUND.
+
+The optional FACE argument determines the values of other face
+attributes."
+  (let* ((defaults (if face (list face) '()))
+         (colors (cond (foreground
+                        (list :foreground color))
+                       (face
+                        (list :background color))
+                       (t
+                        (list :foreground (readable-foreground-color color)
+                              :background color)))))
+    (propertize string 'face (cons colors defaults))))
 
 (defun readable-foreground-color (color)
   "Return a readable foreground color for background COLOR.
@@ -1934,7 +1944,6 @@ If FRAME is omitted or nil, use the selected frame."
     (if (display-graphic-p frame)
        (xw-color-defined-p color frame)
       (numberp (tty-color-translate color frame)))))
-(defalias 'x-color-defined-p 'color-defined-p)
 
 (declare-function xw-color-values "xfns.c" (color &optional frame))
 
@@ -1962,8 +1971,6 @@ return value is nil."
    (t
     (tty-color-values color frame))))
 
-(defalias 'x-color-values 'color-values)
-
 (declare-function xw-display-color-p "xfns.c" (&optional terminal))
 
 (defun display-color-p (&optional display)
@@ -1974,7 +1981,6 @@ If omitted or nil, that stands for the selected frame's 
display."
   (if (display-graphic-p display)
       (xw-display-color-p display)
     (tty-display-color-p display)))
-(defalias 'x-display-color-p 'display-color-p)
 
 (declare-function x-display-grayscale-p "xfns.c" (&optional terminal))
 
@@ -1987,7 +1993,7 @@ If omitted or nil, that stands for the selected frame's 
display."
     (> (tty-color-gray-shades display) 2)))
 
 (defun read-color (&optional prompt convert-to-RGB allow-empty-name msg
-                            foreground)
+                            foreground face)
   "Read a color name or RGB triplet.
 Completion is available for color names, but not for RGB triplets.
 
@@ -2016,17 +2022,25 @@ to enter an empty color name (the empty string).
 Interactively, or with optional arg MSG non-nil, print the
 resulting color name in the echo area.
 
-Interactively, displays a list of colored completions.  If optional
-argument FOREGROUND is non-nil, shows them as foregrounds, otherwise
-as backgrounds."
+Interactively, provides completion for selecting the color.  If
+the optional argument FOREGROUND is non-nil, shows the completion
+candidates with their foregound color changed to be the color of
+the candidate, otherwise changes the background color of the
+candidates.  The optional argument FACE determines the other
+face attributes of the candidates on display."
   (interactive "i\np\ni\np")    ; Always convert to RGB interactively.
   (let* ((completion-ignore-case t)
-        (colors (append '("foreground at point" "background at point")
-                        (if allow-empty-name '(""))
-                         (if (display-color-p)
-                             (defined-colors-with-face-attributes
-                               nil foreground)
-                           (defined-colors))))
+        (color-alist
+          `(("foreground at point" . ,(foreground-color-at-point))
+            ("background at point" . ,(background-color-at-point))
+            ,@(if allow-empty-name '(("" . unspecified)))
+            ,@(mapcar (lambda (c) (cons c c)) (defined-colors))))
+         (colors (mapcar (lambda (pair)
+                           (let* ((name (car pair))
+                                  (color (cdr pair)))
+                             (faces--string-with-color name color
+                                                       foreground face)))
+                         color-alist))
         (color (completing-read
                 (or prompt "Color (name or #RGB triplet): ")
                 ;; Completing function for reading colors, accepting
@@ -2926,7 +2940,7 @@ Note: Other faces cannot inherit from the cursor face."
     (((type haiku))
      :foreground "B_MENU_ITEM_TEXT_COLOR"
      :background "B_MENU_BACKGROUND_COLOR")
-    (((type x w32 ns pgtk) (class color))
+    (((type x w32 ns pgtk android) (class color))
      :background "grey75")
     (((type x) (class mono))
      :background "grey"))
@@ -3201,6 +3215,10 @@ also the same size as FACE on FRAME, or fail."
 
 (define-obsolete-function-alias 'face-background-pixmap #'face-stipple "29.1")
 (define-obsolete-function-alias 'set-face-background-pixmap #'set-face-stipple 
"29.1")
+(define-obsolete-function-alias 'x-defined-colors #'defined-colors "30.1")
+(define-obsolete-function-alias 'x-color-defined-p #'color-defined-p "30.1")
+(define-obsolete-function-alias 'x-color-values #'color-values "30.1")
+(define-obsolete-function-alias 'x-display-color-p #'display-color-p "30.1")
 
 (provide 'faces)
 
diff --git a/lisp/files.el b/lisp/files.el
index b7d47da1129..02db9996b33 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -5797,9 +5797,14 @@ Before and after saving the buffer, this function runs
                  (run-hook-with-args-until-success 'write-file-functions)
                  ;; If a hook returned t, file is already "written".
                  ;; Otherwise, write it the usual way now.
-                 (let ((dir (file-name-directory
+                 (let ((file (buffer-file-name))
+                        (dir (file-name-directory
                              (expand-file-name buffer-file-name))))
-                   (unless (file-exists-p dir)
+                    ;; Some systems have directories (like /content on
+                    ;; Android) in which files can exist without a
+                    ;; corresponding parent directory.
+                   (unless (or (file-exists-p file)
+                                (file-exists-p dir))
                      (if (y-or-n-p
                           (format-message
                             "Directory `%s' does not exist; create? " dir))
@@ -6354,6 +6359,26 @@ non-nil and if FN fails due to a missing file or 
directory."
       (apply fn args)
     (file-missing (or no-such (signal (car err) (cdr err))))))
 
+(defun delete-file (filename &optional trash)
+  "Delete file named FILENAME.  If it is a symlink, remove the symlink.
+If file has multiple names, it continues to exist with the other names.
+TRASH non-nil means to trash the file instead of deleting, provided
+`delete-by-moving-to-trash' is non-nil.
+
+When called interactively, TRASH is t if no prefix argument is given.
+With a prefix argument, TRASH is nil."
+  (interactive (list (read-file-name
+                      (if (and delete-by-moving-to-trash (null 
current-prefix-arg))
+                          "Move file to trash: " "Delete file: ")
+                      nil default-directory 
(confirm-nonexistent-file-or-buffer))
+                     (null current-prefix-arg)))
+  (if (and (file-directory-p filename) (not (file-symlink-p filename)))
+      (signal 'file-error (list "Removing old name: is a directory" filename)))
+  (let* ((handler (find-file-name-handler filename 'delete-file)))
+    (cond (handler (funcall handler 'delete-file filename trash))
+          ((and delete-by-moving-to-trash trash) (move-file-to-trash filename))
+          (t (delete-file-internal filename)))))
+
 (defun delete-directory (directory &optional recursive trash)
   "Delete the directory named DIRECTORY.  Does not follow symlinks.
 If RECURSIVE is non-nil, delete files in DIRECTORY as well, with
diff --git a/lisp/find-dired.el b/lisp/find-dired.el
index db2f5e7d026..48408d35499 100644
--- a/lisp/find-dired.el
+++ b/lisp/find-dired.el
@@ -247,8 +247,8 @@ it finishes, type \\[kill-find]."
     (erase-buffer)
     (setq default-directory dir)
     ;; Start the find process.
-    (shell-command (concat command "&") (current-buffer))
-    (let ((proc (get-buffer-process (current-buffer))))
+    (let ((proc (start-file-process-shell-command
+                 (buffer-name) (current-buffer) command)))
       ;; Initialize the process marker; it is used by the filter.
       (move-marker (process-mark proc) (point) (current-buffer))
       (set-process-filter proc #'find-dired-filter)
diff --git a/lisp/frame.el b/lisp/frame.el
index 39e8a4c88b8..ba5d1caafa2 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -1653,6 +1653,7 @@ live frame and defaults to the selected one."
 (declare-function ns-frame-geometry "nsfns.m" (&optional frame))
 (declare-function pgtk-frame-geometry "pgtkfns.c" (&optional frame))
 (declare-function haiku-frame-geometry "haikufns.c" (&optional frame))
+(declare-function android-frame-geometry "androidfns.c" (&optional frame))
 
 (defun frame-geometry (&optional frame)
   "Return geometric attributes of FRAME.
@@ -1706,6 +1707,8 @@ and width values are in pixels.
       (pgtk-frame-geometry frame))
      ((eq frame-type 'haiku)
       (haiku-frame-geometry frame))
+     ((eq frame-type 'android)
+      (android-frame-geometry frame))
      (t
       (list
        '(outer-position 0 . 0)
@@ -1832,6 +1835,7 @@ of frames like calls to map a frame or change its 
visibility."
 (declare-function ns-frame-edges "nsfns.m" (&optional frame type))
 (declare-function pgtk-frame-edges "pgtkfns.c" (&optional frame type))
 (declare-function haiku-frame-edges "haikufns.c" (&optional frame type))
+(declare-function android-frame-edges "androidfns.c" (&optional frame type))
 
 (defun frame-edges (&optional frame type)
   "Return coordinates of FRAME's edges.
@@ -1859,6 +1863,8 @@ FRAME."
       (pgtk-frame-edges frame type))
      ((eq frame-type 'haiku)
       (haiku-frame-edges frame type))
+     ((eq frame-type 'android)
+      (android-frame-edges frame type))
      (t
       (list 0 0 (frame-width frame) (frame-height frame))))))
 
@@ -1867,6 +1873,7 @@ FRAME."
 (declare-function ns-mouse-absolute-pixel-position "nsfns.m")
 (declare-function pgtk-mouse-absolute-pixel-position "pgtkfns.c")
 (declare-function haiku-mouse-absolute-pixel-position "haikufns.c")
+(declare-function android-mouse-absolute-pixel-position "androidfns.c")
 
 (defun mouse-absolute-pixel-position ()
   "Return absolute position of mouse cursor in pixels.
@@ -1885,6 +1892,8 @@ position (0, 0) of the selected frame's terminal."
       (pgtk-mouse-absolute-pixel-position))
      ((eq frame-type 'haiku)
       (haiku-mouse-absolute-pixel-position))
+     ((eq frame-type 'android)
+      (android-mouse-absolute-pixel-position))
      (t
       (cons 0 0)))))
 
@@ -1893,6 +1902,8 @@ position (0, 0) of the selected frame's terminal."
 (declare-function w32-set-mouse-absolute-pixel-position "w32fns.c" (x y))
 (declare-function x-set-mouse-absolute-pixel-position "xfns.c" (x y))
 (declare-function haiku-set-mouse-absolute-pixel-position "haikufns.c" (x y))
+(declare-function android-set-mouse-absolute-pixel-position
+                  "androidfns.c" (x y))
 
 (defun set-mouse-absolute-pixel-position (x y)
   "Move mouse pointer to absolute pixel position (X, Y).
@@ -1909,7 +1920,9 @@ position (0, 0) of the selected frame's terminal."
      ((eq frame-type 'w32)
       (w32-set-mouse-absolute-pixel-position x y))
      ((eq frame-type 'haiku)
-      (haiku-set-mouse-absolute-pixel-position x y)))))
+      (haiku-set-mouse-absolute-pixel-position x y))
+     ((eq frame-type 'android)
+      (android-set-mouse-absolute-pixel-position x y)))))
 
 (defun frame-monitor-attributes (&optional frame)
   "Return the attributes of the physical monitor dominating FRAME.
@@ -2005,6 +2018,7 @@ workarea attribute."
 ;; TODO: implement this on PGTK.
 ;; (declare-function pgtk-frame-list-z-order "pgtkfns.c" (&optional display))
 (declare-function haiku-frame-list-z-order "haikufns.c" (&optional display))
+(declare-function android-frame-list-z-order "androidfns.c" (&optional 
display))
 
 (defun frame-list-z-order (&optional display)
   "Return list of Emacs' frames, in Z (stacking) order.
@@ -2030,13 +2044,17 @@ Return nil if DISPLAY contains no Emacs frame."
       ;; (pgtk-frame-list-z-order display)
       nil)
      ((eq frame-type 'haiku)
-      (haiku-frame-list-z-order display)))))
+      (haiku-frame-list-z-order display))
+     ((eq frame-type 'android)
+      (android-frame-list-z-order display)))))
 
 (declare-function x-frame-restack "xfns.c" (frame1 frame2 &optional above))
 (declare-function w32-frame-restack "w32fns.c" (frame1 frame2 &optional above))
 (declare-function ns-frame-restack "nsfns.m" (frame1 frame2 &optional above))
 (declare-function pgtk-frame-restack "pgtkfns.c" (frame1 frame2 &optional 
above))
 (declare-function haiku-frame-restack "haikufns.c" (frame1 frame2 &optional 
above))
+(declare-function android-frame-restack "androidfns.c" (frame1 frame2
+                                                               &optional 
above))
 
 (defun frame-restack (frame1 frame2 &optional above)
   "Restack FRAME1 below FRAME2.
@@ -2070,7 +2088,9 @@ Some window managers may refuse to restack windows."
          ((eq frame-type 'haiku)
           (haiku-frame-restack frame1 frame2 above))
          ((eq frame-type 'pgtk)
-          (pgtk-frame-restack frame1 frame2 above))))
+          (pgtk-frame-restack frame1 frame2 above))
+         ((eq frame-type 'android)
+          (android-frame-restack frame1 frame2 above))))
     (error "Cannot restack frames")))
 
 (defun frame-size-changed-p (&optional frame)
@@ -2105,6 +2125,7 @@ for FRAME."
 ;; or in https://debbugs.gnu.org/cgi/bugreport.cgi?bug=35058#17.
 
 (declare-function msdos-mouse-p "dosfns.c")
+(declare-function android-detect-mouse "androidfns.c")
 
 (defun display-mouse-p (&optional display)
   "Return non-nil if DISPLAY has a mouse available.
@@ -2119,6 +2140,8 @@ frame's display)."
        (> w32-num-mouse-buttons 0)))
      ((memq frame-type '(x ns haiku pgtk))
       t)    ;; We assume X, NeXTstep, GTK, and Haiku *always* have a pointing 
device
+     ((eq frame-type 'android)
+      (android-detect-mouse))
      (t
       (or (and (featurep 'xt-mouse)
               xterm-mouse-mode)
@@ -2134,8 +2157,12 @@ frame's display)."
   "Return non-nil if popup menus are supported on DISPLAY.
 DISPLAY can be a display name, a frame, or nil (meaning the selected
 frame's display).
-Support for popup menus requires that the mouse be available."
-  (display-mouse-p display))
+Support for popup menus requires that a suitable pointing device
+be available."
+  ;; Android menus work fine with touch screens as well, and one must
+  ;; be present.
+  (or (eq (framep-on-display display) 'android)
+      (display-mouse-p display)))
 
 (defun display-graphic-p (&optional display)
   "Return non-nil if DISPLAY is a graphic display.
@@ -2144,7 +2171,8 @@ frames and several different fonts at once.  This is true 
for displays
 that use a window system such as X, and false for text-only terminals.
 DISPLAY can be a display name, a frame, or nil (meaning the selected
 frame's display)."
-  (not (null (memq (framep-on-display display) '(x w32 ns pgtk haiku)))))
+  (not (null (memq (framep-on-display display) '(x w32 ns pgtk haiku
+                                                   android)))))
 
 (defun display-images-p (&optional display)
   "Return non-nil if DISPLAY can display images.
@@ -2196,7 +2224,7 @@ frame's display)."
 This means that, for example, DISPLAY can differentiate between
 the keybinding RET and [return]."
   (let ((frame-type (framep-on-display display)))
-    (or (memq frame-type '(x w32 ns pc pgtk haiku))
+    (or (memq frame-type '(x w32 ns pc pgtk haiku android))
         ;; MS-DOS and MS-Windows terminals have built-in support for
         ;; function (symbol) keys
         (memq system-type '(ms-dos windows-nt)))))
@@ -2209,7 +2237,7 @@ DISPLAY should be either a frame or a display name (a 
string).
 If DISPLAY is omitted or nil, it defaults to the selected frame's display."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns haiku pgtk))
+     ((memq frame-type '(x w32 ns haiku pgtk android))
       (x-display-screens display))
      (t
       1))))
@@ -2229,7 +2257,7 @@ with DISPLAY.  To get information for each physical 
monitor, use
 `display-monitor-attributes-list'."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns haiku pgtk))
+     ((memq frame-type '(x w32 ns haiku pgtk android))
       (x-display-pixel-height display))
      (t
       (frame-height (if (framep display) display (selected-frame)))))))
@@ -2249,7 +2277,7 @@ with DISPLAY.  To get information for each physical 
monitor, use
 `display-monitor-attributes-list'."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns haiku pgtk))
+     ((memq frame-type '(x w32 ns haiku pgtk android))
       (x-display-pixel-width display))
      (t
       (frame-width (if (framep display) display (selected-frame)))))))
@@ -2287,7 +2315,7 @@ For graphical terminals, note that on \"multi-monitor\" 
setups this
 refers to the height in millimeters for all physical monitors
 associated with DISPLAY.  To get information for each physical
 monitor, use `display-monitor-attributes-list'."
-  (and (memq (framep-on-display display) '(x w32 ns haiku pgtk))
+  (and (memq (framep-on-display display) '(x w32 ns haiku pgtk android))
        (or (cddr (assoc (or display (frame-parameter nil 'display))
                        display-mm-dimensions-alist))
           (cddr (assoc t display-mm-dimensions-alist))
@@ -2308,7 +2336,7 @@ For graphical terminals, note that on \"multi-monitor\" 
setups this
 refers to the width in millimeters for all physical monitors
 associated with DISPLAY.  To get information for each physical
 monitor, use `display-monitor-attributes-list'."
-  (and (memq (framep-on-display display) '(x w32 ns haiku pgtk))
+  (and (memq (framep-on-display display) '(x w32 ns haiku pgtk android))
        (or (cadr (assoc (or display (frame-parameter nil 'display))
                        display-mm-dimensions-alist))
           (cadr (assoc t display-mm-dimensions-alist))
@@ -2326,7 +2354,7 @@ DISPLAY can be a display name or a frame.
 If DISPLAY is omitted or nil, it defaults to the selected frame's display."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns haiku pgtk))
+     ((memq frame-type '(x w32 ns haiku pgtk android))
       (x-display-backing-store display))
      (t
       'not-useful))))
@@ -2339,7 +2367,7 @@ DISPLAY can be a display name or a frame.
 If DISPLAY is omitted or nil, it defaults to the selected frame's display."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns haiku pgtk))
+     ((memq frame-type '(x w32 ns haiku pgtk android))
       (x-display-save-under display))
      (t
       'not-useful))))
@@ -2352,7 +2380,7 @@ DISPLAY can be a display name or a frame.
 If DISPLAY is omitted or nil, it defaults to the selected frame's display."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns haiku pgtk))
+     ((memq frame-type '(x w32 ns haiku pgtk android))
       (x-display-planes display))
      ((eq frame-type 'pc)
       4)
@@ -2367,7 +2395,7 @@ DISPLAY can be a display name or a frame.
 If DISPLAY is omitted or nil, it defaults to the selected frame's display."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns haiku pgtk))
+     ((memq frame-type '(x w32 ns haiku pgtk android))
       (x-display-color-cells display))
      ((eq frame-type 'pc)
       16)
@@ -2384,7 +2412,7 @@ DISPLAY can be a display name or a frame.
 If DISPLAY is omitted or nil, it defaults to the selected frame's display."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns haiku pgtk))
+     ((memq frame-type '(x w32 ns haiku pgtk android))
       (x-display-visual-class display))
      ((and (memq frame-type '(pc t))
           (tty-display-color-p display))
@@ -2402,6 +2430,8 @@ If DISPLAY is omitted or nil, it defaults to the selected 
frame's display."
                  (&optional terminal))
 (declare-function haiku-display-monitor-attributes-list "haikufns.c"
                  (&optional terminal))
+(declare-function android-display-monitor-attributes-list "androidfns.c"
+                  (&optional terminal))
 
 (defun display-monitor-attributes-list (&optional display)
   "Return a list of physical monitor attributes on DISPLAY.
@@ -2455,6 +2485,8 @@ monitors."
       (pgtk-display-monitor-attributes-list display))
      ((eq frame-type 'haiku)
       (haiku-display-monitor-attributes-list display))
+     ((eq frame-type 'android)
+      (android-display-monitor-attributes-list display))
      (t
       (let ((geometry (list 0 0 (display-pixel-width display)
                            (display-pixel-height display))))
@@ -2530,6 +2562,28 @@ symbols."
                'core-keyboard))))))
 
 
+;;;; On-screen keyboard management.
+
+(declare-function android-toggle-on-screen-keyboard "androidfns.c")
+
+(defun frame-toggle-on-screen-keyboard (frame hide)
+  "Display or hide the on-screen keyboard.
+On systems with an on-screen keyboard, display the on screen
+keyboard on behalf of the frame FRAME if HIDE is nil.  Else, hide
+the on screen keyboard.
+
+Return whether or not the on screen keyboard may have been
+displayed; that is, return t on systems with an on screen
+keyboard, and nil on those without.
+
+FRAME must already have the input focus for this to work
+ reliably."
+  (let ((frame-type (framep-on-display frame)))
+    (cond ((eq frame-type 'android)
+           (android-toggle-on-screen-keyboard frame hide) t)
+          (t nil))))
+
+
 ;;;; Frame geometry values
 
 (defun frame-geom-value-cons (type value &optional frame)
diff --git a/lisp/gnus/gmm-utils.el b/lisp/gnus/gmm-utils.el
index 2fc89681ba5..c0651e3dbfc 100644
--- a/lisp/gnus/gmm-utils.el
+++ b/lisp/gnus/gmm-utils.el
@@ -200,7 +200,8 @@ DEFAULT-MAP specifies the default key map for ICON-LIST."
   "Create function NAME.
 If FUNCTION exists, then NAME becomes an alias for FUNCTION.
 Otherwise, create function NAME with ARG-LIST and BODY."
-  (declare (indent defun))
+  (declare (obsolete nil "30.1")
+           (indent defun))
   (let ((defined-p (fboundp function)))
     (if defined-p
         `(defalias ',name ',function)
diff --git a/lisp/gnus/gnus-score.el b/lisp/gnus/gnus-score.el
index 05459ffae88..8bdfccf7eb8 100644
--- a/lisp/gnus/gnus-score.el
+++ b/lisp/gnus/gnus-score.el
@@ -517,6 +517,35 @@ of the last successful match.")
     "t" #'gnus-score-find-trace
     "w" #'gnus-score-find-favorite-words))
 
+
+
+;; Touch screen ``character reading'' routines for
+;; `gnus-summary-increase-score' and friends.
+
+(defun gnus-read-char (prompt options)
+  "Read a character from the keyboard.
+
+On Android, if `use-dialog-box-p' returns non-nil, display a
+dialog box containing PROMPT, with buttons representing each of
+item in the list of characters OPTIONS instead.
+
+Value is the character read, as with `read-char', or nil upon
+failure."
+  (if (and (display-graphic-p) (featurep 'android)
+           (use-dialog-box-p))
+      ;; Set up the dialog box.
+      (let ((dialog (cons prompt ; Message displayed in dialog box.
+                          (mapcar (lambda (arg)
+                                    (cons (char-to-string arg)
+                                          arg))
+                                  options))))
+        ;; Display the dialog box.
+        (x-popup-dialog t dialog))
+    ;; Fall back to read-char.
+    (read-char)))
+
+
+
 ;; Summary score file commands
 
 ;; Much modification of the kill (ahem, score) code and lots of the
@@ -588,21 +617,23 @@ current score file."
                     (aref (symbol-name gnus-score-default-type) 0)))
         (pchar (and gnus-score-default-duration
                     (aref (symbol-name gnus-score-default-duration) 0)))
-        entry temporary type match extra)
+        entry temporary type match extra header-string)
 
     (unwind-protect
        (progn
-
+          (setq header-string
+                (format "%s header (%s?): " (if increase "Increase" "Lower")
+                       (mapconcat (lambda (s) (char-to-string (car s)))
+                                  char-to-header "")))
          ;; First we read the header to score.
          (while (not hchar)
            (if mimic
                (progn
                  (sit-for 1)
                  (message "%c-" prefix))
-             (message "%s header (%s?): " (if increase "Increase" "Lower")
-                      (mapconcat (lambda (s) (char-to-string (car s)))
-                                 char-to-header "")))
-           (setq hchar (read-char))
+             (message header-string))
+           (setq hchar (gnus-read-char header-string
+                                        (mapcar #'car char-to-header)))
            (when (or (= hchar ??) (= hchar ?\C-h))
              (setq hchar nil)
              (gnus-score-insert-help "Match on header" char-to-header 1)))
@@ -625,17 +656,20 @@ current score file."
                                         (nth 3 s))
                                     s nil))
                               char-to-type))))
+            (setq header-string
+                  (format "%s header `%s' with match type (%s?): "
+                         (if increase "Increase" "Lower")
+                         (nth 1 entry)
+                         (mapconcat (lambda (s) (char-to-string (car s)))
+                                    legal-types "")))
            ;; We continue reading - the type.
            (while (not tchar)
              (if mimic
                  (progn
                    (sit-for 1) (message "%c %c-" prefix hchar))
-               (message "%s header `%s' with match type (%s?): "
-                        (if increase "Increase" "Lower")
-                        (nth 1 entry)
-                        (mapconcat (lambda (s) (char-to-string (car s)))
-                                   legal-types "")))
-             (setq tchar (read-char))
+               (message header-string))
+             (setq tchar (gnus-read-char header-string
+                                          (mapcar #'car legal-types)))
              (when (or (= tchar ??) (= tchar ?\C-h))
                (setq tchar nil)
                (gnus-score-insert-help "Match type" legal-types 2)))
@@ -651,15 +685,19 @@ current score file."
              (message ""))
            (setq pchar (or pchar ?t)))
 
+          (setq header-string
+                (format "%s permanence (%s?): " (if increase "Increase" 
"Lower")
+                       (mapconcat (lambda (s) (char-to-string (car s)))
+                                  char-to-perm "")))
+
          ;; We continue reading.
          (while (not pchar)
            (if mimic
                (progn
                  (sit-for 1) (message "%c %c %c-" prefix hchar tchar))
-             (message "%s permanence (%s?): " (if increase "Increase" "Lower")
-                      (mapconcat (lambda (s) (char-to-string (car s)))
-                                 char-to-perm "")))
-           (setq pchar (read-char))
+             (message header-string))
+           (setq pchar (gnus-read-char header-string
+                                        (mapcar #'car char-to-perm)))
            (when (or (= pchar ??) (= pchar ?\C-h))
              (setq pchar nil)
              (gnus-score-insert-help "Match permanence" char-to-perm 2)))
diff --git a/lisp/gnus/mail-source.el b/lisp/gnus/mail-source.el
index 582c598ac22..f870c0b8274 100644
--- a/lisp/gnus/mail-source.el
+++ b/lisp/gnus/mail-source.el
@@ -285,7 +285,7 @@ number."
   "Number of idle seconds to wait before checking for new mail."
   :type 'number)
 
-(defcustom mail-source-movemail-program "movemail"
+(defcustom mail-source-movemail-program movemail-program-name
   "If non-nil, name of program for fetching new mail."
   :version "26.2"
   :type '(choice (const nil) string))
diff --git a/lisp/help-fns.el b/lisp/help-fns.el
index 3dd5c790157..2d94182df33 100644
--- a/lisp/help-fns.el
+++ b/lisp/help-fns.el
@@ -229,6 +229,10 @@ interactive command."
                (lambda (f) (if want-command
                           (commandp f)
                         (or (fboundp f) (get f 'function-documentation))))
+               ;; We use 'confirm' here, unlike in other describe-*
+               ;; commands, for cases like a function that is advised
+               ;; but not yet defined (e.g., if 'advice-add' is called
+               ;; before defining the function).
                'confirm nil nil
                (and fn (symbol-name fn)))))
     (unless (equal val "")
diff --git a/lisp/help-macro.el b/lisp/help-macro.el
index 6fd0ca3bf98..fac3419f184 100644
--- a/lisp/help-macro.el
+++ b/lisp/help-macro.el
@@ -166,6 +166,7 @@ and then returns."
                      (error nil))
                    (let ((cursor-in-echo-area t)
                          (overriding-local-map local-map))
+                     (frame-toggle-on-screen-keyboard (selected-frame) nil)
                      (setq key (read-key-sequence
                                 (format "Type one of listed options%s: "
                                         (if (pos-visible-in-window-p
@@ -179,7 +180,13 @@ and then returns."
                                                    
(help--key-description-fontified (kbd "SPC"))
                                                    "/"
                                                    
(help--key-description-fontified (kbd "DEL"))
-                                                   " to scroll"))))
+                                                   " to scroll")))
+                                nil nil nil nil
+                                ;; Disable ``text conversion''.  OS
+                                ;; input methods might otherwise chose
+                                ;; to insert user input directly into
+                                ;; a buffer.
+                                t)
                            char (aref key 0)))
 
                    ;; If this is a scroll bar command, just run it.
diff --git a/lisp/hexl.el b/lisp/hexl.el
index bb57f4ac4c3..5fa09459a46 100644
--- a/lisp/hexl.el
+++ b/lisp/hexl.el
@@ -60,7 +60,7 @@
                  (const 64))
   :version "24.3")
 
-(defcustom hexl-program "hexl"
+(defcustom hexl-program hexl-program-name
   "The program that will hexlify and dehexlify its stdin.
 `hexl-program' will always be concatenated with `hexl-options'
 and \"-de\" when dehexlifying a buffer."
diff --git a/lisp/htmlfontify.el b/lisp/htmlfontify.el
index f0e38242e48..6c303226e54 100644
--- a/lisp/htmlfontify.el
+++ b/lisp/htmlfontify.el
@@ -308,13 +308,14 @@ done;")
   :tag   "etags-cmd-alist"
   :type  '(alist :key-type (string) :value-type (string)))
 
-(defcustom hfy-etags-bin "etags"
+(defcustom hfy-etags-bin etags-program-name
   "Location of etags binary (we begin by assuming it's in your path).
 
 Note that if etags is not in your path, you will need to alter the shell
 commands in `hfy-etags-cmd-alist'."
   :tag   "etags-bin"
-  :type  '(file))
+  :type  '(file)
+  :version "30.1")
 
 (defcustom hfy-shell-file-name "/bin/sh"
   "Shell (Bourne or compatible) to invoke for complex shell operations."
diff --git a/lisp/ibuf-ext.el b/lisp/ibuf-ext.el
index 550b5ed0e6a..37065f5d41a 100644
--- a/lisp/ibuf-ext.el
+++ b/lisp/ibuf-ext.el
@@ -143,10 +143,10 @@ Returns (OLD-FORMAT-DETECTED . 
UPDATED-SAVED-FILTERS-LIST)."
                                         (mode         . bibtex-mode)))
                                    ("web"
                                     (or (derived-mode . sgml-mode)
-                                        (derived-mode . css-mode)
-                                        (mode         . javascript-mode)
+                                        (derived-mode . css-base-mode)
+                                        (derived-mode . js-base-mode)
+                                        (derived-mode . 
typescript-ts-base-mode)
                                         (mode         . js2-mode)
-                                        (mode         . scss-mode)
                                         (derived-mode . haml-mode)
                                         (mode         . sass-mode)))
                                    ("gnus"
diff --git a/lisp/ielm.el b/lisp/ielm.el
index 01550de71b5..b45db3ec97e 100644
--- a/lisp/ielm.el
+++ b/lisp/ielm.el
@@ -612,7 +612,7 @@ Customized bindings may be defined in `ielm-map', which 
currently contains:
     ;; Was cat, but on non-Unix platforms that might not exist, so
     ;; use hexl instead, which is part of the Emacs distribution.
     (condition-case nil
-        (start-process "ielm" (current-buffer) "hexl")
+        (start-process "ielm" (current-buffer) hexl-program-name)
       (file-error (start-process "ielm" (current-buffer) "cat")))
     (set-process-query-on-exit-flag (ielm-process) nil)
     (goto-char (point-max))
diff --git a/lisp/image/image-dired-external.el 
b/lisp/image/image-dired-external.el
index 9f35e17a7e6..77352c25a3b 100644
--- a/lisp/image/image-dired-external.el
+++ b/lisp/image/image-dired-external.el
@@ -405,7 +405,8 @@ The new file will be named THUMBNAIL-FILE."
                 (not image-dired-rotate-original-ask-before-overwrite))
             (progn
               (copy-file image-dired-temp-rotate-image-file file t)
-              (image-dired-refresh-thumb))
+              (image-dired-refresh-thumb)
+              (image-dired-update-thumbnail-at-point))
           (image-dired-display-image file))))))
 
 
diff --git a/lisp/image/image-dired-util.el b/lisp/image/image-dired-util.el
index 3f6880fc807..53a5e274175 100644
--- a/lisp/image/image-dired-util.el
+++ b/lisp/image/image-dired-util.el
@@ -77,8 +77,9 @@ vary:
   added to the file name.
 
 - Otherwise `image-dired-thumbnail-storage' is used to set the
-  directory where to store the thumbnail.  In this latter case
-  the name given to the thumbnail depends on the value of
+  directory where to store the thumbnail.  In this latter case,
+  if `image-dired-thumbnail-storage' is set to `image-dired' the
+  file name given to the thumbnail depends on the value of
   `image-dired-thumb-naming'.
 
 See also `image-dired-thumbnail-storage' and
@@ -99,15 +100,14 @@ See also `image-dired-thumbnail-storage' and
       (let ((name (if (eq 'sha1-contents image-dired-thumb-naming)
                       (image-dired-contents-sha1 file)
                     ;; Defaults to SHA-1 of file name
-                    (if (eq 'per-directory image-dired-thumbnail-storage)
-                        (sha1 (file-name-nondirectory file))
-                      (sha1 file)))))
+                    (sha1 file))))
         (cond ((or (eq 'image-dired image-dired-thumbnail-storage)
                    ;; Maintained for backwards compatibility:
                    (eq 'use-image-dired-dir image-dired-thumbnail-storage))
                (expand-file-name (format "%s.jpg" name) (image-dired-dir)))
               ((eq 'per-directory image-dired-thumbnail-storage)
-               (expand-file-name (format "%s.jpg" name)
+               (expand-file-name (format "%s.thumb.jpg"
+                                         (file-name-nondirectory file))
                                  (expand-file-name
                                   ".image-dired"
                                   (file-name-directory file)))))))))
@@ -190,6 +190,23 @@ Should be used by commands in 
`image-dired-thumbnail-mode'."
   "Return non-nil if there is an `image-dired' thumbnail at point."
   (get-text-property (point) 'image-dired-thumbnail))
 
+(defun image-dired-update-thumbnail-at-point ()
+  "Update the thumbnail at point if the original image file has been modified.
+This function uncaches and removes the thumbnail file under the old name."
+  (when (image-dired-image-at-point-p)
+    (let* ((file (image-dired-original-file-name))
+           (thumb (expand-file-name (image-dired-thumb-name file)))
+           (image (get-text-property (point) 'display)))
+      (when image
+        (let ((old-thumb (plist-get (cdr image) :file)))
+          ;; When 'image-dired-thumb-naming' is set to
+          ;; 'sha1-contents', 'thumb' and 'old-thumb' could be
+          ;; different file names.  Update the thumbnail then.
+          (unless (string= thumb old-thumb)
+            (setf (plist-get (cdr image) :file) thumb)
+            (clear-image-cache old-thumb)
+            (delete-file old-thumb)))))))
+
 (defun image-dired-window-width-pixels (window)
   "Calculate WINDOW width in pixels."
   (declare (obsolete window-body-width "29.1"))
diff --git a/lisp/image/image-dired.el b/lisp/image/image-dired.el
index 96a0c2ef9bc..9a92cae8ad5 100644
--- a/lisp/image/image-dired.el
+++ b/lisp/image/image-dired.el
@@ -174,8 +174,8 @@ computing the SHA-1 of first 4KiB of the image contents (See
 In both case, a \"jpg\" extension is appended to save as JPEG.
 
 The value of this option is ignored if Image-Dired is customized
-to use the Thumbnail Managing Standard.  See
-`image-dired-thumbnail-storage'."
+to use the Thumbnail Managing Standard or the per-directory
+thumbnails setting.  See `image-dired-thumbnail-storage'."
   :type '(choice :tag "How to name thumbnail files"
                  (const :tag "SHA-1 of the image file name" sha1-filename)
                  (const :tag "SHA-1 of the image contents" sha1-contents))
@@ -208,8 +208,8 @@ thumbnails:
 
     Set this user option to `per-directory'.
 
-To control the naming of thumbnails for alternatives (2) and (3)
-above, customize the value of `image-dired-thumb-naming'.
+To control the naming of thumbnails for alternative (2) above,
+customize the value of `image-dired-thumb-naming'.
 
 To control the default size of thumbnails for alternatives (2)
 and (3) above, customize the value of `image-dired-thumb-size'.
@@ -590,7 +590,7 @@ used or not.  If non-nil, use `display-buffer' instead of
 `image-dired-previous-line-and-display' where we do not want the
 thumbnail buffer to be selected."
   (interactive "P" nil dired-mode)
-  (setq image-dired--generate-thumbs-start  (current-time))
+  (setq image-dired--generate-thumbs-start (current-time))
   (let ((buf (image-dired-create-thumbnail-buffer))
         files dired-buf)
     (if arg
@@ -1905,8 +1905,8 @@ when using per-directory thumbnail file storage"))
     (if (file-exists-p image-dired-gallery-dir)
         (if (not (file-directory-p image-dired-gallery-dir))
             (error "Variable image-dired-gallery-dir is not a directory"))
-      ;; FIXME: Should we set umask to 077 here, as we do for thumbnails?
-      (make-directory image-dired-gallery-dir))
+      (with-file-modes #o700
+        (make-directory image-dired-gallery-dir)))
     ;; Open index file
     (with-temp-file index-file
       (if (file-exists-p index-file)
diff --git a/lisp/image/wallpaper.el b/lisp/image/wallpaper.el
index a2f175e4628..c778264d3ef 100644
--- a/lisp/image/wallpaper.el
+++ b/lisp/image/wallpaper.el
@@ -432,6 +432,8 @@ See also `wallpaper-default-width'.")
 
 ;;; wallpaper-set
 
+(declare-function x-open-connection "xfns.c")
+
 (defun wallpaper--x-monitor-name ()
   "Get the monitor name for `wallpaper-set'.
 On a graphical display, try using the same monitor as the current
diff --git a/lisp/international/characters.el b/lisp/international/characters.el
index 9aea5e27063..1aa570ca59a 100644
--- a/lisp/international/characters.el
+++ b/lisp/international/characters.el
@@ -1394,6 +1394,174 @@ with L, LRE, or LRO Unicode bidi character type.")
   (dolist (elt l)
     (set-char-table-range char-width-table elt 2)))
 
+;; A: East Asian "Ambiguous" characters.
+(let ((l '((#x00A1 . #x00A1)
+          (#x00A4 . #x00A4)
+          (#x00A7 . #x00A8)
+          (#x00AA . #x00AA)
+          (#x00AD . #x00AE)
+          (#x00B0 . #x00B4)
+          (#x00B6 . #x00BA)
+          (#x00BC . #x00BF)
+          (#x00C6 . #x00C6)
+          (#x00D0 . #x00D0)
+          (#x00D7 . #x00D8)
+          (#x00E0 . #x00E1)
+          (#x00E6 . #x00E6)
+          (#x00E8 . #x00EA)
+          (#x00EC . #x00ED)
+          (#x00F0 . #x00F0)
+          (#x00F2 . #x00F3)
+          (#x00F7 . #x00FA)
+          (#x00FC . #x00FC)
+          (#x00FE . #x00FE)
+          (#x0101 . #x0101)
+          (#x0111 . #x0111)
+          (#x0113 . #x0113)
+          (#x011B . #x011B)
+          (#x0126 . #x0127)
+          (#x012B . #x012B)
+          (#x0131 . #x0133)
+          (#x0138 . #x0138)
+          (#x013F . #x0142)
+          (#x0144 . #x0144)
+          (#x0148 . #x014B)
+          (#x014D . #x014D)
+          (#x0152 . #x0153)
+          (#x0166 . #x0167)
+          (#x016B . #x016B)
+          (#x01CE . #x01CE)
+          (#x01D0 . #x01D0)
+          (#x01D2 . #x01D2)
+          (#x01D4 . #x01D4)
+          (#x01D6 . #x01D6)
+          (#x01D8 . #x01D8)
+          (#x01DA . #x01DA)
+          (#x01DC . #x01DC)
+          (#x0251 . #x0251)
+          (#x0261 . #x0261)
+          (#x02C4 . #x02C4)
+          (#x02C7 . #x02C7)
+          (#x02C9 . #x02CB)
+          (#x02CD . #x02CD)
+          (#x02D0 . #x02D0)
+          (#x02D8 . #x02DB)
+          (#x02DD . #x02DD)
+          (#x02DF . #x02DF)
+          (#x0300 . #x036F)
+          (#x0391 . #x03A1)
+          (#x03A3 . #x03A9)
+          (#x03B1 . #x03C1)
+          (#x03C3 . #x03C9)
+          (#x0401 . #x0401)
+          (#x0410 . #x044F)
+          (#x0451 . #x0451)
+          (#x2010 . #x2010)
+          (#x2013 . #x2016)
+          (#x2018 . #x2019)
+          (#x201C . #x201D)
+          (#x2020 . #x2022)
+          (#x2024 . #x2027)
+          (#x2030 . #x2030)
+          (#x2032 . #x2033)
+          (#x2035 . #x2035)
+          (#x203E . #x203E)
+          (#x2074 . #x2074)
+          (#x207F . #x207F)
+          (#x2081 . #x2084)
+          (#x20AC . #x20AC)
+          (#x2103 . #x2103)
+          (#x2105 . #x2105)
+          (#x2109 . #x2109)
+          (#x2113 . #x2113)
+          (#x2116 . #x2116)
+          (#x2121 . #x2122)
+          (#x2126 . #x2126)
+          (#x212B . #x212B)
+          (#x2153 . #x2154)
+          (#x215B . #x215E)
+          (#x2160 . #x216B)
+          (#x2170 . #x2179)
+          (#x2189 . #x2189)
+          (#x2190 . #x2199)
+          (#x21B8 . #x21B9)
+          (#x21D2 . #x21D2)
+          (#x21D4 . #x21D4)
+          (#x21E7 . #x21E7)
+          (#x2200 . #x2200)
+          (#x2202 . #x2203)
+          (#x2207 . #x2208)
+          (#x220B . #x220B)
+          (#x220F . #x220F)
+          (#x2211 . #x2211)
+          (#x2215 . #x2215)
+          (#x221A . #x221A)
+          (#x221D . #x2220)
+          (#x2223 . #x2223)
+          (#x2225 . #x2225)
+          (#x2227 . #x222C)
+          (#x222E . #x222E)
+          (#x2234 . #x2237)
+          (#x223C . #x223D)
+          (#x2248 . #x2248)
+          (#x224C . #x224C)
+          (#x2252 . #x2252)
+          (#x2260 . #x2261)
+          (#x2264 . #x2267)
+          (#x226A . #x226B)
+          (#x226E . #x226F)
+          (#x2282 . #x2283)
+          (#x2286 . #x2287)
+          (#x2295 . #x2295)
+          (#x2299 . #x2299)
+          (#x22A5 . #x22A5)
+          (#x22BF . #x22BF)
+          (#x2312 . #x2312)
+          (#x2460 . #x24E9)
+          (#x24EB . #x254B)
+          (#x2550 . #x2573)
+          (#x2580 . #x258F)
+          (#x2592 . #x2595)
+          (#x25A0 . #x25A1)
+          (#x25A3 . #x25A9)
+          (#x25B2 . #x25B3)
+          (#x25B6 . #x25B7)
+          (#x25BC . #x25BD)
+          (#x25C0 . #x25C1)
+          (#x25C6 . #x25C8)
+          (#x25CE . #x25D1)
+          (#x25E2 . #x25E5)
+          (#x25EF . #x25EF)
+          (#x2605 . #x2606)
+          (#x260E . #x260F)
+          (#x261C . #x261C)
+          (#x261E . #x261E)
+          (#x2640 . #x2640)
+          (#x2642 . #x2642)
+          (#x2660 . #x2661)
+          (#x2663 . #x2665)
+          (#x2667 . #x266A)
+          (#x266C . #x266D)
+          (#x266F . #x266F)
+          (#x269E . #x269F)
+          (#x26BF . #x26BF)
+          (#x26C6 . #x26CD)
+          (#x26CF . #x26D3)
+          (#x26D5 . #x26E1)
+          (#x26E3 . #x26E3)
+          (#x26E8 . #x26E9)
+          (#x26EB . #x26F1)
+          (#x26F4 . #x26F4)
+          (#x26F6 . #x26F9)
+          (#x26FB . #x26FC)
+          (#x26FE . #x26FF)
+          (#x273D . #x273D)
+          (#x2776 . #x277F)
+          (#x2B56 . #x2B59)
+          (#x3248 . #x324F))))
+  (dolist (elt l)
+    (set-char-table-range ambiguous-width-chars elt t)))
+
 ;; Other double width
 ;;(map-charset-chars
 ;; (lambda (range ignore) (set-char-table-range char-width-table range 2))
@@ -1427,6 +1595,45 @@ with L, LRE, or LRO Unicode bidi character type.")
               (chinese-cns11643-1 (#x2121 . #x427E)))
     (ko_KR nil (korean-ksc5601 (#x2121 . #x2C7E)))))
 
+(defun update-cjk-ambiguous-char-widths (locale-name)
+  "Update character widths for LOCALE-NAME using `ambiguous-width-chars'.
+LOCALE-NAME is the symbol of a CJK locale, such as \\='zh_CN."
+  (let ((slot (assq locale-name cjk-char-width-table-list)))
+    (or slot (error "Unknown locale for CJK language environment: %s"
+                   locale-name))
+    ;; Force recomputation of child table in 'use-cjk-char-width-table'.
+    (setcar (cdr slot) nil)
+    (use-cjk-char-width-table locale-name)))
+
+
+(defcustom cjk-ambiguous-chars-are-wide t
+  "Whether the \"ambiguous-width\" characters take 2 columns on display.
+
+Some of the characters are defined by Unicode as being of \"ambiguous\"
+width: the actual width, either 1 column or 2 columns, should be
+determined at display time, depending on the language context.
+If this variable is non-nil, Emacs will consider these characters as
+full-width, i.e. taking 2 columns; otherwise they are narrow characters
+taking 1 column on display.  Which value is correct depends on the
+fonts being used.  In some CJK locales the fonts are set so that
+these characters are displayed as full-width.  This setting is most
+important for text-mode frames, because there Emacs cannot access the
+metrics of the fonts used by the console or the terminal emulator.
+
+Do not set this directly via `setq'; instead, use `setopt' or the
+Customize commands.  Alternatively, call `update-cjk-ambiguous-char-widths'
+passing it the symbol of the current locale environment, after changing
+the value of the variable with `setq'."
+  :type 'boolean
+  :set (lambda (symbol value)
+         (set-default symbol value)
+         (let ((locsym (get-language-info current-language-environment
+                                          'cjk-locale-symbol)))
+           (when locsym
+             (update-cjk-ambiguous-char-widths locsym))))
+  :version "30.1"
+  :group 'display)
+
 ;; Internal use only.
 ;; Setup char-width-table appropriate for a language environment
 ;; corresponding to LOCALE-NAME (symbol).
@@ -1448,7 +1655,15 @@ with L, LRE, or LRO Unicode bidi character type.")
                                 (car code-range) (cdr code-range)))))
        (optimize-char-table table)
        (set-char-table-parent table char-width-table)
-       (setcar (cdr slot) table)))
+        (let ((tbl (make-char-table nil)))
+          (map-char-table
+           (lambda (range _val)
+             (set-char-table-range tbl range
+                                   (if cjk-ambiguous-chars-are-wide 2 1)))
+           ambiguous-width-chars)
+          (optimize-char-table tbl)
+          (set-char-table-parent tbl table)
+         (setcar (cdr slot) tbl))))
     (setq char-width-table (nth 1 slot))))
 
 (defun use-default-char-width-table ()
diff --git a/lisp/international/fontset.el b/lisp/international/fontset.el
index b72c68d9d59..bbb1993ba3c 100644
--- a/lisp/international/fontset.el
+++ b/lisp/international/fontset.el
@@ -200,7 +200,10 @@
        (symbol . [#x201C #x2200 #x2500])
        (braille #x2800)
        (ideographic-description #x2FF0)
-       (cjk-misc #x300E)
+        ;; Noto Sans Phags Pa is broken and reuses the CJK misc code
+        ;; points for some of its own characters.  Add one actual CJK
+        ;; character to prevent finding such broken fonts.
+       (cjk-misc #x300E #xff0c #x300a #xff09 #x5b50)
        (kana #x304B)
        (bopomofo #x3105)
        (kanbun #x319D)
@@ -685,7 +688,11 @@
          (nil . "JISX0213.2000-2")
          (nil . "JISX0213.2004-1")
          ,(font-spec :registry "iso10646-1" :lang 'ja)
-         ,(font-spec :registry "iso10646-1" :lang 'zh))
+         ,(font-spec :registry "iso10646-1" :lang 'zh)
+          ;; This is required, as otherwise many TrueType fonts with
+          ;; CJK characters but no corresponding ``design language''
+          ;; declaration can't be found.
+          ,(font-spec :registry "iso10646-1" :script 'han))
 
      (cjk-misc (nil . "GB2312.1980-0")
               (nil . "JISX0208*")
@@ -704,7 +711,11 @@
               (nil . "JISX0213.2000-1")
               (nil . "JISX0213.2000-2")
               ,(font-spec :registry "iso10646-1" :lang 'ja)
-              ,(font-spec :registry "iso10646-1" :lang 'zh))
+              ,(font-spec :registry "iso10646-1" :lang 'zh)
+               ;; This is required, as otherwise many TrueType fonts
+               ;; with CJK characters but no corresponding ``design
+               ;; language'' declaration can't be found.
+               ,(font-spec :registry "iso10646-1" :script 'cjk-misc))
 
      (hangul (nil . "KSC5601.1987-0")
             ,(font-spec :registry "iso10646-1" :lang 'ko))
diff --git a/lisp/international/mule-cmds.el b/lisp/international/mule-cmds.el
index 3d6d66970d3..e3811d81ec2 100644
--- a/lisp/international/mule-cmds.el
+++ b/lisp/international/mule-cmds.el
@@ -88,7 +88,8 @@
     (bindings--define-key map [separator-3] menu-bar-separator)
     (bindings--define-key map [set-terminal-coding-system]
       '(menu-item "For Terminal" set-terminal-coding-system
-        :enable (null (memq initial-window-system '(x w32 ns haiku pgtk)))
+        :enable (null (memq initial-window-system '(x w32 ns haiku pgtk
+                                                   android)))
         :help "How to encode terminal output"))
     (bindings--define-key map [set-keyboard-coding-system]
       '(menu-item "For Keyboard" set-keyboard-coding-system
diff --git a/lisp/isearch.el b/lisp/isearch.el
index 3d2bbda4975..daf884d3d53 100644
--- a/lisp/isearch.el
+++ b/lisp/isearch.el
@@ -244,6 +244,10 @@ If you use `add-function' to modify this variable, you can 
use the
 `isearch-message-prefix' advice property to specify the prefix string
 displayed in the search message.")
 
+(defvar isearch-text-conversion-style nil
+  "Value of `text-conversion-style' before Isearch mode
+was enabled in this buffer.")
+
 ;; Search ring.
 
 (defvar search-ring nil
@@ -1221,6 +1225,8 @@ active region is added to the search string."
 ;;                          isearch-forward-regexp isearch-backward-regexp)
 ;;  "List of commands for which isearch-mode does not recursive-edit.")
 
+(declare-function set-text-conversion-style "textconv.c")
+
 (defun isearch-mode (forward &optional regexp op-fun recursive-edit 
regexp-function)
   "Start Isearch minor mode.
 It is called by the function `isearch-forward' and other related functions.
@@ -1237,6 +1243,8 @@ does not return to the calling function until the search 
is completed.
 To behave this way it enters a recursive edit and exits it when done
 isearching.
 
+Also display the on-screen keyboard if necessary.
+
 The arg REGEXP-FUNCTION, if non-nil, should be a function.  It is
 used to set the value of `isearch-regexp-function'."
 
@@ -1332,6 +1340,21 @@ used to set the value of `isearch-regexp-function'."
   (add-hook 'mouse-leave-buffer-hook 'isearch-mouse-leave-buffer)
   (add-hook 'kbd-macro-termination-hook 'isearch-done)
 
+  ;; If the keyboard is not up and the last event did not come from
+  ;; a keyboard, bring it up so that the user can type.
+  (when (or (not last-event-frame)
+            (not (eq (device-class last-event-frame
+                                   last-event-device)
+                     'keyboard)))
+    (frame-toggle-on-screen-keyboard (selected-frame) nil))
+
+  ;; Disable text conversion so that isearch can behave correctly.
+
+  (when (fboundp 'set-text-conversion-style)
+    (setq isearch-text-conversion-style
+          text-conversion-style)
+    (set-text-conversion-style nil))
+
   ;; isearch-mode can be made modal (in the sense of not returning to
   ;; the calling function until searching is completed) by entering
   ;; a recursive-edit and exiting it when done isearching.
@@ -1465,6 +1488,10 @@ NOPUSH is t and EDIT is t."
         (setq isearch-tool-bar-old-map nil))
     (kill-local-variable 'tool-bar-map))
 
+  ;; Restore the previous text conversion style.
+  (when (fboundp 'set-text-conversion-style)
+    (set-text-conversion-style isearch-text-conversion-style))
+
   (force-mode-line-update)
 
   ;; If we ended in the middle of some intangible text,
diff --git a/lisp/language/chinese.el b/lisp/language/chinese.el
index 26f1194aa4c..e35f3f179ad 100644
--- a/lisp/language/chinese.el
+++ b/lisp/language/chinese.el
@@ -111,6 +111,7 @@
 (set-language-info-alist
  "Chinese-GB" '((charset chinese-gb2312 chinese-sisheng)
                (iso639-language . zh)
+                (cjk-locale-symbol . zh_CN)
                (setup-function . (lambda ()
                                    (use-cjk-char-width-table 'zh_CN)))
                (exit-function . use-default-char-width-table)
@@ -142,6 +143,7 @@
 (set-language-info-alist
  "Chinese-BIG5" '((charset chinese-big5-1 chinese-big5-2)
                  (iso639-language . zh)
+                  (cjk-locale-symbol . zh_HK)
                  (setup-function . (lambda ()
                                      (use-cjk-char-width-table 'zh_HK)))
                  (exit-function . use-default-char-width-table)
@@ -198,6 +200,7 @@
                          chinese-cns11643-5 chinese-cns11643-6
                          chinese-cns11643-7)
                 (iso639-language . zh)
+                 (cjk-locale-symbol . zh_TW)
                 (setup-function . (lambda ()
                                     (use-cjk-char-width-table 'zh_TW)))
                 (exit-function . use-default-char-width-table)
@@ -218,6 +221,7 @@ accepts Big5 for input also (which is then converted to 
CNS)."))
                             chinese-cns11643-5 chinese-cns11643-6
                             chinese-cns11643-7 chinese-big5-1 chinese-big5-2)
                    (iso639-language . zh)
+                    (cjk-locale-symbol . zh_TW)
                    (setup-function . (lambda ()
                                        (use-cjk-char-width-table 'zh_TW)))
                    (exit-function . use-default-char-width-table)
@@ -248,6 +252,7 @@ converted to CNS)."))
 (set-language-info-alist
  "Chinese-GBK" '((charset chinese-gbk)
                 (iso639-language . zh)
+                 (cjk-locale-symbol . zh_CN)
                 (setup-function . (lambda ()
                                     (use-cjk-char-width-table 'zh_CN)))
                 (exit-function . use-default-char-width-table)
diff --git a/lisp/language/japanese.el b/lisp/language/japanese.el
index 681dc9d7b92..6042ebf4511 100644
--- a/lisp/language/japanese.el
+++ b/lisp/language/japanese.el
@@ -208,6 +208,7 @@ eucJP-ms is defined in 
<http://www.opengroup.or.jp/jvc/cde/appendix.html>."
  "Japanese" '((setup-function . setup-japanese-environment-internal)
              (exit-function . use-default-char-width-table)
              (iso639-language . ja)
+              (cjk-locale-symbol . ja_JP)
              (tutorial . "TUTORIAL.ja")
              (charset japanese-jisx0208
                       japanese-jisx0212 latin-jisx0201 katakana-jisx0201
diff --git a/lisp/language/korean.el b/lisp/language/korean.el
index fef5796bc4b..ede37d5d07c 100644
--- a/lisp/language/korean.el
+++ b/lisp/language/korean.el
@@ -68,6 +68,7 @@
 (set-language-info-alist
  "Korean" '((setup-function . setup-korean-environment-internal)
            (exit-function . exit-korean-environment)
+            (cjk-locale-symbol . ko_KR)
            (iso639-language . ko)
            (tutorial . "TUTORIAL.ko")
            (charset korean-ksc5601 cp949)
diff --git a/lisp/loadup.el b/lisp/loadup.el
index 5b387de3f3e..8679fba68f9 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -258,6 +258,9 @@
 (load "jit-lock")
 
 (load "mouse")
+;; This loading happens on Android despite scroll bars being
+;; unsupported, because scroll-bar-mode (the variable) must be
+;; defined.
 (if (boundp 'x-toolkit-scroll-bars)
     (load "scroll-bar"))
 (load "select")
@@ -295,6 +298,10 @@
 (if (featurep 'dynamic-setting)
     (load "dynamic-setting"))
 
+;; touch-screen.el is tiny and is used liberally throughout the button
+;; code etc, so it may as well be preloaded everywhere.
+(load "touch-screen")
+
 (if (featurep 'x)
     (progn
       (load "x-dnd")
@@ -306,6 +313,12 @@
       (load "term/common-win")
       (load "term/haiku-win")))
 
+(if (featurep 'android)
+    (progn
+      (load "ls-lisp")
+      (load "term/common-win")
+      (load "term/android-win")))
+
 (if (or (eq system-type 'windows-nt)
         (featurep 'w32))
     (progn
@@ -429,6 +442,13 @@ lost after dumping")))
       (defconst emacs-build-number
        (if versions (1+ (apply #'max versions)) 1))))
 
+;; Just set the repository branch during initial dumping on Android.
+(if (and (eq system-type 'android)
+         (not (pdumper-stats)))
+    (setq emacs-repository-version
+          (ignore-errors (emacs-repository-get-version))
+          emacs-repository-branch
+          (ignore-errors (emacs-repository-get-branch))))
 
 (message "Finding pointers to doc strings...")
 (if (and (or (and (fboundp 'dump-emacs)
@@ -546,66 +566,97 @@ lost after dumping")))
 
 
 
-(if dump-mode
-    (let ((output (cond ((equal dump-mode "pdump") "emacs.pdmp")
-                        ((equal dump-mode "dump") "emacs")
-                        ((equal dump-mode "bootstrap") "emacs")
-                        ((equal dump-mode "pbootstrap") "bootstrap-emacs.pdmp")
-                        (t (error "Unrecognized dump mode %s" dump-mode)))))
-      (when (and (featurep 'native-compile)
-                 (equal dump-mode "pdump"))
-        ;; Don't enable this before bootstrap is completed, as the
-        ;; compiler infrastructure may not be usable yet.
-        (setq native-comp-enable-subr-trampolines t))
-      (message "Dumping under the name %s" output)
-      (condition-case ()
-          (delete-file output)
-        (file-error nil))
-      ;; On MS-Windows, the current directory is not necessarily the
-      ;; same as invocation-directory.
-      (let (success)
-        (unwind-protect
-             (let ((tmp-dump-mode dump-mode)
-                   (dump-mode nil)
-                   (lexical-binding nil))
-               (if (member tmp-dump-mode '("pdump" "pbootstrap"))
-                   (dump-emacs-portable (expand-file-name output 
invocation-directory))
-                 (dump-emacs output (if (eq system-type 'ms-dos)
-                                        "temacs.exe"
-                                      "temacs"))
-                 (message "%d pure bytes used" pure-bytes-used))
-               (setq success t))
-          (unless success
-            (ignore-errors
-              (delete-file output)))))
-      ;; Recompute NAME now, so that it isn't set when we dump.
-      (if (not (or (eq system-type 'ms-dos)
-                   (eq system-type 'haiku) ;; BFS doesn't support hard links
-                   ;; Don't bother adding another name if we're just
-                   ;; building bootstrap-emacs.
-                   (member dump-mode '("pbootstrap" "bootstrap"))))
-          (let ((name (format "emacs-%s.%d" emacs-version emacs-build-number))
-                (exe (if (eq system-type 'windows-nt) ".exe" "")))
-            (while (string-match "[^-+_.a-zA-Z0-9]+" name)
-              (setq name (concat (downcase (substring name 0 (match-beginning 
0)))
-                                 "-"
-                                 (substring name (match-end 0)))))
-            (message "Adding name %s" (concat name exe))
-            ;; When this runs on Windows, invocation-directory is not
-            ;; necessarily the current directory.
-            (add-name-to-file (expand-file-name (concat "emacs" exe)
-                                                invocation-directory)
-                              (expand-file-name (concat name exe)
-                                                invocation-directory)
-                              t)
-            (when (equal dump-mode "pdump")
-              (message "Adding name %s" (concat name ".pdmp"))
-              (add-name-to-file (expand-file-name "emacs.pdmp"
+(if (eq system-type 'android)
+    (progn
+      ;; Dumping Emacs on Android works slightly differently from
+      ;; everywhere else.  The first time Emacs starts, Emacs dumps
+      ;; itself to "emacs-%s.pdump", and then proceeds with loadup,
+      ;; where %s is replaced by the dump fingerprint.
+      ;; EmacsApplication.java removes any pdump files with a
+      ;; different build fingerprint upon being created, which happens
+      ;; the moment the Android system starts Emacs.  Then, it passes
+      ;; the appropriate "--dump-file" to libemacs.so as it starts.
+      (when (not noninteractive)
+        (let ((temp-dir (getenv "TEMP"))
+              (dump-file-name (format "%semacs-%s.pdmp"
+                                      (file-name-as-directory "~")
+                                      pdumper-fingerprint))
+              (dump-temp-file-name (format "%s~emacs-%s.pdmp"
+                                           (file-name-as-directory "~")
+                                           pdumper-fingerprint)))
+          (unless (pdumper-stats)
+            (condition-case ()
+                (progn
+                  (dump-emacs-portable dump-temp-file-name)
+                  ;; Move the dumped file to the actual dump file name.
+                  (rename-file dump-temp-file-name dump-file-name)
+                  ;; Continue with loadup.
+                  nil)
+              (error nil))))))
+  (if dump-mode
+      (let ((output (cond ((equal dump-mode "pdump") "emacs.pdmp")
+                          ((equal dump-mode "dump") "emacs")
+                          ((equal dump-mode "bootstrap") "emacs")
+                          ((equal dump-mode "pbootstrap") 
"bootstrap-emacs.pdmp")
+                          (t (error "Unrecognized dump mode %s" dump-mode)))))
+        (when (and (featurep 'native-compile)
+                   (equal dump-mode "pdump"))
+          ;; Don't enable this before bootstrap is completed, as the
+          ;; compiler infrastructure may not be usable yet.
+          (setq comp-enable-subr-trampolines t))
+        (message "Dumping under the name %s" output)
+        (condition-case ()
+            (delete-file output)
+          (file-error nil))
+        ;; On MS-Windows, the current directory is not necessarily the
+        ;; same as invocation-directory.
+        (let (success)
+          (unwind-protect
+              (let ((tmp-dump-mode dump-mode)
+                    (dump-mode nil)
+                    (lexical-binding nil))
+                (if (member tmp-dump-mode '("pdump" "pbootstrap"))
+                    (dump-emacs-portable (expand-file-name output 
invocation-directory))
+                  (dump-emacs output (if (eq system-type 'ms-dos)
+                                         "temacs.exe"
+                                       "temacs"))
+                  (message "%d pure bytes used" pure-bytes-used))
+                (setq success t))
+            (unless success
+              (ignore-errors
+                (delete-file output)))))
+        ;; Recompute NAME now, so that it isn't set when we dump.
+        (if (not (or (eq system-type 'ms-dos)
+                     (eq system-type 'haiku) ;; BFS doesn't support hard links
+                     ;; There's no point keeping old dumps around for
+                     ;; the binary used to build Lisp on the build
+                     ;; machine.
+                     (featurep 'android)
+                     ;; Don't bother adding another name if we're just
+                     ;; building bootstrap-emacs.
+                     (member dump-mode '("pbootstrap" "bootstrap"))))
+            (let ((name (format "emacs-%s.%d" emacs-version 
emacs-build-number))
+                  (exe (if (eq system-type 'windows-nt) ".exe" "")))
+              (while (string-match "[^-+_.a-zA-Z0-9]+" name)
+                (setq name (concat (downcase (substring name 0 
(match-beginning 0)))
+                                   "-"
+                                   (substring name (match-end 0)))))
+              (message "Adding name %s" (concat name exe))
+              ;; When this runs on Windows, invocation-directory is not
+              ;; necessarily the current directory.
+              (add-name-to-file (expand-file-name (concat "emacs" exe)
                                                   invocation-directory)
-                                (expand-file-name (concat name ".pdmp")
+                                (expand-file-name (concat name exe)
                                                   invocation-directory)
-                                t))))
-      (kill-emacs)))
+                                t)
+              (when (equal dump-mode "pdump")
+                (message "Adding name %s" (concat name ".pdmp"))
+                (add-name-to-file (expand-file-name "emacs.pdmp"
+                                                    invocation-directory)
+                                  (expand-file-name (concat name ".pdmp")
+                                                    invocation-directory)
+                                  t))))
+        (kill-emacs))))
 
 ;; This file must be loaded each time Emacs is run from scratch, e.g., temacs.
 ;; So run the startup code now.  First, remove `-l loadup' from args.
@@ -621,6 +672,13 @@ lost after dumping")))
 (setq load-file-name nil)
 (eval top-level t)
 
+;; loadup.el is loaded at startup, but clobbers current-load-list.
+;; Set current-load-list to a list containing no definitions and only
+;; its name, to prevent invalid entries from ending up in
+;; Vload_history when running temacs interactively.
+
+(setq current-load-list (list "loadup.el"))
+
 
 ;; Local Variables:
 ;; no-byte-compile: t
diff --git a/lisp/ls-lisp.el b/lisp/ls-lisp.el
index 485ac4476ef..b0f86839740 100644
--- a/lisp/ls-lisp.el
+++ b/lisp/ls-lisp.el
@@ -184,7 +184,7 @@ if emulation is GNU then default is `(links uid gid)'."
   :group 'ls-lisp)
 
 (defcustom ls-lisp-use-insert-directory-program
-  (not (memq system-type '(ms-dos windows-nt)))
+  (not (memq system-type '(ms-dos windows-nt android)))
   "Non-nil causes ls-lisp to revert back to using `insert-directory-program'.
 This is useful on platforms where ls-lisp is dumped into Emacs, such as
 Microsoft Windows, but you would still like to use a program to list
diff --git a/lisp/mail/emacsbug.el b/lisp/mail/emacsbug.el
index 7a66089aec9..3893e93238f 100644
--- a/lisp/mail/emacsbug.el
+++ b/lisp/mail/emacsbug.el
@@ -408,6 +408,12 @@ copy text to your preferred mail program.\n"
                   "', version "
                  (mapconcat #'number-to-string (x-server-version) ".") "\n")
        (error t)))
+  (when (and (boundp 'android-build-fingerprint)
+             (symbol-value 'android-build-fingerprint))
+    ;; This is used on Android.
+    (insert "Android version and manufacturer: "
+            (symbol-value 'android-build-fingerprint)
+            "\n"))
   (let ((os (ignore-errors (report-emacs-bug--os-description))))
     (if (stringp os)
         (insert "System Description: " os "\n\n")))
diff --git a/lisp/mail/footnote.el b/lisp/mail/footnote.el
index b88fd405da8..4b2770ee3a1 100644
--- a/lisp/mail/footnote.el
+++ b/lisp/mail/footnote.el
@@ -347,10 +347,11 @@ Use Unicode characters for footnoting."
     ("ק" "ר" "ש" "ת" "תק" "תר" "תש" "תת" "תתק")))
 
 (defconst footnote-hebrew-numeric-regex
-  (let ((numchars (string-to-list
-                  (apply #'concat (apply #'append footnote-hebrew-numeric)))))
+  (let ((numchars
+         (delete-dups
+          (string-to-list
+           (apply #'concat (apply #'append footnote-hebrew-numeric))))))
     (rx-to-string `(1+ (in ?' ,@numchars)))))
-;; (defconst footnote-hebrew-numeric-regex 
"\\([אבגדהוזחט]'\\)?\\(ת\\)?\\(ת\\)?\\([קרשת]\\)?\\([טיכלמנסעפצ]\\)?\\([אבגדהוזחט]\\)?")
 
 (defun footnote--hebrew-numeric (n)
   "Supports 9999 footnotes, then rolls over."
diff --git a/lisp/mail/rmail.el b/lisp/mail/rmail.el
index 872299c2415..716848dc34f 100644
--- a/lisp/mail/rmail.el
+++ b/lisp/mail/rmail.el
@@ -264,7 +264,7 @@ Otherwise, look for `movemail' in the directories in
          ;; assuming it would work.
          ;; https://lists.gnu.org/r/bug-gnu-emacs/2008-02/msg00087.html
          (let ((progname (expand-file-name
-                          (concat "movemail"
+                          (concat movemail-program-name
                                   (if (memq system-type '(ms-dos windows-nt))
                                       ".exe")) dir)))
            (when (and (not (file-directory-p progname))
@@ -2016,7 +2016,9 @@ Value is the size of the newly read mail after 
conversion."
                 (buffer-disable-undo errors)
                 (let ((args
                        (append
-                        (list (or rmail-movemail-program "movemail") nil 
errors nil)
+                        (list (or rmail-movemail-program
+                                   movemail-program-name)
+                               nil errors nil)
                         (if rmail-preserve-inbox
                             (list "-p")
                           nil)
diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el
index 030fe374598..5e837485db3 100644
--- a/lisp/menu-bar.el
+++ b/lisp/menu-bar.el
@@ -79,6 +79,14 @@
                   :help "Print current buffer with page headings"))
     menu))
 
+(defcustom menu-bar-close-window nil
+  "Whether or not to close the current window from the menu bar.
+If non-nil, selecting Close from the File menu or clicking Close
+in the tool bar will close the current window where possible."
+  :type 'boolean
+  :group 'menu
+  :version "30.1")
+
 (defvar menu-bar-file-menu
   (let ((menu (make-sparse-keymap "File")))
 
@@ -486,6 +494,11 @@
 (defvar menu-bar-edit-menu
   (let ((menu (make-sparse-keymap "Edit")))
 
+    (bindings--define-key menu [execute-extended-command]
+      '(menu-item "Execute Command" execute-extended-command
+                  :enable t
+                  :help "Read a command name, its arguments, then call it."))
+
     ;; ns-win.el said: Add spell for platform consistency.
     (if (featurep 'ns)
         (bindings--define-key menu [spell]
@@ -686,10 +699,10 @@ Do the same for the keys of the same name."
       menu-bar-separator)
     (bindings--define-key menu [customize-browse]
       '(menu-item "Browse Customization Groups" customize-browse
-                  :help "Browse all customization groups"))
+                  :help "Tree-like browser of all the groups of customizable 
options"))
     (bindings--define-key menu [customize]
-      '(menu-item "Top-level Customization Group" customize
-                  :help "The master group called `Emacs'"))
+      '(menu-item "Top-level Emacs Customization Group" customize
+                  :help "Top-level groups of customizable options, and their 
descriptions"))
     (bindings--define-key menu [customize-themes]
       '(menu-item "Custom Themes" customize-themes
                   :help "Choose a pre-defined customization theme"))
@@ -2213,12 +2226,19 @@ otherwise it could decide to silently do nothing."
    ;; (Bug#8184).
    ((not (menu-bar-menu-frame-live-and-visible-p)))
    ((menu-bar-non-minibuffer-window-p)
-    (kill-buffer (current-buffer)))
+    (kill-buffer (current-buffer))
+    ;; Also close the current window if `menu-bar-close-windows' is
+    ;; set.
+    (when menu-bar-close-window
+      (ignore-errors (delete-window))))
    (t
     (abort-recursive-edit))))
 
 (defun kill-this-buffer-enabled-p ()
-  "Return non-nil if the `kill-this-buffer' menu item should be enabled."
+  "Return non-nil if the `kill-this-buffer' menu item should be enabled.
+It should be enabled there is at least one non-hidden buffer, or if
+`menu-bar-close-window' is non-nil and there is more than one window on
+this frame."
   (or (not (menu-bar-non-minibuffer-window-p))
       (let (found-1)
        ;; Instead of looping over entire buffer list, stop once we've
@@ -2228,7 +2248,9 @@ otherwise it could decide to silently do nothing."
            (unless (string-match-p "^ " (buffer-name buffer))
              (if (not found-1)
                  (setq found-1 t)
-               (throw 'found-2 t))))))))
+               (throw 'found-2 t))))))
+      (and menu-bar-close-window
+           (window-parent (selected-window)))))
 
 (put 'dired 'menu-enable '(menu-bar-non-minibuffer-window-p))
 
@@ -2708,20 +2730,25 @@ FROM-MENU-BAR, if non-nil, means we are dropping one of 
menu-bar's menus."
 POSITION can be an event, a posn- value, a value having the
 form ((XOFFSET YOFFSET) WINDOW), or nil.
 If nil, the current mouse position is used, or nil if there is no mouse."
-  (pcase position
+  (cond
     ;; nil -> mouse cursor position
-    ('nil
+    ((eq position nil)
      (let ((mp (mouse-pixel-position)))
        (list (list (cadr mp) (cddr mp)) (car mp))))
     ;; Value returned from `event-end' or `posn-at-point'.
-    ((pred posnp)
+    ((posnp position)
      (let ((xy (posn-x-y position)))
        (list (list (car xy) (cdr xy))
             (posn-window position))))
+    ;; `touchscreen-begin' or `touchscreen-end' event.
+    ((or (eq (car-safe position) 'touchscreen-begin)
+         (eq (car-safe position) 'touchscreen-end))
+     position)
     ;; Event.
-    ((pred eventp)
+    ((eventp position)
      (popup-menu-normalize-position (event-end position)))
-    (_ position)))
+    ;; Some other value.
+    (t position)))
 
 (defcustom tty-menu-open-use-tmm nil
   "If non-nil, \\[menu-bar-open] on a TTY will invoke `tmm-menubar'.
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 275302fece5..0f6220d519d 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -975,10 +975,16 @@ Intended to be called via `clear-message-function'."
     (when (overlayp minibuffer-message-overlay)
       (delete-overlay minibuffer-message-overlay)
       (setq minibuffer-message-overlay nil)))
-
-  ;; Return nil telling the caller that the message
-  ;; should be also handled by the caller.
-  nil)
+  ;; Don't clear the message if touch screen drag-to-select is in
+  ;; progress, because a preview message might currently be displayed
+  ;; in the echo area.  FIXME: find some way to place this in
+  ;; touch-screen.el.
+  (if (and touch-screen-preview-select
+           (eq (nth 3 touch-screen-current-tool) 'drag))
+      'dont-clear-message
+    ;; Return nil telling the caller that the message
+    ;; should be also handled by the caller.
+    nil))
 
 (setq clear-message-function 'clear-minibuffer-message)
 
@@ -2959,7 +2965,10 @@ For customizing this mode, it is better to use
 `minibuffer-setup-hook' and `minibuffer-exit-hook' rather than
 the mode hook of this mode."
   :syntax-table nil
-  :interactive nil)
+  :interactive nil
+  ;; Enable text conversion, but always make sure `RET' does
+  ;; something.
+  (setq text-conversion-style 'action))
 
 ;;; Completion tables.
 
@@ -4614,6 +4623,59 @@ is included in the return value."
                   default)))
    ": "))
 
+
+;;; On screen keyboard support.
+;; Try to display the on screen keyboard whenever entering the
+;; mini-buffer, and hide it whenever leaving.
+
+(defvar minibuffer-on-screen-keyboard-timer nil
+  "Timer run upon exiting the minibuffer.
+It will hide the on screen keyboard when necessary.")
+
+(defvar minibuffer-on-screen-keyboard-displayed nil
+  "Whether or not the on-screen keyboard has been displayed.
+Set inside `minibuffer-setup-on-screen-keyboard'.")
+
+(defun minibuffer-setup-on-screen-keyboard ()
+  "Maybe display the on-screen keyboard in the current frame.
+Display the on-screen keyboard in the current frame if the
+last device to have sent an input event is not a keyboard.
+This is run upon minibuffer setup."
+  ;; Don't hide the on screen keyboard later on.
+  (when minibuffer-on-screen-keyboard-timer
+    (cancel-timer minibuffer-on-screen-keyboard-timer)
+    (setq minibuffer-on-screen-keyboard-timer nil))
+  (setq minibuffer-on-screen-keyboard-displayed nil)
+  (when (and (framep last-event-frame)
+             (not (memq (device-class last-event-frame
+                                      last-event-device)
+                        '(keyboard core-keyboard))))
+    (setq minibuffer-on-screen-keyboard-displayed
+          (frame-toggle-on-screen-keyboard (selected-frame) nil))))
+
+(defun minibuffer-exit-on-screen-keyboard ()
+  "Hide the on-screen keyboard if it was displayed.
+Hide the on-screen keyboard in a timer set to run in 0.1 seconds.
+It will be cancelled if the minibuffer is displayed again within
+that timeframe.
+
+Do not hide the on screen keyboard inside a recursive edit.
+Likewise, do not hide the on screen keyboard if point in the
+window that will be selected after exiting the minibuffer is not
+on read-only text.
+
+The latter is implemented in `touch-screen.el'."
+  (unless (or (not minibuffer-on-screen-keyboard-displayed)
+              (> (recursion-depth) 1))
+    (when minibuffer-on-screen-keyboard-timer
+      (cancel-timer minibuffer-on-screen-keyboard-timer))
+    (setq minibuffer-on-screen-keyboard-timer
+          (run-with-timer 0.1 nil #'frame-toggle-on-screen-keyboard
+                          (selected-frame) t))))
+
+(add-hook 'minibuffer-setup-hook #'minibuffer-setup-on-screen-keyboard)
+(add-hook 'minibuffer-exit-hook #'minibuffer-exit-on-screen-keyboard)
+
 (provide 'minibuffer)
 
 ;;; minibuffer.el ends here
diff --git a/lisp/mouse.el b/lisp/mouse.el
index 661980f4ac2..11a2db52eec 100644
--- a/lisp/mouse.el
+++ b/lisp/mouse.el
@@ -206,13 +206,16 @@ always return a positive integer or zero."
 
 ;; Provide a mode-specific menu on a mouse button.
 
-(defun minor-mode-menu-from-indicator (indicator &optional window)
+(defun minor-mode-menu-from-indicator (indicator &optional window event)
   "Show menu for minor mode specified by INDICATOR.
 
 INDICATOR is either a string object returned by `posn-object' or
 the car of such an object.  WINDOW may be the window whose mode
 line is being displayed.
 
+EVENT may be the mouse event that is causing this menu to be
+displayed.
+
 Interactively, INDICATOR is read using completion.
 If there is no menu defined for the minor mode, then create one with
 items `Turn Off' and `Help'."
@@ -276,7 +279,7 @@ items `Turn Off' and `Help'."
                           ,(lambda () (interactive)
                              (describe-function mm-fun)))))))
       (if menu
-          (popup-menu menu)
+          (popup-menu menu event)
         (message "No menu available")))))
 
 (defun mouse-minor-mode-menu (event)
@@ -285,7 +288,10 @@ items `Turn Off' and `Help'."
   (let* ((posn (event-start event))
          (indicator (posn-object posn))
          (window (posn-window posn)))
-    (minor-mode-menu-from-indicator indicator window)))
+    (minor-mode-menu-from-indicator indicator window event)))
+
+;; See (elisp)Touchscreen Events.
+(put 'mouse-minor-mode-menu 'mouse-1-menu-command t)
 
 (defun mouse-menu-major-mode-map ()
   (run-hooks 'activate-menubar-hook 'menu-bar-update-hook)
diff --git a/lisp/mwheel.el b/lisp/mwheel.el
index caa74159ecd..86ed7393a17 100644
--- a/lisp/mwheel.el
+++ b/lisp/mwheel.el
@@ -58,7 +58,8 @@
 
 (defcustom mouse-wheel-down-event
   (if (or (featurep 'w32-win) (featurep 'ns-win)
-          (featurep 'haiku-win) (featurep 'pgtk-win))
+          (featurep 'haiku-win) (featurep 'pgtk-win)
+          (featurep 'android-win))
       'wheel-up
     'mouse-4)
   "Event used for scrolling down."
@@ -79,7 +80,8 @@
 
 (defcustom mouse-wheel-up-event
   (if (or (featurep 'w32-win) (featurep 'ns-win)
-          (featurep 'haiku-win) (featurep 'pgtk-win))
+          (featurep 'haiku-win) (featurep 'pgtk-win)
+          (featurep 'android-win))
       'wheel-down
     'mouse-5)
   "Event used for scrolling up."
@@ -254,7 +256,8 @@ Also see `mouse-wheel-tilt-scroll'."
 
 (defvar mouse-wheel-left-event
   (if (or (featurep 'w32-win) (featurep 'ns-win)
-          (featurep 'haiku-win) (featurep 'pgtk-win))
+          (featurep 'haiku-win) (featurep 'pgtk-win)
+          (featurep 'android-win))
       'wheel-left
     'mouse-6)
   "Event used for scrolling left.")
@@ -268,7 +271,8 @@ Also see `mouse-wheel-tilt-scroll'."
 
 (defvar mouse-wheel-right-event
   (if (or (featurep 'w32-win) (featurep 'ns-win)
-          (featurep 'haiku-win) (featurep 'pgtk-win))
+          (featurep 'haiku-win) (featurep 'pgtk-win)
+          (featurep 'android-win))
       'wheel-right
     'mouse-7)
   "Event used for scrolling right.")
diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el
index 39513b8f602..daa46baf8f2 100644
--- a/lisp/net/browse-url.el
+++ b/lisp/net/browse-url.el
@@ -52,6 +52,7 @@
 ;; browse-url-xdg-open                freedesktop.org xdg-open
 ;; browse-url-kde                     KDE konqueror (kfm)
 ;; browse-url-elinks                  Elinks      Don't know (tried with 
0.12.GIT)
+;; browse-url-default-android-browser Android     2.3.3 (should work on 2.2 
too)
 ;; eww-browse-url                     Emacs Web Wowser
 
 ;; Browsers can cache web pages so it may be necessary to tell them to
@@ -173,6 +174,9 @@
     ,@(when (eq system-type 'darwin)
         (list '(function-item :tag "Default macOS browser"
                               :value browse-url-default-macosx-browser)))
+    ,@(when (eq system-type 'android)
+        (list '(function-item :tag "Default Android browser"
+                              :value browse-url-default-android-browser)))
     (function-item :tag "Default browser"
                   :value browse-url-default-browser)
     (function :tag "Your own function")
@@ -1064,6 +1068,8 @@ instead of `browse-url-new-window-flag'."
      'browse-url-default-macosx-browser)
     ((featurep 'haiku)
      'browse-url-default-haiku-browser)
+    ((eq system-type 'android)
+     'browse-url-default-android-browser)
     ((browse-url-can-use-xdg-open) 'browse-url-xdg-open)
 ;;;    ((executable-find browse-url-gnome-moz-program) 'browse-url-gnome-moz)
     ((executable-find browse-url-firefox-program) 'browse-url-firefox)
@@ -1301,6 +1307,35 @@ Default to the URL around or before point."
 (function-put 'browse-url-default-haiku-browser
               'browse-url-browser-kind 'external)
 
+(defcustom browse-url-android-share nil
+  "If non-nil, share URLs instead of opening them.
+When non-nil, `browse-url-default-android-browser' will try to
+share the URL being browsed through programs such as mail clients
+and instant messengers instead of opening it in a web browser."
+  :type 'boolean
+  :version "30.1")
+
+(declare-function android-browse-url "androidselect.c")
+
+;;;###autoload
+(defun browse-url-default-android-browser (url &optional _new-window)
+  "Browse URL with the system default browser.
+If `browse-url-android-share' is non-nil, try to share URL using
+an external program instead.  Default to the URL around or before
+point."
+  (interactive (browse-url-interactive-arg "URL: "))
+  (unless browse-url-android-share
+    ;; The URL shouldn't be encoded if it's being shared through
+    ;; another program.
+    (setq url (browse-url-encode-url url)))
+  ;; Make sure the URL starts with an appropriate scheme.
+  (unless (string-match "\\(.+\\):/" url)
+    (setq url (concat "http://"; url)))
+  (android-browse-url url browse-url-android-share))
+
+(function-put 'browse-url-default-android-browser
+              'browse-url-browser-kind 'external)
+
 ;;;###autoload
 (defun browse-url-emacs (url &optional same-window)
   "Ask Emacs to load URL into a buffer and show it in another window.
diff --git a/lisp/net/eww.el b/lisp/net/eww.el
index 2e743751427..cb73926f462 100644
--- a/lisp/net/eww.el
+++ b/lisp/net/eww.el
@@ -249,7 +249,7 @@ parameter, and should return the (possibly) transformed 
URL."
   :version "29.1")
 
 (defface eww-form-submit
-  '((((type x w32 ns haiku pgtk) (class color))        ; Like default mode line
+  '((((type x w32 ns haiku pgtk android) (class color))        ; Like default 
mode line
      :box (:line-width 2 :style released-button)
      :background "#808080" :foreground "black"))
   "Face for eww buffer buttons."
@@ -257,7 +257,7 @@ parameter, and should return the (possibly) transformed 
URL."
   :group 'eww)
 
 (defface eww-form-file
-  '((((type x w32 ns haiku pgtk) (class color))        ; Like default mode line
+  '((((type x w32 ns haiku pgtk android) (class color))        ; Like default 
mode line
      :box (:line-width 2 :style released-button)
      :background "#808080" :foreground "black"))
   "Face for eww buffer buttons."
@@ -265,7 +265,7 @@ parameter, and should return the (possibly) transformed 
URL."
   :group 'eww)
 
 (defface eww-form-checkbox
-  '((((type x w32 ns haiku pgtk) (class color))        ; Like default mode line
+  '((((type x w32 ns haiku pgtk android) (class color))        ; Like default 
mode line
      :box (:line-width 2 :style released-button)
      :background "lightgrey" :foreground "black"))
   "Face for eww buffer buttons."
@@ -273,7 +273,7 @@ parameter, and should return the (possibly) transformed 
URL."
   :group 'eww)
 
 (defface eww-form-select
-  '((((type x w32 ns haiku pgtk) (class color))        ; Like default mode line
+  '((((type x w32 ns haiku pgtk android) (class color))        ; Like default 
mode line
      :box (:line-width 2 :style released-button)
      :background "lightgrey" :foreground "black"))
   "Face for eww buffer buttons."
diff --git a/lisp/net/newst-backend.el b/lisp/net/newst-backend.el
index a68a6bf1a24..31dc8d8e177 100644
--- a/lisp/net/newst-backend.el
+++ b/lisp/net/newst-backend.el
@@ -81,15 +81,15 @@ considered to be running if the newsticker timer list is 
not empty."
     ("The Register"
      "https://www.theregister.co.uk/headlines.rss";)
     ("slashdot"
-     "http://rss.slashdot.org/Slashdot/slashdot";
+     "https://rss.slashdot.org/Slashdot/slashdot";
      nil
      3600)                        ;/. will ban you if under 3600 seconds!
     ("Wired News"
      "https://www.wired.com/feed/rss";)
     ("Heise News (german)"
-     "http://www.heise.de/newsticker/heise.rdf";)
+     "https://www.heise.de/newsticker/heise.rdf";)
     ("Tagesschau (german)"
-     "http://www.tagesschau.de/newsticker.rdf";
+     "https://www.tagesschau.de/newsticker.rdf";
      nil
      1800))
   "Default URL list in raw form.
@@ -1168,7 +1168,7 @@ URL 
`http://www.atompub.org/2005/08/17/draft-ietf-atompub-format-11.html'"
                       ;; allows for integrating (x)html into the atom
                       ;; structure but we need the raw html string.
                       ;; e.g. https://www.heise.de/open/news/news-atom.xml
-                      ;; http://feeds.feedburner.com/ru_nix_blogs
+                      ;; https://feeds.feedburner.com/ru_nix_blogs
                       (or (newsticker--unxml
                            (car (xml-node-children
                                  (car (xml-get-children node 'content)))))
@@ -1302,7 +1302,7 @@ For the RSS 0.92 specification see URL 
`http://backend.userland.com/rss092'."
 Return value as well as arguments NAME, TIME, and TOPNODE are the
 same as in `newsticker--parse-atom-1.0'.
 
-For the RSS 1.0 specification see URL `http://web.resource.org/rss/1.0/spec'."
+For the RSS 1.0 specification see URL `https://web.resource.org/rss/1.0/spec'."
   (newsticker--debug-msg "Parsing RSS 1.0 feed %s" name)
   (let* ((channelnode (car (xml-get-children topnode 'channel)))
          is-new-feed has-new-items)
@@ -1361,7 +1361,7 @@ For the RSS 1.0 specification see URL 
`http://web.resource.org/rss/1.0/spec'."
 Return value as well as arguments NAME, TIME, and TOPNODE are the
 same as in `newsticker--parse-atom-1.0'.
 
-For the RSS 2.0 specification see URL `http://blogs.law.harvard.edu/tech/rss'."
+For the RSS 2.0 specification see URL `https://cyber.harvard.edu/rss/'."
   (newsticker--debug-msg "Parsing RSS 2.0 feed %s" name)
   (let* ((channelnode (car (xml-get-children topnode 'channel)))
          is-new-feed has-new-items)
diff --git a/lisp/net/newsticker.el b/lisp/net/newsticker.el
index 938683a12a1..c86174b6d8e 100644
--- a/lisp/net/newsticker.el
+++ b/lisp/net/newsticker.el
@@ -47,9 +47,9 @@
 ;;  * RSS 0.92
 ;;    (see http://backend.userland.com/rss092)
 ;;  * RSS 1.0
-;;    (see http://purl.org/rss/1.0/spec)
+;;    (see https://web.resource.org/rss/1.0/spec)
 ;;  * RSS 2.0
-;;    (see http://blogs.law.harvard.edu/tech/rss)
+;;    (see https://cyber.harvard.edu/rss/)
 ;; as well as the following Atom formats:
 ;;  * Atom 0.3
 ;;  * Atom 1.0
diff --git a/lisp/net/tramp-archive.el b/lisp/net/tramp-archive.el
index 6fcb0ae5e69..590544f199f 100644
--- a/lisp/net/tramp-archive.el
+++ b/lisp/net/tramp-archive.el
@@ -263,7 +263,7 @@ It must be supported by libarchive(3).")
     (file-regular-p . tramp-handle-file-regular-p)
     ;; `file-remote-p' performed by default handler.
     (file-selinux-context . tramp-handle-file-selinux-context)
-    (file-symlink-p . tramp-handle-file-symlink-p)
+    (file-symlink-p . tramp-archive-handle-file-symlink-p)
     (file-system-info . tramp-archive-handle-file-system-info)
     (file-truename . tramp-archive-handle-file-truename)
     (file-user-uid . tramp-archive-handle-file-user-uid)
@@ -666,6 +666,10 @@ offered."
   "Like `file-readable-p' for file archives."
   (file-readable-p (tramp-archive-gvfs-file-name filename)))
 
+(defun tramp-archive-handle-file-symlink-p (filename)
+  "Like `file-symlink-p' for file archives."
+  (file-symlink-p (tramp-archive-gvfs-file-name filename)))
+
 (defun tramp-archive-handle-file-system-info (filename)
   "Like `file-system-info' for file archives."
   (with-parsed-tramp-archive-file-name filename nil
diff --git a/lisp/net/tramp-cache.el b/lisp/net/tramp-cache.el
index e0d38853956..8282e9c87ff 100644
--- a/lisp/net/tramp-cache.el
+++ b/lisp/net/tramp-cache.el
@@ -80,7 +80,6 @@
 ;;; Code:
 
 (require 'tramp-compat)
-(require 'tramp-loaddefs)
 (require 'time-stamp)
 
 ;;; -- Cache --
@@ -125,6 +124,7 @@ details see the info pages."
 If it doesn't exist yet, it is created and initialized with
 matching entries of `tramp-connection-properties'.
 If KEY is `tramp-cache-undefined', don't create anything, and return nil."
+  (declare (tramp-suppress-trace t))
   (unless (eq key tramp-cache-undefined)
     (or (gethash key tramp-cache-data)
        (let ((hash
@@ -506,6 +506,7 @@ PROPERTIES is a list of file properties (strings)."
 ;;;###tramp-autoload
 (defun tramp-cache-print (table)
   "Print hash table TABLE."
+  ;; (declare (tramp-suppress-trace t))
   (when (hash-table-p table)
     (let (result)
       (maphash
@@ -538,6 +539,11 @@ PROPERTIES is a list of file properties (strings)."
        table)
       result)))
 
+;; We cannot use the `declare' form for `tramp-suppress-trace' in
+;; autoloaded functions, because the tramp-loaddefs.el generation
+;; would fail.
+(function-put #'tramp-cache-print 'tramp-suppress-trace t)
+
 ;;;###tramp-autoload
 (defun tramp-list-connections ()
   "Return all active `tramp-file-name' structs according to 
`tramp-cache-data'."
@@ -553,6 +559,7 @@ PROPERTIES is a list of file properties (strings)."
 (defun tramp-dump-connection-properties ()
   "Write persistent connection properties into file \
 `tramp-persistency-file-name'."
+  (declare (tramp-suppress-trace t))
   ;; We shouldn't fail, otherwise Emacs might not be able to be closed.
   (ignore-errors
     (when (and (hash-table-p tramp-cache-data)
diff --git a/lisp/net/tramp-cmds.el b/lisp/net/tramp-cmds.el
index 3c9b9e984e6..87651d60328 100644
--- a/lisp/net/tramp-cmds.el
+++ b/lisp/net/tramp-cmds.el
@@ -221,6 +221,7 @@ functions are called with `current-buffer' set."
 
 (defun tramp-cleanup-dired-buffer-p ()
   "Return t if current buffer runs `dired-mode'."
+  (declare (tramp-suppress-trace t))
   (derived-mode-p 'dired-mode))
 
 (add-hook 'tramp-cleanup-some-buffers-hook
@@ -231,14 +232,21 @@ functions are called with `current-buffer' set."
 
 (defun tramp-delete-tainted-remote-process-buffer-function ()
   "Delete current buffer from `tramp-tainted-remote-process-buffers'."
+  (declare (tramp-suppress-trace t))
   (setq tramp-tainted-remote-process-buffers
        (delete (current-buffer) tramp-tainted-remote-process-buffers)))
 
 ;;;###tramp-autoload
 (defun tramp-taint-remote-process-buffer (buffer)
   "Mark buffer as related to remote processes."
+  ;; (declare (tramp-suppress-trace t))
   (add-to-list 'tramp-tainted-remote-process-buffers buffer))
 
+;; We cannot use the `declare' form for `tramp-suppress-trace' in
+;; autoloaded functions, because the tramp-loaddefs.el generation
+;; would fail.
+(function-put #'tramp-taint-remote-process-buffer 'tramp-suppress-trace t)
+
 (add-hook 'kill-buffer-hook
          #'tramp-delete-tainted-remote-process-buffer-function)
 (add-hook 'tramp-unload-hook
diff --git a/lisp/net/tramp-compat.el b/lisp/net/tramp-compat.el
index 85ddb81f398..5bd3dff3d21 100644
--- a/lisp/net/tramp-compat.el
+++ b/lisp/net/tramp-compat.el
@@ -29,6 +29,7 @@
 
 ;;; Code:
 
+(require 'tramp-loaddefs)
 (require 'ansi-color)
 (require 'auth-source)
 (require 'format-spec)
@@ -36,7 +37,7 @@
 (require 'shell)
 (require 'xdg)
 
-(declare-function tramp-error "tramp")
+(declare-function tramp-error "tramp-message")
 (declare-function tramp-tramp-file-p "tramp")
 (defvar tramp-temp-name-prefix)
 
diff --git a/lisp/net/tramp-crypt.el b/lisp/net/tramp-crypt.el
index c85f566c4d5..79eafc5c12e 100644
--- a/lisp/net/tramp-crypt.el
+++ b/lisp/net/tramp-crypt.el
@@ -281,7 +281,7 @@ arguments to pass to the OPERATION."
                    (assoc operation tramp-crypt-file-name-handler-alist))))
       (prog1 (save-match-data (apply (cdr fn) args))
        (setq tramp-debug-message-fnh-function (cdr fn)))
-    (prog1 (tramp-run-real-handler operation args)
+    (prog1 (tramp-crypt-run-real-handler operation args)
       (setq tramp-debug-message-fnh-function operation))))
 
 ;;;###tramp-autoload
diff --git a/lisp/net/tramp-integration.el b/lisp/net/tramp-integration.el
index d7fcd8afefa..c73c86a9110 100644
--- a/lisp/net/tramp-integration.el
+++ b/lisp/net/tramp-integration.el
@@ -65,6 +65,7 @@
   "Set up a minibuffer for `file-name-shadow-mode'.
 Adds another overlay hiding filename parts according to Tramp's
 special handling of `substitute-in-file-name'."
+  (declare (tramp-suppress-trace t))
   (when minibuffer-completing-file-name
     (setq tramp-rfn-eshadow-overlay
          (make-overlay (minibuffer-prompt-end) (minibuffer-prompt-end)))
@@ -86,6 +87,7 @@ special handling of `substitute-in-file-name'."
 
 (defun tramp-rfn-eshadow-update-overlay-regexp ()
   "An overlay covering the shadowed part of the filename."
+  (declare (tramp-suppress-trace t))
   (rx-to-string
    `(: (* (not (any ,tramp-postfix-host-format "/~"))) (| "/" "~"))))
 
@@ -94,6 +96,7 @@ special handling of `substitute-in-file-name'."
 This is intended to be used as a minibuffer `post-command-hook' for
 `file-name-shadow-mode'; the minibuffer should have already
 been set up by `rfn-eshadow-setup-minibuffer'."
+  (declare (tramp-suppress-trace t))
   ;; In remote files name, there is a shadowing just for the local part.
   (ignore-errors
     (let ((end (or (overlay-end rfn-eshadow-overlay)
diff --git a/lisp/net/tramp-message.el b/lisp/net/tramp-message.el
index cf90db1d6b1..cca22a28d7c 100644
--- a/lisp/net/tramp-message.el
+++ b/lisp/net/tramp-message.el
@@ -47,25 +47,13 @@
 
 ;;; Code:
 
-(require 'tramp-loaddefs)
+(require 'tramp-compat)
 (require 'help-mode)
 
-(declare-function tramp-compat-string-replace "tramp-compat")
 (declare-function tramp-file-name-equal-p "tramp")
 (declare-function tramp-file-name-host-port "tramp")
 (declare-function tramp-file-name-user-domain "tramp")
 (declare-function tramp-get-default-directory "tramp")
-(defvar tramp-compat-temporary-file-directory)
-
-(eval-and-compile
-  (defalias 'tramp-byte-run--set-suppress-trace
-    #'(lambda (f _args val)
-       (list 'function-put (list 'quote f)
-              ''tramp-suppress-trace val)))
-
-  (add-to-list
-   'defun-declarations-alist
-   (list 'tramp-suppress-trace #'tramp-byte-run--set-suppress-trace)))
 
 ;;;###tramp-autoload
 (defcustom tramp-verbose 3
@@ -132,6 +120,7 @@ When it is used for regexp matching, the regexp groups are
 Point must be at the beginning of a header line.
 
 The outline level is equal to the verbosity of the Tramp message."
+  (declare (tramp-suppress-trace t))
   (1+ (string-to-number (match-string 3))))
 
 ;; This function takes action since Emacs 28.1, when
@@ -140,6 +129,7 @@ The outline level is equal to the verbosity of the Tramp 
message."
 (defun tramp-debug-buffer-command-completion-p (_symbol buffer)
   "A predicate for Tramp interactive commands.
 They are completed by \"M-x TAB\" only in Tramp debug buffers."
+  (declare (tramp-suppress-trace t))
   (with-current-buffer buffer
     (string-equal
      (buffer-substring (point-min) (min (+ (point-min) 10) (point-max)))
@@ -306,6 +296,7 @@ is greater than or equal 4.
 Calls functions `message' and `tramp-debug-message' with FMT-STRING as
 control string and the remaining ARGUMENTS to actually emit the message (if
 applicable)."
+  ;; (declare (tramp-suppress-trace t))
   (ignore-errors
     (when (<= level tramp-verbose)
       ;; Display only when there is a minimum level, and the progress
@@ -346,8 +337,10 @@ applicable)."
                 (concat (format "(%d) # " level) fmt-string)
                 arguments))))))
 
-;; We cannot declare our private symbols in loaddefs.
-(function-put 'tramp-message 'tramp-suppress-trace t)
+;; We cannot use the `declare' form for `tramp-suppress-trace' in
+;; autoloaded functions, because the tramp-loaddefs.el generation
+;; would fail.
+(function-put #'tramp-message 'tramp-suppress-trace t)
 
 (defsubst tramp-backtrace (&optional vec-or-proc force)
   "Dump a backtrace into the debug buffer.
@@ -473,6 +466,7 @@ the resulting error message."
 
 (defun tramp-debug-button-action (button)
   "Goto the linked message in debug buffer at place."
+  (declare (tramp-suppress-trace t))
   (when (mouse-event-p last-input-event) (mouse-set-point last-input-event))
   (when-let ((point (button-get button 'position)))
     (goto-char point)))
@@ -485,6 +479,7 @@ the resulting error message."
 (defun tramp-debug-link-messages (pos1 pos2)
   "Set links for two messages in current buffer.
 The link buttons are in the verbositiy level substrings."
+  (declare (tramp-suppress-trace t))
   (save-excursion
     (let (beg1 end1 beg2 end2)
       (goto-char pos1)
@@ -518,6 +513,7 @@ Bound in `tramp-*-file-name-handler' functions.")
 
 (defun tramp-debug-message-buttonize (position)
   "Buttonize function in current buffer, at next line starting after POSTION."
+  (declare (tramp-suppress-trace t))
   (save-excursion
     (goto-char position)
     (while (not (search-forward-regexp
diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el
index b33e788b893..2b1d26dd232 100644
--- a/lisp/net/tramp-sh.el
+++ b/lisp/net/tramp-sh.el
@@ -717,6 +717,25 @@ on the remote file system.
 Format specifiers are replaced by `tramp-expand-script', percent
 characters need to be doubled.")
 
+(defconst tramp-stat-file-attributes-with-selinux
+  (format
+   (concat
+    "(%%s -c"
+    " '((%s%%%%N%s) %%%%h (%s%%%%U%s . %%%%u) (%s%%%%G%s . %%%%g)"
+    " %%%%X %%%%Y %%%%Z %%%%s %s%%%%A%s t %%%%i -1 %s%%%%C%s)'"
+    " \"$1\" %%n || echo nil) |"
+    " sed -e 's/\"/\\\\\"/g' -e 's/%s/\"/g'")
+   tramp-stat-marker tramp-stat-marker ; %%N
+   tramp-stat-marker tramp-stat-marker ; %%U
+   tramp-stat-marker tramp-stat-marker ; %%G
+   tramp-stat-marker tramp-stat-marker ; %%A
+   tramp-stat-marker tramp-stat-marker ; %%C
+   tramp-stat-quoted-marker)
+  "Shell function to produce output suitable for use with `file-attributes'
+on the remote file system, including SELinux context.
+Format specifiers are replaced by `tramp-expand-script', percent
+characters need to be doubled.")
+
 (defconst tramp-perl-directory-files-and-attributes
   "%p -e '
 chdir($ARGV[0]) or printf(\"\\\"Cannot change to $ARGV[0]: $''!''\\\"\\n\"), 
exit();
@@ -795,6 +814,33 @@ characters need to be doubled.")
 Format specifiers are replaced by `tramp-expand-script', percent
 characters need to be doubled.")
 
+(defconst tramp-stat-directory-files-and-attributes-with-selinux
+  (format
+   (concat
+    ;; We must care about file names with spaces, or starting with
+    ;; "-"; this would confuse xargs.  "ls -aQ" might be a solution,
+    ;; but it does not work on all remote systems.  Therefore, we use
+    ;; \000 as file separator.  `tramp-sh--quoting-style-options' do
+    ;; not work for file names with spaces piped to "xargs".
+    ;; Apostrophes in the stat output are masked as
+    ;; `tramp-stat-marker', in order to make a proper shell escape of
+    ;; them in file names.
+    "cd \"$1\" && echo \"(\"; (%%l -a | tr '\\n\\r' '\\000\\000' |"
+    " xargs -0 %%s -c"
+    " '(%s%%%%n%s (%s%%%%N%s) %%%%h (%s%%%%U%s . %%%%u) (%s%%%%G%s . %%%%g) 
%%%%X %%%%Y %%%%Z %%%%s %s%%%%A%s t %%%%i -1 %s%%%%C%s)'"
+    " -- %%n | sed -e 's/\"/\\\\\"/g' -e 's/%s/\"/g'); echo \")\"")
+   tramp-stat-marker tramp-stat-marker ; %n
+   tramp-stat-marker tramp-stat-marker ; %N
+   tramp-stat-marker tramp-stat-marker ; %U
+   tramp-stat-marker tramp-stat-marker ; %G
+   tramp-stat-marker tramp-stat-marker ; %A
+   tramp-stat-marker tramp-stat-marker ; %C
+   tramp-stat-quoted-marker)
+  "Shell function implementing `directory-files-and-attributes' as Lisp
+`read'able output, including SELinux context.
+Format specifiers are replaced by `tramp-expand-script', percent
+characters need to be doubled.")
+
 (defconst tramp-perl-id
   "%p -e '
 use strict;
@@ -1255,10 +1301,10 @@ Operations not mentioned here will be handled by the 
normal Emacs functions.")
   (let (symlinkp dirp
        res-inode res-filemodes res-numlinks
        res-uid-string res-gid-string res-uid-integer res-gid-integer
-       res-size res-symlink-target)
+       res-size res-symlink-target res-context)
     (tramp-message vec 5 "file attributes with ls: %s" localname)
-    ;; We cannot send all three commands combined, it could exceed
-    ;; NAME_MAX or PATH_MAX.  Happened on macOS, for example.
+    ;; We cannot send both commands combined, it could exceed NAME_MAX
+    ;; or PATH_MAX.  Happened on macOS, for example.
     (when (tramp-send-command-and-check
            vec
            (format "cd %s && (%s %s || %s -h %s)"
@@ -1277,13 +1323,14 @@ Operations not mentioned here will be handled by the 
normal Emacs functions.")
                      (file-name-nondirectory localname)))))
       (tramp-send-command
        vec
-       (format "%s -ild %s %s; %s -lnd %s %s"
+       (format "%s -ild %s %s; %s -lnd%s %s %s"
                (tramp-get-ls-command vec)
                ;; On systems which have no quoting style, file names
                ;; with special characters could fail.
                (tramp-sh--quoting-style-options vec)
                (tramp-shell-quote-argument localname)
                (tramp-get-ls-command vec)
+              (if (tramp-remote-selinux-p vec) "Z" "")
                ;; On systems which have no quoting style, file names
                ;; with special characters could fail.
                (tramp-sh--quoting-style-options vec)
@@ -1333,6 +1380,10 @@ Operations not mentioned here will be handled by the 
normal Emacs functions.")
            (setq res-uid-integer tramp-unknown-id-integer))
           (unless (numberp res-gid-integer)
            (setq res-gid-integer tramp-unknown-id-integer))
+         ;; ... SELinux context
+         (when (tramp-remote-selinux-p vec)
+           (setq res-context (read (current-buffer))
+                 res-context (symbol-name res-context)))
 
          ;; Return data gathered.
           (list
@@ -1359,7 +1410,10 @@ Operations not mentioned here will be handled by the 
normal Emacs functions.")
            ;; 10. Inode number.
            res-inode
            ;; 11. Device number.  Will be replaced by a virtual device number.
-           -1))))))
+           -1
+          ;; 12. SELinux context.  Will be extracted in
+          ;; `tramp-convert-file-attributes'.
+          res-context))))))
 
 (defun tramp-do-file-attributes-with-perl (vec localname)
   "Implement `file-attributes' for Tramp files using a Perl script."
@@ -1373,11 +1427,20 @@ Operations not mentioned here will be handled by the 
normal Emacs functions.")
 (defun tramp-do-file-attributes-with-stat (vec localname)
   "Implement `file-attributes' for Tramp files using stat(1) command."
   (tramp-message vec 5 "file attributes with stat: %s" localname)
-  (tramp-maybe-send-script
-   vec tramp-stat-file-attributes "tramp_stat_file_attributes")
-  (tramp-send-command-and-read
-   vec (format "tramp_stat_file_attributes %s"
-              (tramp-shell-quote-argument localname))))
+  (cond
+   ((tramp-remote-selinux-p vec)
+    (tramp-maybe-send-script
+     vec tramp-stat-file-attributes-with-selinux
+     "tramp_stat_file_attributes_with_selinux")
+    (tramp-send-command-and-read
+     vec (format "tramp_stat_file_attributes_with_selinux %s"
+                (tramp-shell-quote-argument localname))))
+   (t
+    (tramp-maybe-send-script
+     vec tramp-stat-file-attributes "tramp_stat_file_attributes")
+    (tramp-send-command-and-read
+     vec (format "tramp_stat_file_attributes %s"
+                (tramp-shell-quote-argument localname))))))
 
 (defun tramp-sh-handle-set-visited-file-modtime (&optional time-list)
   "Like `set-visited-file-modtime' for Tramp files."
@@ -1572,7 +1635,7 @@ ID-FORMAT valid values are `string' and `integer'."
              (tramp-shell-quote-argument localname))))))))
 
 (defun tramp-remote-selinux-p (vec)
-  "Check, whether SELINUX is enabled on the remote host."
+  "Check, whether SELinux is enabled on the remote host."
   (with-tramp-connection-property (tramp-get-process vec) "selinux-p"
     (tramp-send-command-and-check vec "selinuxenabled")))
 
@@ -1775,12 +1838,21 @@ ID-FORMAT valid values are `string' and `integer'."
 (defun tramp-do-directory-files-and-attributes-with-stat (vec localname)
   "Implement `directory-files-and-attributes' for Tramp files with stat(1) 
command."
   (tramp-message vec 5 "directory-files-and-attributes with stat: %s" 
localname)
-  (tramp-maybe-send-script
-   vec tramp-stat-directory-files-and-attributes
-   "tramp_stat_directory_files_and_attributes")
-  (tramp-send-command-and-read
-   vec (format "tramp_stat_directory_files_and_attributes %s"
-              (tramp-shell-quote-argument localname))))
+  (cond
+   ((tramp-remote-selinux-p vec)
+    (tramp-maybe-send-script
+     vec tramp-stat-directory-files-and-attributes-with-selinux
+     "tramp_stat_directory_files_and_attributes_with_selinux")
+    (tramp-send-command-and-read
+     vec (format "tramp_stat_directory_files_and_attributes_with_selinux %s"
+                (tramp-shell-quote-argument localname))))
+   (t
+    (tramp-maybe-send-script
+     vec tramp-stat-directory-files-and-attributes
+     "tramp_stat_directory_files_and_attributes")
+    (tramp-send-command-and-read
+     vec (format "tramp_stat_directory_files_and_attributes %s"
+                (tramp-shell-quote-argument localname))))))
 
 ;; This function should return "foo/" for directories and "bar" for
 ;; files.
@@ -1966,7 +2038,7 @@ OK-IF-ALREADY-EXISTS means don't barf if NEWNAME exists 
already.
 KEEP-DATE means to make sure that NEWNAME has the same timestamp
 as FILENAME.  PRESERVE-UID-GID, when non-nil, instructs to keep
 the uid and gid if both files are on the same host.
-PRESERVE-EXTENDED-ATTRIBUTES activates selinux and acl commands.
+PRESERVE-EXTENDED-ATTRIBUTES activates SELinux and ACL commands.
 
 This function is invoked by `tramp-sh-handle-copy-file' and
 `tramp-sh-handle-rename-file'.  It is an error if OP is neither
diff --git a/lisp/net/tramp-sudoedit.el b/lisp/net/tramp-sudoedit.el
index 2ce2647b5ac..3d6e1d92d0b 100644
--- a/lisp/net/tramp-sudoedit.el
+++ b/lisp/net/tramp-sudoedit.el
@@ -234,7 +234,7 @@ OK-IF-ALREADY-EXISTS means don't barf if NEWNAME exists 
already.
 KEEP-DATE means to make sure that NEWNAME has the same timestamp
 as FILENAME.  PRESERVE-UID-GID, when non-nil, instructs to keep
 the uid and gid if both files are on the same host.
-PRESERVE-EXTENDED-ATTRIBUTES activates selinux and acl commands.
+PRESERVE-EXTENDED-ATTRIBUTES activates SELinux and ACL commands.
 
 This function is invoked by `tramp-sudoedit-handle-copy-file' and
 `tramp-sudoedit-handle-rename-file'.  It is an error if OP is
@@ -434,14 +434,37 @@ the result will be a local, non-Tramp, file name."
   "stat format string to produce output suitable for use with
 `file-attributes' on the remote file system.")
 
+(defconst tramp-sudoedit-file-attributes-with-selinux
+  (format
+   ;; Apostrophes in the stat output are masked as
+   ;; `tramp-stat-marker', in order to make a proper shell escape of
+   ;; them in file names.  They are replaced in
+   ;; `tramp-sudoedit-send-command-and-read'.
+   (concat "((%s%%N%s) %%h (%s%%U%s . %%u) (%s%%G%s . %%g)"
+          " %%X %%Y %%Z %%s %s%%A%s t %%i -1 %s%%C%s)")
+   tramp-stat-marker tramp-stat-marker  ; %%N
+   tramp-stat-marker tramp-stat-marker  ; %%U
+   tramp-stat-marker tramp-stat-marker  ; %%G
+   tramp-stat-marker tramp-stat-marker  ; %%A
+   tramp-stat-marker tramp-stat-marker) ; %%C
+  "stat format string to produce output suitable for use with
+`file-attributes' on the remote file system, including SELinux context.")
+
 (defun tramp-sudoedit-handle-file-attributes (filename &optional id-format)
   "Like `file-attributes' for Tramp files."
   ;; The result is cached in `tramp-convert-file-attributes'.
   (with-parsed-tramp-file-name (expand-file-name filename) nil
     (tramp-convert-file-attributes v localname id-format
-      (tramp-sudoedit-send-command-and-read
-       v "env" "QUOTING_STYLE=locale" "stat" "-c"
-       tramp-sudoedit-file-attributes (file-name-unquote localname)))))
+      (cond
+       ((tramp-sudoedit-remote-selinux-p v)
+       (tramp-sudoedit-send-command-and-read
+        v "env" "QUOTING_STYLE=locale" "stat" "-c"
+        tramp-sudoedit-file-attributes-with-selinux
+        (file-name-unquote localname)))
+       (t
+       (tramp-sudoedit-send-command-and-read
+        v "env" "QUOTING_STYLE=locale" "stat" "-c"
+        tramp-sudoedit-file-attributes (file-name-unquote localname)))))))
 
 (defun tramp-sudoedit-handle-file-executable-p (filename)
   "Like `file-executable-p' for Tramp files."
@@ -507,7 +530,7 @@ the result will be a local, non-Tramp, file name."
         v 'file-error "Error while changing file's mode %s" filename)))))
 
 (defun tramp-sudoedit-remote-selinux-p (vec)
-  "Check, whether SELINUX is enabled on the remote host."
+  "Check, whether SELinux is enabled on the remote host."
   (with-tramp-connection-property (tramp-get-process vec) "selinux-p"
     (zerop (tramp-call-process vec "selinuxenabled"))))
 
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index 00b47f6bead..40efea7bbd0 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -87,15 +87,6 @@
 ;;;###autoload (when (featurep 'tramp-compat)
 ;;;###autoload   (load "tramp-compat" 'noerror 'nomessage))
 
-;;; User Customizable Internal Variables:
-
-(defgroup tramp nil
-  "Edit remote files with a combination of ssh, scp, etc."
-  :group 'files
-  :group 'comm
-  :version "22.1"
-  :link '(custom-manual "(tramp)Top"))
-
 ;;;###tramp-autoload
 (progn
   (defvar tramp--startup-hook nil
@@ -105,9 +96,26 @@
 
   (defmacro tramp--with-startup (&rest body)
     "Schedule BODY to be executed at the end of tramp.el."
-    `(add-hook 'tramp--startup-hook (lambda () ,@body))))
+    `(add-hook 'tramp--startup-hook (lambda () ,@body)))
+
+  (eval-and-compile
+    (defalias 'tramp-byte-run--set-suppress-trace
+      #'(lambda (f _args val)
+         (list 'function-put (list 'quote f)
+               ''tramp-suppress-trace val)))
+
+    (add-to-list
+     'defun-declarations-alist
+     (list 'tramp-suppress-trace #'tramp-byte-run--set-suppress-trace))))
+
+;;; User Customizable Internal Variables:
 
-(require 'tramp-loaddefs)
+(defgroup tramp nil
+  "Edit remote files with a combination of ssh, scp, etc."
+  :group 'files
+  :group 'comm
+  :version "22.1"
+  :link '(custom-manual "(tramp)Top"))
 
 ;; Maybe we need once a real Tramp mode, with key bindings etc.
 ;;;###autoload
@@ -140,7 +148,12 @@ This setting has precedence over 
`auto-save-file-name-transforms'."
 ;; Suppress `shell-file-name' for w32 systems.
 (defcustom tramp-encoding-shell
   (let (shell-file-name)
-    (or (tramp-compat-funcall 'w32-shell-name) "/bin/sh"))
+    (or (tramp-compat-funcall 'w32-shell-name)
+        (if (eq system-type 'android)
+            ;; The shell is located at /system/bin/sh on Android
+            ;; systems.
+            "/system/bin/sh"
+          "/bin/sh")))
   "Use this program for encoding and decoding commands on the local host.
 This shell is used to execute the encoding and decoding command on the
 local host, so if you want to use \"~\" in those commands, you should
@@ -1480,6 +1493,7 @@ If LOCALNAME is an absolute file name, set it as 
localname.  If
 LOCALNAME is a relative file name, return `tramp-cache-undefined'.
 Objects returned by this function compare `equal' if they refer to the
 same connection.  Make a copy in order to avoid side effects."
+  ;; (declare (tramp-suppress-trace t))
   (if (and (stringp localname)
           (not (file-name-absolute-p localname)))
       (setq vec tramp-cache-undefined)
@@ -1491,13 +1505,16 @@ same connection.  Make a copy in order to avoid side 
effects."
            (tramp-file-name-hop vec) nil))
     vec))
 
-;; We cannot declare our private symbols in loaddefs.
-(function-put 'tramp-file-name-unify 'tramp-suppress-trace t)
+;; We cannot use the `declare' form for `tramp-suppress-trace' in
+;; autoloaded functions, because the tramp-loaddefs.el generation
+;; would fail.
+(function-put #'tramp-file-name-unify 'tramp-suppress-trace t)
 
 ;; Comparison of file names is performed by `tramp-equal-remote'.
 (defun tramp-file-name-equal-p (vec1 vec2)
   "Check, whether VEC1 and VEC2 denote the same `tramp-file-name'.
 LOCALNAME and HOP do not count."
+  (declare (tramp-suppress-trace t))
   (and (tramp-file-name-p vec1) (tramp-file-name-p vec2)
        (equal (tramp-file-name-unify vec1)
              (tramp-file-name-unify vec2))))
@@ -1526,6 +1543,7 @@ entry does not exist, return nil."
 ;;;###tramp-autoload
 (defun tramp-tramp-file-p (name)
   "Return t if NAME is a string with Tramp file name syntax."
+  ;; (declare (tramp-suppress-trace t))
   (and tramp-mode (stringp name)
        ;; No "/:" and "/c:".  This is not covered by `tramp-file-name-regexp'.
        (not (string-match-p (rx bos "/" (? alpha) ":") name))
@@ -1535,6 +1553,11 @@ entry does not exist, return nil."
        (string-match-p tramp-file-name-regexp name)
        t))
 
+;; We cannot use the `declare' form for `tramp-suppress-trace' in
+;; autoloaded functions, because the tramp-loaddefs.el generation
+;; would fail.
+(function-put #'tramp-tramp-file-p 'tramp-suppress-trace t)
+
 ;; This function bypasses the file name handler approach.  It is NOT
 ;; recommended to use it in any package if not absolutely necessary.
 ;; However, it is more performant than `file-local-name', and might be
@@ -1563,6 +1586,7 @@ of `process-file', `start-file-process', or 
`shell-command'."
   "Return the right method string to use depending on USER and HOST.
 This is METHOD, if non-nil.  Otherwise, do a lookup in
 `tramp-default-method-alist' and `tramp-default-method'."
+  (declare (tramp-suppress-trace t))
   (when (and method
             (or (string-empty-p method)
                 (string-equal method tramp-default-method-marker)))
@@ -1588,6 +1612,7 @@ This is METHOD, if non-nil.  Otherwise, do a lookup in
   "Return the right user string to use depending on METHOD and HOST.
 This is USER, if non-nil.  Otherwise, do a lookup in
 `tramp-default-user-alist' and `tramp-default-user'."
+  (declare (tramp-suppress-trace t))
   (let ((result
         (or user
             (let ((choices tramp-default-user-alist)
@@ -1609,6 +1634,7 @@ This is USER, if non-nil.  Otherwise, do a lookup in
   "Return the right host string to use depending on METHOD and USER.
 This is HOST, if non-nil.  Otherwise, do a lookup in
 `tramp-default-host-alist' and `tramp-default-host'."
+  (declare (tramp-suppress-trace t))
   (let ((result
         (or (and (tramp-compat-length> host 0) host)
             (let ((choices tramp-default-host-alist)
@@ -1635,6 +1661,7 @@ localname (file name on remote host), and hop.
 Unless NODEFAULT is non-nil, method, user and host are expanded
 to their default values.  For the other file name parts, no
 default values are used."
+  ;; (declare (tramp-suppress-trace t))
   (save-match-data
     (unless (tramp-tramp-file-p name)
       (tramp-user-error nil "Not a Tramp file name: \"%s\"" name))
@@ -1691,8 +1718,10 @@ default values are used."
            (tramp-user-error
             v "Method `%s' is not supported for multi-hops" method)))))))
 
-;; We cannot declare our private symbols in loaddefs.
-(function-put 'tramp-dissect-file-name 'tramp-suppress-trace t)
+;; We cannot use the `declare' form for `tramp-suppress-trace' in
+;; autoloaded functions, because the tramp-loaddefs.el generation
+;; would fail.
+(function-put #'tramp-dissect-file-name 'tramp-suppress-trace t)
 
 ;;;###tramp-autoload
 (defun tramp-ensure-dissected-file-name (vec-or-filename)
@@ -1700,13 +1729,16 @@ default values are used."
 
 VEC-OR-FILENAME may be either a string or a `tramp-file-name'.
 If it's not a Tramp filename, return nil."
+  ;; (declare (tramp-suppress-trace t))
   (cond
    ((tramp-file-name-p vec-or-filename) vec-or-filename)
    ((tramp-tramp-file-p vec-or-filename)
     (tramp-dissect-file-name vec-or-filename))))
 
-;; We cannot declare our private symbols in loaddefs.
-(function-put 'tramp-ensure-dissected-file-name 'tramp-suppress-trace t)
+;; We cannot use the `declare' form for `tramp-suppress-trace' in
+;; autoloaded functions, because the tramp-loaddefs.el generation
+;; would fail.
+(function-put #'tramp-ensure-dissected-file-name 'tramp-suppress-trace t)
 
 (defun tramp-dissect-hop-name (name &optional nodefault)
   "Return a `tramp-file-name' structure of `hop' part of NAME.
@@ -1733,6 +1765,7 @@ See `tramp-dissect-file-name' for details."
 
 (defun tramp-buffer-name (vec)
   "A name for the connection buffer VEC."
+  (declare (tramp-suppress-trace t))
   (let ((method (tramp-file-name-method vec))
        (user-domain (tramp-file-name-user-domain vec))
        (host-port (tramp-file-name-host-port vec)))
@@ -3499,29 +3532,29 @@ BODY is the backend specific code."
 
             ;; Set the ownership.
              (when need-chown
-               (tramp-set-file-uid-gid filename uid gid)))
-
-          ;; Set extended attributes.  We ignore possible errors,
-          ;; because ACL strings could be incompatible.
-          (when attributes
-            (ignore-errors
-              (set-file-extended-attributes filename attributes)))
-
-          ;; Unlock file.
-          (when file-locked
-            ;; `unlock-file' exists since Emacs 28.1.
-            (tramp-compat-funcall 'unlock-file lockname))
-
-          ;; Sanity check.
-          (unless (equal curbuf (current-buffer))
-            (tramp-error
-             v 'file-error
-             "Buffer has changed from `%s' to `%s'" curbuf (current-buffer)))
-
-          (when (and (null noninteractive)
-                     (or (eq ,visit t) (string-or-null-p ,visit)))
-            (tramp-message v 0 "Wrote %s" filename))
-          (run-hooks 'tramp-handle-write-region-hook))))))
+               (tramp-set-file-uid-gid filename uid gid))
+
+            ;; Set extended attributes.  We ignore possible errors,
+            ;; because ACL strings could be incompatible.
+            (when attributes
+              (ignore-errors
+                (set-file-extended-attributes filename attributes)))
+
+            ;; Unlock file.
+            (when file-locked
+              ;; `unlock-file' exists since Emacs 28.1.
+              (tramp-compat-funcall 'unlock-file lockname))
+
+            ;; Sanity check.
+            (unless (equal curbuf (current-buffer))
+              (tramp-error
+               v 'file-error
+               "Buffer has changed from `%s' to `%s'" curbuf (current-buffer)))
+
+            (when (and (null noninteractive)
+                       (or (eq ,visit t) (string-or-null-p ,visit)))
+              (tramp-message v 0 "Wrote %s" filename))
+            (run-hooks 'tramp-handle-write-region-hook)))))))
 
 ;;; Common file name handler functions for different backends:
 
@@ -6027,6 +6060,13 @@ to cache the result.  Return the modified ATTR."
                 ;; Set virtual device number.
                 (setcar (nthcdr 11 attr)
                         (tramp-get-device ,vec))
+                ;; Set SELinux context.
+                (when (stringp (nth 12 attr))
+                  (tramp-set-file-property
+                   ,vec ,localname  "file-selinux-context"
+                   (split-string (nth 12 attr) ":" 'omit)))
+                ;; Remove optional entries.
+                (setcdr (nthcdr 11 attr) nil)
                 attr)))))
 
        ;; Return normalized result.
@@ -6166,6 +6206,7 @@ Return the local name of the temporary file."
 
 (defun tramp-delete-temp-file-function ()
   "Remove temporary files related to current buffer."
+  (declare (tramp-suppress-trace t))
   (when (stringp tramp-temp-buffer-file-name)
     (ignore-errors (delete-file tramp-temp-buffer-file-name))))
 
@@ -6458,6 +6499,7 @@ Consults the auth-source package."
 (defun tramp-time-diff (t1 t2)
   "Return the difference between the two times, in seconds.
 T1 and T2 are time values (as returned by `current-time' for example)."
+  (declare (tramp-suppress-trace t))
   (float-time (time-subtract t1 t2)))
 
 (defun tramp-unquote-shell-quote-argument (s)
diff --git a/lisp/nxml/nxml-maint.el b/lisp/nxml/nxml-maint.el
index 24b38ed0635..24eb711eb99 100644
--- a/lisp/nxml/nxml-maint.el
+++ b/lisp/nxml/nxml-maint.el
@@ -26,7 +26,7 @@
 
 ;;; Parsing target repertoire files from ucs-fonts.
 ;; This is for converting the TARGET? files in
-;; http://www.cl.cam.ac.uk/~mgk25/download/ucs-fonts.tar.gz
+;; https://www.cl.cam.ac.uk/~mgk25/download/ucs-fonts.tar.gz
 ;; into a glyph set.
 
 (defun nxml-insert-target-repertoire-glyph-set (file var)
diff --git a/lisp/nxml/rng-util.el b/lisp/nxml/rng-util.el
index 27c924d960f..4f49885fc45 100644
--- a/lisp/nxml/rng-util.el
+++ b/lisp/nxml/rng-util.el
@@ -24,6 +24,8 @@
 
 ;;; Code:
 
+(require 'cl-lib)
+
 (defun rng-make-datatypes-uri (uri)
   (if (string-equal uri "")
       ;; The spec doesn't say to do this, but it's perfectly conformant
@@ -39,26 +41,7 @@
 (defun rng-substq (new old list)
   "Replace first member of LIST (if any) that is `eq' to OLD by NEW.
 LIST is not modified."
-  (cond ((null list) nil)
-       ((eq (car list) old)
-        (cons new (cdr list)))
-       (t
-        (let ((tail (cons (car list)
-                          nil))
-              (rest (cdr list)))
-          (setq list tail)
-          (while rest
-            (let ((item (car rest)))
-              (setq rest (cdr rest))
-              (cond ((eq item old)
-                     (setcdr tail
-                             (cons new rest))
-                     (setq rest nil))
-                    (t
-                     (setq tail
-                           (setcdr tail
-                                   (cons item nil))))))))
-        list)))
+  (cl-substitute new old list :count 1 :test #'eq))
 
 (defun rng-escape-string (s)
   (replace-regexp-in-string "[&\"<>]"
diff --git a/lisp/obsolete/iswitchb.el b/lisp/obsolete/iswitchb.el
index 6d316fdf1cc..59e6c0403a2 100644
--- a/lisp/obsolete/iswitchb.el
+++ b/lisp/obsolete/iswitchb.el
@@ -251,7 +251,7 @@
   :group 'convenience
   :group 'completion
   :link '(emacs-commentary-link :tag "Commentary" "iswitchb.el")
-  :link '(url-link "http://www.anc.ed.ac.uk/~stephen/emacs/";)
+  :link '(url-link "https://www.anc.ed.ac.uk/~stephen/emacs/";)
   :link '(emacs-library-link :tag "Lisp File" "iswitchb.el"))
 
 (defcustom iswitchb-case case-fold-search
diff --git a/lisp/obsolete/terminal.el b/lisp/obsolete/terminal.el
index 4a2ef680b09..4e23fc3c710 100644
--- a/lisp/obsolete/terminal.el
+++ b/lisp/obsolete/terminal.el
@@ -1095,7 +1095,9 @@ subprocess started."
                    (or explicit-shell-file-name
                        (getenv "ESHELL")
                        (getenv "SHELL")
-                       "/bin/sh"))
+                       (if (eq system-type 'android)
+                           "/system/bin/sh"
+                         "/bin/sh")))
                  (s (read-string
                      (format "Run program in emulator (default %s): "
                              default-s))))
diff --git a/lisp/org/org-ctags.el b/lisp/org/org-ctags.el
index 5dd2bfd59cd..990214f4117 100644
--- a/lisp/org/org-ctags.el
+++ b/lisp/org/org-ctags.el
@@ -156,7 +156,9 @@ Format is: /REGEXP/TAGNAME/FLAGS,TAGTYPE/
 See the ctags documentation for more information.")
 
 (defcustom org-ctags-path-to-ctags
-  (if (executable-find "ctags-exuberant") "ctags-exuberant" "ctags")
+  (if (executable-find "ctags-exuberant")
+      "ctags-exuberant"
+    ctags-program-name)
   "Name of the ctags executable file."
   :version "24.1"
   :type 'file)
diff --git a/lisp/pcomplete.el b/lisp/pcomplete.el
index 36f68f1af57..c7ec228c1db 100644
--- a/lisp/pcomplete.el
+++ b/lisp/pcomplete.el
@@ -1318,10 +1318,10 @@ If specific documentation can't be given, be generic."
 
 ;; general utilities
 
-(defun pcomplete-uniquify-list (l)
-  "Sort and remove multiples in L."
-  (setq l (sort l #'string-lessp))
-  (seq-uniq l))
+(defun pcomplete-uniquify-list (sequence)
+  "Sort and remove multiples in SEQUENCE.
+Sequence should be a vector or list of strings."
+  (sort (seq-uniq sequence) #'string-lessp))
 (define-obsolete-function-alias 'pcomplete-uniqify-list 
#'pcomplete-uniquify-list "27.1")
 
 (defun pcomplete-process-result (cmd &rest args)
diff --git a/lisp/pixel-scroll.el b/lisp/pixel-scroll.el
index 5f412bf418a..ac867a1351c 100644
--- a/lisp/pixel-scroll.el
+++ b/lisp/pixel-scroll.el
@@ -504,6 +504,7 @@ Otherwise, redisplay will reset the window's vscroll."
   (set-window-start nil (pixel-point-at-unseen-line) t)
   (set-window-vscroll nil vscroll t))
 
+;;;###autoload
 (defun pixel-scroll-precision-scroll-down-page (delta)
   "Scroll the current window down by DELTA pixels.
 Note that this function doesn't work if DELTA is larger than
@@ -560,6 +561,7 @@ the height of the current window."
       (setq delta (- delta max-height)))
     (pixel-scroll-precision-scroll-down-page delta)))
 
+;;;###autoload
 (defun pixel-scroll-precision-scroll-up-page (delta)
   "Scroll the current window up by DELTA pixels.
 Note that this function doesn't work if DELTA is larger than
diff --git a/lisp/play/doctor.el b/lisp/play/doctor.el
index dcf36c5c330..891274448d3 100644
--- a/lisp/play/doctor.el
+++ b/lisp/play/doctor.el
@@ -129,6 +129,9 @@
   "C-j" #'doctor-read-print
   "RET" #'doctor-ret-or-read)
 
+;; Actually defined in textconv.c.
+(defvar text-conversion-style)
+
 (define-derived-mode doctor-mode text-mode "Doctor"
   "Major mode for running the Doctor (Eliza) program.
 Like Text mode with Auto Fill mode
@@ -137,6 +140,8 @@ reads the sentence before point, and prints the Doctor's 
answer."
   :interactive nil
   (doctor-make-variables)
   (turn-on-auto-fill)
+  ;; Make sure RET is processed by Emacs.
+  (setq text-conversion-style 'action)
   (doctor-type '(i am the psychotherapist \.
                 (doc$ doctor--please) (doc$ doctor--describe) your (doc$ 
doctor--problems) \.
                 each time you are finished talking\, type \R\E\T twice \.))
diff --git a/lisp/play/dunnet.el b/lisp/play/dunnet.el
index 837508779e7..e290a9d73ec 100644
--- a/lisp/play/dunnet.el
+++ b/lisp/play/dunnet.el
@@ -1132,9 +1132,14 @@ treasures for points?" "4" "four")
 
 ;;;; Mode definitions for interactive mode
 
+;; Actually defined in textconv.c.
+(defvar text-conversion-style)
+
 (define-derived-mode dun-mode text-mode "Dungeon"
   "Major mode for running dunnet."
   :interactive nil
+  ;; Make sure RET is processed by Emacs.
+  (setq text-conversion-style 'action)
   (setq-local scroll-step 2))
 
 (defun dun-parse (_arg)
diff --git a/lisp/play/gamegrid.el b/lisp/play/gamegrid.el
index 54df983740e..bf195d6a0ec 100644
--- a/lisp/play/gamegrid.el
+++ b/lisp/play/gamegrid.el
@@ -411,7 +411,9 @@ convert to an Emacs image-spec instead")
                pixel-size (floor (* resy (/ point-size 72.27)))
                point-size (* (/ pixel-size resy) 72.27))
          (face-spec-set gamegrid-face
-                        `((t :height ,(floor (* point-size 10))))))))))
+                         ;; With some very high resolution displays,
+                         ;; point-size floored can be zero.
+                        `((t :height ,(max 8 (floor (* point-size 10)))))))))))
 
 (defun gamegrid-initialize-display ()
   (setq gamegrid-display-mode (gamegrid-display-type))
diff --git a/lisp/progmodes/cc-cmds.el b/lisp/progmodes/cc-cmds.el
index 4c2340bfc2c..15b103a081f 100644
--- a/lisp/progmodes/cc-cmds.el
+++ b/lisp/progmodes/cc-cmds.el
@@ -5145,6 +5145,41 @@ details."
       (delete-char 1))))
 
 
+
+;; Text conversion support.
+
+(defun c-post-text-conversion ()
+  "Notice that the character `last-command-event' has been inserted.
+If said character is an electric character such as `*' or `{', delete
+it, then call the appropriate CC Mode function to electrically insert
+it again."
+  (cond ((eq last-command-event ?#)
+        (delete-char -1)
+        (c-electric-pound nil) t)
+       ((memq last-command-event '(?{ ?}))
+        (delete-char -1)
+        (c-electric-brace nil) t)
+       ((memq last-command-event '(?\( ?\)))
+        (delete-char -1)
+        (c-electric-paren nil) t)
+       ((eq last-command-event ?*)
+        (delete-char -1)
+        (c-electric-star nil) t)
+       ((eq last-command-event ?/)
+        (delete-char -1)
+        (c-electric-slash nil) t)
+       ((memq last-command-event '(?\; ?,))
+        (delete-char -1)
+        (c-electric-semi&comma nil) t)
+       ((eq last-command-event ?:)
+        (delete-char -1)
+        (c-electric-colon nil) t)
+       ((memq last-command-event '(?> ?<))
+        (delete-char -1)
+        (c-electric-lt-gt nil) t)))
+
+
+
 (cc-provide 'cc-cmds)
 
 ;; Local Variables:
diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el
index dd699b9a119..1dbe91b5633 100644
--- a/lisp/progmodes/cc-mode.el
+++ b/lisp/progmodes/cc-mode.el
@@ -255,6 +255,10 @@ control).  See \"cc-mode.el\" for more info."
        ;; Will try initialization hooks again if they failed.
        (put 'c-initialize-cc-mode initprop c-initialization-ok))))
 
+  ;; Set up text conversion, for Emacs >= 30.0
+  (when (boundp 'post-text-conversion-hook)
+    (add-hook 'post-text-conversion-hook #'c-post-text-conversion))
+
   (unless new-style-init
     (c-init-language-vars-for 'c-mode)))
 
@@ -2723,18 +2727,18 @@ This function is called from `c-common-init', once per 
mode initialization."
 ;; Emacs < 22 and XEmacs
 (defmacro c-advise-fl-for-region (function)
   (declare (debug t))
-  `(defadvice ,function (before get-awk-region activate)
-     ;; Make sure that any string/regexp is completely font-locked.
-     (when c-buffer-is-cc-mode
-       (save-excursion
-        (ad-set-arg 1 c-new-END)   ; end
-        (ad-set-arg 0 c-new-BEG)))))   ; beg
-
-(unless (boundp 'font-lock-extend-after-change-region-function)
-  (c-advise-fl-for-region font-lock-after-change-function)
-  (c-advise-fl-for-region jit-lock-after-change)
-  (c-advise-fl-for-region lazy-lock-defer-rest-after-change)
-  (c-advise-fl-for-region lazy-lock-defer-line-after-change))
+  (unless (boundp 'font-lock-extend-after-change-region-function)
+    `(defadvice ,function (before get-awk-region activate)
+       ;; Make sure that any string/regexp is completely font-locked.
+       (when c-buffer-is-cc-mode
+        (save-excursion
+          (ad-set-arg 1 c-new-END)   ; end
+          (ad-set-arg 0 c-new-BEG))))))        ; beg
+
+(c-advise-fl-for-region font-lock-after-change-function)
+(c-advise-fl-for-region jit-lock-after-change)
+(c-advise-fl-for-region lazy-lock-defer-rest-after-change)
+(c-advise-fl-for-region lazy-lock-defer-line-after-change)
 
 ;; Connect up to `electric-indent-mode' (Emacs 24.4 and later).
 (defun c-electric-indent-mode-hook ()
diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el
index 5dc49e4ebb4..75ea81d55a4 100644
--- a/lisp/progmodes/cperl-mode.el
+++ b/lisp/progmodes/cperl-mode.el
@@ -5875,6 +5875,14 @@ default function."
     cperl-here-face)
    (t (funcall (default-value 'font-lock-syntactic-face-function) state))))
 
+(defface cperl-method-call
+  '((t (:inherit 'default )))
+  "Font Lock mode face for method calls.
+Usually, method calls are not fontified.
+We use this face to prevent calls to methods which look like
+builtin functions to be fontified like, well, builtin
+functions (which they are not).  Inherits from `default'.")
+
 (defun cperl-init-faces ()
   (condition-case errs
       (progn
@@ -5885,8 +5893,59 @@ default function."
             ;; -------- trailing spaces -> use invalid-face as a warning
             ;; (matcher subexp facespec)
            `("[ \t]+$" 0 ',cperl-invalid-face t)
+            ;; -------- function definition _and_ declaration
+            ;; (matcher (subexp facespec))
+            ;; facespec is evaluated depending on whether the
+            ;; statement ends in a "{" (definition) or ";"
+            ;; (declaration without body)
+           (list (concat "\\<" cperl-sub-regexp
+                          ;; group 1: optional subroutine name
+                          (rx
+                           (sequence (eval cperl--ws+-rx)
+                                     (group (optional
+                                             (eval 
cperl--normal-identifier-rx)))))
+                          ;; "fontified" elsewhere: Prototype
+                          (rx (optional
+                               (sequence (eval cperl--ws*-rx)
+                                         (eval cperl--prototype-rx))))
+                          ;; fontified elsewhere: Attributes
+                          (rx (optional (sequence (eval cperl--ws*-rx)
+                                                  (eval 
cperl--attribute-list-rx))))
+                          (rx (eval cperl--ws*-rx))
+                          ;; group 2: Identifies the start of the anchor
+                          (rx (group
+                               (or (group-n 3 ";") ; Either a declaration...
+                                   "{"             ; ... or a code block
+                                   ;; ... or a complete signature
+                                   (sequence (eval cperl--signature-rx)
+                                             (eval cperl--ws*-rx))
+                                   ;; ... or the start of a "sloppy" signature
+                                   (sequence (eval cperl--sloppy-signature-rx)
+                                             ;; arbtrarily continue "a few 
lines"
+                                             (repeat 0 200 (not (in "{"))))
+                                   ;; make sure we have a reasonably
+                                   ;; short match for an incomplete sub
+                                   (not (in ";{("))
+                                   buffer-end))))
+                 '(1 (if (match-beginning 3)
+                         'font-lock-variable-name-face
+                       'font-lock-function-name-face)
+                      nil ; override
+                      t)  ; laxmatch in case of anonymous subroutines
+                  ;; -------- anchored: Signature
+                  `(,(rx (sequence (in "(,")
+                                   (eval cperl--ws*-rx)
+                                   (group (eval cperl--basic-variable-rx))))
+                    (progn
+                      (goto-char (match-beginning 2)) ; pre-match: Back to sig
+                      (match-end 2))
+                    nil
+                    (1 font-lock-variable-name-face)))
             ;; -------- flow control
             ;; (matcher . subexp) font-lock-keyword-face by default
+           ;; This highlights declarations and definitions differently.
+           ;; We do not try to highlight in the case of attributes:
+           ;; it is already done by `cperl-find-pods-heres'
            (cons
             (concat
              "\\(^\\|[^$@%&\\]\\)\\<\\("
@@ -5910,6 +5969,11 @@ default function."
              "\\)\\>") 2)              ; was "\\)[ \n\t;():,|&]"
                                        ; In what follows we use `type' style
                                        ; for overwritable builtins
+            ;; -------- avoid method calls being fontified as keywords
+            ;; (matcher (subexp facespec))
+            (list
+             (rx "->" (* space) (group-n 1(eval cperl--basic-identifier-rx)))
+             1 ''cperl-method-call)
             ;; -------- builtin functions
             ;; (matcher subexp facespec)
            (list
@@ -5982,57 +6046,6 @@ default function."
             ;; (matcher subexp facespec)
            '("-[rwxoRWXOezsfdlpSbctugkTBMAC]\\>\\([ \t]+_\\>\\)?" 0
              font-lock-function-name-face keep) ; Not very good, triggers at 
"[a-z]"
-           ;; This highlights declarations and definitions differently.
-           ;; We do not try to highlight in the case of attributes:
-           ;; it is already done by `cperl-find-pods-heres'
-            ;; -------- function definition _and_ declaration
-            ;; (matcher (subexp facespec))
-            ;; facespec is evaluated depending on whether the
-            ;; statement ends in a "{" (definition) or ";"
-            ;; (declaration without body)
-           (list (concat "\\<" cperl-sub-regexp
-                          ;; group 1: optional subroutine name
-                          (rx
-                           (sequence (eval cperl--ws+-rx)
-                                     (group (optional
-                                             (eval 
cperl--normal-identifier-rx)))))
-                          ;; "fontified" elsewhere: Prototype
-                          (rx (optional
-                               (sequence (eval cperl--ws*-rx)
-                                         (eval cperl--prototype-rx))))
-                          ;; fontified elsewhere: Attributes
-                          (rx (optional (sequence (eval cperl--ws*-rx)
-                                                  (eval 
cperl--attribute-list-rx))))
-                          (rx (eval cperl--ws*-rx))
-                          ;; group 2: Identifies the start of the anchor
-                          (rx (group
-                               (or (group-n 3 ";") ; Either a declaration...
-                                   "{"             ; ... or a code block
-                                   ;; ... or a complete signature
-                                   (sequence (eval cperl--signature-rx)
-                                             (eval cperl--ws*-rx))
-                                   ;; ... or the start of a "sloppy" signature
-                                   (sequence (eval cperl--sloppy-signature-rx)
-                                             ;; arbtrarily continue "a few 
lines"
-                                             (repeat 0 200 (not (in "{"))))
-                                   ;; make sure we have a reasonably
-                                   ;; short match for an incomplete sub
-                                   (not (in ";{("))
-                                   buffer-end))))
-                 '(1 (if (match-beginning 3)
-                         'font-lock-variable-name-face
-                       'font-lock-function-name-face)
-                      t  ;; override
-                      t) ;; laxmatch in case of anonymous subroutines
-                  ;; -------- anchored: Signature
-                  `(,(rx (sequence (in "(,")
-                                   (eval cperl--ws*-rx)
-                                   (group (eval cperl--basic-variable-rx))))
-                    (progn
-                      (goto-char (match-beginning 2)) ; pre-match: Back to sig
-                      (match-end 2))
-                    nil
-                    (1 font-lock-variable-name-face)))
             ;; -------- various stuff calling for a package name
             ;; (matcher (subexp facespec) (subexp facespec))
             `(,(rx (sequence
@@ -6795,7 +6808,7 @@ in subdirectories too."
   ;; of etags has been commented out in the menu since ... well,
   ;; forever.  So, let's just stick to ASCII here. -- haj, 2021-09-14
   (interactive)
-  (let ((cmd "etags")
+  (let ((cmd etags-program-name)
        (args `("-l" "none" "-r"
                ;;                        1=fullname  2=package?             
3=name                       4=proto?             5=attrs? (VERY APPROX!)
                ,(concat
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index ae31985a676..37875e3d7f1 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -421,6 +421,14 @@ This can be useful when using docker to run a language 
server.")
 
 ;;; Constants
 ;;;
+(defconst eglot--version
+  (eval-when-compile
+    (when byte-compile-current-file
+      (require 'lisp-mnt)
+      (lm-version byte-compile-current-file)))
+  "The version as a string of this version of Eglot.
+It is nil if Eglot is not byte-complied.")
+
 (defconst eglot--symbol-kind-names
   `((1 . "File") (2 . "Module")
     (3 . "Namespace") (4 . "Package") (5 . "Class")
@@ -1352,7 +1360,9 @@ This docstring appeases checkdoc, that's all."
                                         (eq (jsonrpc-process-type server)
                                             'network))
                               (emacs-pid))
-                            :clientInfo '(:name "Eglot")
+                            :clientInfo
+                            `(:name "Eglot" ,@(when eglot--version
+                                                `(:version ,eglot--version)))
                             ;; Maybe turn trampy `/ssh:foo@bar:/path/to/baz.py'
                             ;; into `/path/to/baz.py', so LSP groks it.
                             :rootPath (file-local-name
diff --git a/lisp/progmodes/prog-mode.el b/lisp/progmodes/prog-mode.el
index a434c7e9058..16497097061 100644
--- a/lisp/progmodes/prog-mode.el
+++ b/lisp/progmodes/prog-mode.el
@@ -337,6 +337,8 @@ support it."
   (setq-local require-final-newline mode-require-final-newline)
   (setq-local parse-sexp-ignore-comments t)
   (add-hook 'context-menu-functions 'prog-context-menu 10 t)
+  ;; Enable text conversion in this buffer.
+  (setq-local text-conversion-style t)
   ;; Any programming language is always written left to right.
   (setq bidi-paragraph-direction 'left-to-right))
 
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index eac6a60f5bf..2c5b07ce750 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -24,11 +24,6 @@
 
 ;;; Commentary:
 
-;; NOTE: The project API is still experimental and can change in major,
-;; backward-incompatible ways.  Everyone is encouraged to try it, and
-;; report to us any problems or use cases we hadn't anticipated, by
-;; sending an email to emacs-devel, or `M-x report-emacs-bug'.
-;;
 ;; This file contains generic infrastructure for dealing with
 ;; projects, some utility functions, and commands using that
 ;; infrastructure.
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 52e5a36f4b0..1930f68617c 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -5214,11 +5214,13 @@ def __FFAP_get_module_path(objstr):
 
 (defcustom python-check-command
   (cond ((executable-find "pyflakes") "pyflakes")
+        ((executable-find "ruff") "ruff")
+        ((executable-find "flake8") "flake8")
         ((executable-find "epylint") "epylint")
         (t "pyflakes"))
   "Command used to check a Python file."
   :type 'string
-  :version "29.1")
+  :version "30.1")
 
 (defcustom python-check-buffer-name
   "*Python check: %s*"
diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el
index e441ffbbfe3..9d80bbd72dd 100644
--- a/lisp/progmodes/ruby-mode.el
+++ b/lisp/progmodes/ruby-mode.el
@@ -2697,18 +2697,18 @@ Currently there are `ruby-mode' and `ruby-ts-mode'."
 ;;;###autoload
 (add-to-list 'auto-mode-alist
              (cons (purecopy (concat "\\(?:\\.\\(?:"
-                                     "rbw?\\|ru\\|rake\\|thor"
+                                     "rbw?\\|ru\\|rake\\|thor\\|axlsx"
                                      "\\|jbuilder\\|rabl\\|gemspec\\|podspec"
                                      "\\)"
                                      "\\|/"
                                      "\\(?:Gem\\|Rake\\|Cap\\|Thor"
-                                     "\\|Puppet\\|Berks\\|Brew"
+                                     "\\|Puppet\\|Berks\\|Brew\\|Fast"
                                      "\\|Vagrant\\|Guard\\|Pod\\)file"
                                      "\\)\\'"))
                    'ruby-mode))
 
 ;;;###autoload
-(dolist (name (list "ruby" "rbx" "jruby" "ruby1.9" "ruby1.8"))
+(dolist (name (list "ruby" "rbx" "jruby" "j?ruby\\(?:[0-9.]+\\)"))
   (add-to-list 'interpreter-mode-alist (cons (purecopy name) 'ruby-mode)))
 
 (provide 'ruby-mode)
diff --git a/lisp/progmodes/typescript-ts-mode.el 
b/lisp/progmodes/typescript-ts-mode.el
index a4752f7a2ee..3f8e232b71f 100644
--- a/lisp/progmodes/typescript-ts-mode.el
+++ b/lisp/progmodes/typescript-ts-mode.el
@@ -142,7 +142,7 @@ Argument LANGUAGE is either `typescript' or `tsx'."
     "export" "extends" "finally" "for" "from" "function"
     "get" "if" "implements" "import" "in" "instanceof" "interface" "is" "infer"
     "keyof" "let" "namespace" "new" "of" "private" "protected"
-    "public" "readonly" "return" "set" "static" "switch"
+    "public" "readonly" "return" "satisfies" "set" "static" "switch"
     "target" "throw" "try" "type" "typeof" "var" "void"
     "while" "with" "yield")
   "TypeScript keywords for tree-sitter font-locking.")
diff --git a/lisp/saveplace.el b/lisp/saveplace.el
index 18d296ba2d9..a28de6f63a8 100644
--- a/lisp/saveplace.el
+++ b/lisp/saveplace.el
@@ -49,6 +49,17 @@
 Each element looks like (FILENAME . POSITION);
 visiting file FILENAME goes automatically to position POSITION
 rather than the beginning of the buffer.
+A list element can also have the form
+
+   (DIRECTORY (dired-filename . FILENAME))
+
+where DIRECTORY is the name of a directory ending in a slash,
+and FILENAME is the name of a file in that directory.  This
+format is used for saving places in Dired buffers, see the
+function `save-place-dired-hook'; the FILENAME is the file
+where point was located in the Dired listing of DIRECTORY
+when the place in that buffer was recorded.
+
 This alist is saved between Emacs sessions.")
 
 (defcustom save-place-file (locate-user-emacs-file "places" ".emacs-places")
@@ -399,7 +410,8 @@ It runs the hook `save-place-after-find-file-hook'."
 (declare-function dired-goto-file "dired" (file))
 
 (defun save-place-dired-hook ()
-  "Position the point in a Dired buffer."
+  "Position point in a Dired buffer according to its saved place.
+This is run via `dired-initial-position-hook', which see."
   (or save-place-loaded (save-place-load-alist-from-file))
   (when-let ((directory (and (derived-mode-p 'dired-mode)
                              (boundp 'dired-subdir-alist)
diff --git a/lisp/shell.el b/lisp/shell.el
index 0a24b4ea4c2..7ea9d1c2ead 100644
--- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -1374,7 +1374,12 @@ Returns t if successful."
     (while path-dirs
       (setq dir (file-name-as-directory (comint-directory (or (car path-dirs) 
".")))
            comps-in-dir (and (file-accessible-directory-p dir)
-                             (file-name-all-completions filenondir dir)))
+                             (condition-case nil
+                                  (file-name-all-completions filenondir dir)
+                                ;; Systems such as Android sometimes
+                                ;; put inaccessible directories in
+                                ;; PATH.
+                                (permission-denied nil))))
       ;; Go thru each completion found, to see whether it should be used.
       (while comps-in-dir
        (setq file (car comps-in-dir)
diff --git a/lisp/simple.el b/lisp/simple.el
index 6dc08ff0eb0..e21976c30ee 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -4095,10 +4095,11 @@ default values.")
   "Amalgamate undo if necessary.
 This function can be called before an amalgamating command.  It
 removes the previous `undo-boundary' if a series of such calls
-have been made.  By default `self-insert-command' and
-`delete-char' are the only amalgamating commands, although this
-function could be called by any command wishing to have this
-behavior."
+have been made.  `self-insert-command' and `delete-char' are the
+most common amalgamating commands, although this function can be
+called by any command which desires this behavior.
+`analyze-text-conversion' (which see) is also an amalgamating
+command in most circumstances."
   (let ((last-amalgamating-count
          (undo-auto--last-boundary-amalgamating-number)))
     (setq undo-auto--this-command-amalgamating t)
@@ -10281,18 +10282,34 @@ SYMBOL is the name of this modifier, as a symbol.
 LSHIFTBY is the numeric value of this modifier, in keyboard events.
 PREFIX is the string that represents this modifier in an event type symbol."
   (if (numberp event)
-      (cond ((eq symbol 'control)
-            (if (<= 64 (upcase event) 95)
-                (- (upcase event) 64)
-              (logior (ash 1 lshiftby) event)))
-           ((eq symbol 'shift)
-             ;; FIXME: Should we also apply this "upcase" behavior of shift
-             ;; to non-ascii letters?
-            (if (<= ?a (downcase event) ?z)
-                (upcase event)
-              (logior (ash 1 lshiftby) event)))
-           (t
-            (logior (ash 1 lshiftby) event)))
+      ;; Use the base event to determine how the control and shift
+      ;; modifiers should be applied.
+      (let* ((base-event (event-basic-type event)))
+        (cond ((eq symbol 'control)
+              (if (<= 64 (upcase base-event) 95)
+                   ;; Apply the control modifier...
+                  (logior (- (upcase base-event) 64)
+                           ;; ... and any additional modifiers
+                           ;; specified in the original event...
+                           (logand event (logior ?\M-\0 ?\C-\0 ?\S-\0
+                                                ?\H-\0 ?\s-\0 ?\A-\0))
+                           ;; ... including any shift modifier that
+                           ;; `event-basic-type' may have removed.
+                           (if (<= ?A event ?Z) ?\S-\0 0))
+                (logior (ash 1 lshiftby) event)))
+             ((eq symbol 'shift)
+               ;; FIXME: Should we also apply this "upcase" behavior of shift
+               ;; to non-ascii letters?
+              (if (<= ?a base-event ?z)
+                   ;; Apply the Shift modifier.
+                  (logior (upcase base-event)
+                           ;; ... and any additional modifiers
+                           ;; specified in the original event.
+                           (logand event (logior ?\M-\0 ?\C-\0 ?\S-\0
+                                                ?\H-\0 ?\s-\0 ?\A-\0)))
+                (logior (ash 1 lshiftby) event)))
+             (t
+              (logior (ash 1 lshiftby) event))))
     (if (memq symbol (event-modifiers event))
        event
       (let ((event-type (if (symbolp event) event (car event))))
@@ -10565,7 +10582,7 @@ call `normal-erase-is-backspace-mode' (which see) 
instead."
        (if (if (eq normal-erase-is-backspace 'maybe)
                (and (not noninteractive)
                     (or (memq system-type '(ms-dos windows-nt))
-                       (memq window-system '(w32 ns pgtk haiku))
+                       (memq window-system '(w32 ns pgtk haiku android))
                         (and (eq window-system 'x)
                              (fboundp 'x-backspace-delete-keys-p)
                              (x-backspace-delete-keys-p))
@@ -10990,6 +11007,139 @@ If the buffer doesn't exist, create it first."
   "Change value in PLIST of PROP to VAL, comparing with `equal'."
   (declare (obsolete plist-put "29.1"))
   (plist-put plist prop val #'equal))
+
+
+
+;; Text conversion support.  See textconv.c for more details about
+;; what this is.
+
+;; Actually in textconv.c.
+(defvar text-conversion-edits)
+
+;; Actually in elec-pair.el.
+(defvar electric-pair-preserve-balance)
+(declare-function electric-pair-analyze-conversion "elec-pair.el")
+
+;; Actually in emacs-lisp/timer.el.
+(declare-function timer-set-time "emacs-lisp/timer.el")
+
+(defvar-local post-text-conversion-hook nil
+  "Hook run after text is inserted by an input method.
+Each function in this list is run until one returns non-nil.
+When run, `last-command-event' is bound to the last character
+that was inserted by the input method.")
+
+(defun analyze-text-conversion ()
+  "Analyze the results of the previous text conversion event.
+
+For each insertion:
+
+  - Look for the insertion of a string starting or ending with a
+    character inside `auto-fill-chars', and fill the text around
+    it if `auto-fill-mode' is enabled.
+
+  - Look for the insertion of a new line, and cause automatic
+    line breaking of the previous line when `auto-fill-mode' is
+    enabled.
+
+  - Look for the deletion of a single electric pair character,
+    and delete the adjascent pair if
+    `electric-pair-delete-adjacent-pairs'.
+
+  - Run `post-self-insert-functions' for the last character of
+    any inserted text so that modes such as `electric-pair-mode'
+    can work.
+
+  - Run `post-text-conversion-hook' with `last-command-event' set
+    to the last character of any inserted text to finish up.
+
+Finally, amalgamate recent changes to the undo list with previous
+ones, unless a new line has been inserted or auto-fill has taken
+place.  If undo information is being recorded, make sure
+`undo-auto-current-boundary-timer' will run within the next 5
+seconds."
+  (interactive)
+  (let ((any-nonephemeral nil))
+    ;; The list must be processed in reverse.
+    (dolist (edit (reverse text-conversion-edits))
+      ;; Filter out ephemeral edits and deletions after point.  Here, we
+      ;; are only interested in insertions or deletions whose contents
+      ;; can be identified.
+      (when (stringp (nth 3 edit))
+        (with-current-buffer (car edit)
+          (if (not (eq (nth 1 edit) (nth 2 edit)))
+              ;; Process this insertion.  (nth 3 edit) is the text which
+              ;; was inserted.
+              (let* ((inserted (nth 3 edit))
+                     ;; Get the first and last characters.
+                     (start (aref inserted 0))
+                     (end (aref inserted (1- (length inserted))))
+                     ;; Figure out whether or not to auto-fill.
+                     (auto-fill-p (or (aref auto-fill-chars start)
+                                      (aref auto-fill-chars end)))
+                     ;; Figure out whether or not a newline was inserted.
+                     (newline-p (string-search "\n" inserted))
+                     ;; Save the current undo list to figure out
+                     ;; whether or not auto-fill has actually taken
+                     ;; place.
+                     (old-undo-list buffer-undo-list))
+                (save-excursion
+                  (if (and auto-fill-function newline-p)
+                      (progn (goto-char (nth 2 edit))
+                             (previous-logical-line)
+                             (funcall auto-fill-function))
+                    (when (and auto-fill-function auto-fill-p)
+                      (progn (goto-char (nth 2 edit))
+                             (funcall auto-fill-function))))
+                  ;; Record whether or not this edit should result in
+                  ;; an undo boundary being added.
+                  (setq any-nonephemeral
+                        (or any-nonephemeral newline-p
+                            ;; See if auto-fill has taken place by
+                            ;; comparing the current undo list with
+                            ;; the saved head.
+                            (not (eq old-undo-list
+                                     buffer-undo-list)))))
+                (goto-char (nth 2 edit))
+                (let ((last-command-event end))
+                  (unless (run-hook-with-args-until-success
+                           'post-text-conversion-hook)
+                    (run-hooks 'post-self-insert-hook))))
+            ;; Process this deletion before point.  (nth 2 edit) is the
+            ;; text which was deleted.  Input methods typically prefer
+            ;; to edit words instead of deleting characters off their
+            ;; ends, but they seem to always send proper requests for
+            ;; deletion for punctuation.
+            (when (and (boundp 'electric-pair-delete-adjacent-pairs)
+                       (symbol-value 'electric-pair-delete-adjacent-pairs)
+                       ;; Make sure elec-pair is loaded.
+                       (fboundp 'electric-pair-analyze-conversion)
+                       ;; Only do this if only a single edit happened.
+                       text-conversion-edits)
+              (save-excursion
+                (goto-char (nth 2 edit))
+                (electric-pair-analyze-conversion (nth 3 edit))))))))
+    ;; If all edits were ephemeral, make this an amalgamating command.
+    ;; Then, make sure that an undo boundary is placed within the next
+    ;; five seconds.
+    (unless any-nonephemeral
+      (undo-auto-amalgamate)
+      (let ((timer undo-auto-current-boundary-timer))
+        (if timer
+            ;; The timer is already running.  See if it's due to expire
+            ;; within the next five seconds.
+            (let ((time (list (aref timer 1) (aref timer 2)
+                              (aref timer 3))))
+              (unless (<= (time-convert (time-subtract time nil)
+                                        'integer)
+                          5)
+                ;; It's not, so make it run in 5 seconds.
+                (timer-set-time undo-auto-current-boundary-timer
+                                (time-add nil 5))))
+          ;; Otherwise, start it for five seconds from now.
+          (setq undo-auto-current-boundary-timer
+                (run-at-time 5 nil #'undo-auto--boundary-timer)))))))
+
 
 
 (provide 'simple)
diff --git a/lisp/speedbar.el b/lisp/speedbar.el
index 0115a6f4ae4..f56c3915521 100644
--- a/lisp/speedbar.el
+++ b/lisp/speedbar.el
@@ -3531,7 +3531,7 @@ to be at the beginning of a line in the etags buffer.
 
 This variable is ignored if `speedbar-use-imenu-flag' is non-nil.")
 
-(defcustom speedbar-fetch-etags-command "etags"
+(defcustom speedbar-fetch-etags-command etags-program-name
   "Command used to create an etags file.
 This variable is ignored if `speedbar-use-imenu-flag' is t."
   :group 'speedbar
diff --git a/lisp/sqlite-mode.el b/lisp/sqlite-mode.el
index c3047c786f7..8cb94485369 100644
--- a/lisp/sqlite-mode.el
+++ b/lisp/sqlite-mode.el
@@ -126,7 +126,7 @@
         (forward-line 1)
         (if (looking-at " ")
             ;; Delete the info.
-            (delete-region (point) (if (re-search-forward "^[^ ]" nil t)
+            (delete-region (point) (if (re-search-forward "^[^ \t]" nil t)
                                        (match-beginning 0)
                                      (point-max)))
           ;; Insert the info.
diff --git a/lisp/startup.el b/lisp/startup.el
index 7f601668369..43d6bf7fd59 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -553,11 +553,24 @@ the updated value."
     (setq startup--original-eln-load-path
           (copy-sequence native-comp-eln-load-path))))
 
+(defvar android-fonts-enumerated nil
+  "Whether or not fonts have been enumerated already.
+On Android, Emacs uses this variable internally at startup.")
+
 (defun normal-top-level ()
   "Emacs calls this function when it first starts up.
 It sets `command-line-processed', processes the command-line,
 reads the initialization files, etc.
 It is the default value of the variable `top-level'."
+  ;; Initialize the Android font driver late.
+  ;; This is done here because it needs the `mac-roman' coding system
+  ;; to be loaded.
+  (when (and (featurep 'android)
+             (fboundp 'android-enumerate-fonts)
+             (not android-fonts-enumerated))
+    (funcall 'android-enumerate-fonts)
+    (setq android-fonts-enumerated t))
+
   (if command-line-processed
       (message internal--top-level-message)
     (setq command-line-processed t)
diff --git a/lisp/subr.el b/lisp/subr.el
index 9949bf8d729..58ec642dd92 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -1675,7 +1675,13 @@ nil or (STRING . POSITION)'.
 
 For more information, see Info node `(elisp)Click Events'."
   (declare (side-effect-free t))
-  (or (and (consp event) (nth 1 event))
+  (or (and (consp event)
+           ;; Ignore touchscreen events.  They store the posn in a
+           ;; different format, and can have multiple posns.
+           (not (memq (car event) '(touchscreen-begin
+                                    touchscreen-update
+                                    touchscreen-end)))
+           (nth 1 event))
       (event--posn-at-point)))
 
 (defun event-end (event)
@@ -1684,7 +1690,11 @@ EVENT should be a click, drag, or key press event.
 
 See `event-start' for a description of the value returned."
   (declare (side-effect-free t))
-  (or (and (consp event) (nth (if (consp (nth 2 event)) 2 1) event))
+  (or (and (consp event)
+           (not (memq (car event) '(touchscreen-begin
+                                    touchscreen-update
+                                    touchscreen-end)))
+           (nth (if (consp (nth 2 event)) 2 1) event))
       (event--posn-at-point)))
 
 (defsubst event-click-count (event)
@@ -3087,6 +3097,11 @@ So escape sequences and keyboard encoding are taken into 
account.
 When there's an ambiguity because the key looks like the prefix of
 some sort of escape sequence, the ambiguity is resolved via `read-key-delay'.
 
+Also in contrast to `read-event', input method text conversion
+will be disabled while the key sequence is read, so that
+character input events will always be generated for keyboard
+input.
+
 If the optional argument PROMPT is non-nil, display that as a
 prompt.
 
@@ -3145,7 +3160,8 @@ only unbound fallback disabled is downcasing of the last 
event."
                   (lookup-key global-map [tool-bar])))
              map))
           (let* ((keys
-                  (catch 'read-key (read-key-sequence-vector prompt nil t)))
+                  (catch 'read-key (read-key-sequence-vector prompt nil t
+                                                             nil nil t)))
                  (key (aref keys 0)))
             (if (and (> (length keys) 1)
                      (memq key '(mode-line header-line
@@ -3331,6 +3347,8 @@ causes it to evaluate `help-form' and display the result."
        (while (not done)
          (unless (get-text-property 0 'face prompt)
            (setq prompt (propertize prompt 'face 'minibuffer-prompt)))
+          ;; Display the on screen keyboard if it exists.
+          (frame-toggle-on-screen-keyboard (selected-frame) nil)
          (setq char (let ((inhibit-quit inhibit-keyboard-quit))
                       (read-key prompt)))
          (and show-help (buffer-live-p (get-buffer helpbuf))
@@ -3491,6 +3509,9 @@ an error message."
     (minibuffer-message "Wrong answer")
     (sit-for 2)))
 
+;; Defined in textconv.c.
+(defvar overriding-text-conversion-style)
+
 (defun read-char-from-minibuffer (prompt &optional chars history)
   "Read a character from the minibuffer, prompting for it with PROMPT.
 Like `read-char', but uses the minibuffer to read and return a character.
@@ -3505,7 +3526,15 @@ while calling this function, then pressing `help-char'
 causes it to evaluate `help-form' and display the result.
 There is no need to explicitly add `help-char' to CHARS;
 `help-char' is bound automatically to `help-form-show'."
-  (let* ((map (if (consp chars)
+
+  ;; If text conversion is enabled in this buffer, then it will only
+  ;; be disabled the next time `force-mode-line-update' happens.
+  (when (and (bound-and-true-p overriding-text-conversion-style)
+             (bound-and-true-p text-conversion-style))
+    (force-mode-line-update))
+
+  (let* ((overriding-text-conversion-style nil)
+         (map (if (consp chars)
                   (or (gethash (list help-form (cons help-char chars))
                                read-char-from-minibuffer-map-hash)
                       (let ((map (make-sparse-keymap))
@@ -3517,15 +3546,15 @@ There is no need to explicitly add `help-char' to CHARS;
                         ;; being a command char.
                         (when help-form
                           (define-key map (vector help-char)
-                            (lambda ()
-                              (interactive)
-                              (let ((help-form msg)) ; lexically bound msg
-                                (help-form-show)))))
+                                      (lambda ()
+                                        (interactive)
+                                        (let ((help-form msg)) ; lexically 
bound msg
+                                          (help-form-show)))))
                         (dolist (char chars)
                           (define-key map (vector char)
-                            #'read-char-from-minibuffer-insert-char))
+                                      #'read-char-from-minibuffer-insert-char))
                         (define-key map [remap self-insert-command]
-                          #'read-char-from-minibuffer-insert-other)
+                                    #'read-char-from-minibuffer-insert-other)
                         (puthash (list help-form (cons help-char chars))
                                  map read-char-from-minibuffer-map-hash)
                         map))
@@ -3630,9 +3659,14 @@ confusing to some users.")
        (or (consp last-nonmenu-event)   ; invoked by a mouse event
            (and (null last-nonmenu-event)
                 (consp last-input-event))
+           (featurep 'android)         ; Prefer dialog boxes on Android.
            from--tty-menu-p)            ; invoked via TTY menu
        use-dialog-box))
 
+;; Actually in textconv.c.
+(defvar overriding-text-conversion-style)
+(declare-function set-text-conversion-style "textconv.c")
+
 (defun y-or-n-p (prompt)
   "Ask user a \"y or n\" question.
 Return t if answer is \"y\" and nil if it is \"n\".
@@ -3711,6 +3745,9 @@ like) while `y-or-n-p' is running)."
       (while
           (let* ((scroll-actions '(recenter scroll-up scroll-down
                                             scroll-other-window 
scroll-other-window-down))
+                 ;; Disable text conversion so that real key events
+                 ;; are sent.
+                 (overriding-text-conversion-style nil)
                  (key
                   (let ((cursor-in-echo-area t))
                     (when minibuffer-auto-raise
@@ -3742,6 +3779,9 @@ like) while `y-or-n-p' is running)."
       (setq prompt (funcall padded prompt))
       (let* ((enable-recursive-minibuffers t)
              (msg help-form)
+             ;; Disable text conversion so that real Y or N events are
+             ;; sent.
+             (overriding-text-conversion-style nil)
              (keymap (let ((map (make-composed-keymap
                                  y-or-n-p-map query-replace-map)))
                        (when help-form
@@ -3755,9 +3795,15 @@ like) while `y-or-n-p' is running)."
                        map))
              ;; Protect this-command when called from pre-command-hook 
(bug#45029)
              (this-command this-command)
-             (str (read-from-minibuffer
-                   prompt nil keymap nil
-                   (or y-or-n-p-history-variable t))))
+             (str (progn
+                    ;; If the minibuffer is already active, the
+                    ;; selected window might not change.  Disable
+                    ;; text conversion by hand.
+                    (when (fboundp 'set-text-conversion-style)
+                      (set-text-conversion-style text-conversion-style))
+                    (read-from-minibuffer
+                     prompt nil keymap nil
+                     (or y-or-n-p-history-variable t)))))
         (setq answer (if (member str '("y" "Y")) 'act 'skip)))))
     (let ((ret (eq answer 'act)))
       (unless noninteractive
diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el
index 14b340f6183..e9ce7735229 100644
--- a/lisp/tab-bar.el
+++ b/lisp/tab-bar.el
@@ -36,6 +36,8 @@
   (require 'seq)
   (require 'icons))
 
+(autoload 'cl--set-substring "cl-lib")
+
 
 (defgroup tab-bar nil
   "Frame-local tabs."
@@ -341,10 +343,12 @@ only when you click on its \"x\" close button."
     (unless (eq tab-number t)
       (tab-bar-close-tab tab-number))))
 
-(defun tab-bar-mouse-context-menu (event)
-  "Pop up the context menu for the tab on which you click."
+(defun tab-bar-mouse-context-menu (event &optional posn)
+  "Pop up the context menu for the tab on which you click.
+EVENT is a mouse or touch screen event.  POSN is nil or the
+position of EVENT."
   (interactive "e")
-  (let* ((item (tab-bar--event-to-item (event-start event)))
+  (let* ((item (tab-bar--event-to-item (or posn (event-start event))))
          (tab-number (tab-bar--key-to-number (nth 0 item)))
          (menu (make-sparse-keymap (propertize "Context Menu" 'hide t))))
 
@@ -397,6 +401,78 @@ at the mouse-down event to the position at mouse-up event."
       (tab-bar-move-tab-to
        (if (null to) (1+ (tab-bar--current-tab-index)) to) from))))
 
+
+
+;;; Tab bar touchscreen support.
+
+(declare-function touch-screen-track-tap "touch-screen.el")
+
+(defun tab-bar-handle-timeout ()
+  "Handle a touch-screen timeout on the tab bar.
+Beep, then throw to `context-menu' and return."
+  (beep)
+  (throw 'context-menu 'context-menu))
+
+(defun tab-bar-touchscreen-begin (event)
+  "Handle a touchscreen begin EVENT on the tab bar.
+
+Determine where the touch was made.  If it was made on a tab
+itself, start a timer set to go off after a certain amount of
+time, and wait for the touch point to be released, and either
+display a context menu or select a tab as appropriate.
+
+Otherwise, if it was made on a button, close or create a tab as
+appropriate."
+  (interactive "e")
+  (let* ((posn (cdadr event))
+         (item (tab-bar--event-to-item posn))
+         (number (tab-bar--key-to-number (car item)))
+         timer)
+    (when (eq (catch 'context-menu
+                (cond ((integerp number)
+                       ;; The touch began on a tab.  Start a context
+                       ;; menu timer and start tracking the tap.
+                       (unwind-protect
+                           (progn
+                             (setq timer (run-at-time touch-screen-delay nil
+                                                      
#'tab-bar-handle-timeout))
+                             ;; Now wait for the tap to complete.
+                             (when (touch-screen-track-tap event)
+                               ;; And select the tab, or close it,
+                               ;; depending on whether or not the
+                               ;; close button was pressed.
+                               (if (caddr item)
+                                   (tab-bar-close-tab number)
+                                 (tab-bar-select-tab number))))
+                         ;; Cancel the timer.
+                         (cancel-timer timer)))
+                      ((and (memq (car item) '(add-tab history-back
+                                                       history-forward))
+                            (functionp (cadr item)))
+                       ;; This is some kind of button.  Wait for the
+                       ;; tap to complete and press it.
+                       (when (touch-screen-track-tap event)
+                         (call-interactively (cadr item))))
+                      (t
+                       ;; The touch began on the tab bar itself.
+                       ;; Start a context menu timer and start
+                       ;; tracking the tap, but don't do anything
+                       ;; afterwards.
+                       (unwind-protect
+                           (progn
+                             (setq timer (run-at-time touch-screen-delay nil
+                                                      
#'tab-bar-handle-timeout))
+                             ;; Now wait for the tap to complete.
+                             (touch-screen-track-tap event))
+                         ;; Cancel the timer.
+                         (cancel-timer timer)))))
+              'context-menu)
+      ;; Display the context menu in response to a time out waiting
+      ;; for the tap to complete.
+      (tab-bar-mouse-context-menu event posn))))
+
+
+
 (defvar-keymap tab-bar-map
   :doc "Keymap for the commands used on the tab bar."
   "<down-mouse-1>"  #'tab-bar-mouse-down-1
@@ -418,7 +494,8 @@ at the mouse-down event to the position at mouse-up event."
   "S-<wheel-up>"    #'tab-bar-move-tab-backward
   "S-<wheel-down>"  #'tab-bar-move-tab
   "S-<wheel-left>"  #'tab-bar-move-tab-backward
-  "S-<wheel-right>" #'tab-bar-move-tab)
+  "S-<wheel-right>" #'tab-bar-move-tab
+  "<touchscreen-begin>" #'tab-bar-touchscreen-begin)
 
 (global-set-key [tab-bar]
                 `(menu-item ,(purecopy "tab bar") ,(make-sparse-keymap)
diff --git a/lisp/tab-line.el b/lisp/tab-line.el
index 1958f12975f..d7c3049270a 100644
--- a/lisp/tab-line.el
+++ b/lisp/tab-line.el
@@ -137,33 +137,38 @@ function `tab-line-tab-face-group'."
 
 (defvar-keymap tab-line-tab-map
   :doc "Local keymap for `tab-line-mode' window tabs."
-  "<tab-line> <down-mouse-1>" #'tab-line-select-tab
-  "<tab-line> <mouse-2>"      #'tab-line-close-tab
-  "<tab-line> <down-mouse-3>" #'tab-line-tab-context-menu
+  "<tab-line> <down-mouse-1>"      #'tab-line-select-tab
+  "<tab-line> <mouse-2>"           #'tab-line-close-tab
+  "<tab-line> <down-mouse-3>"      #'tab-line-tab-context-menu
+  "<tab-line> <touchscreen-begin>" #'tab-line-select-tab
   "RET" #'tab-line-select-tab)
 
 (defvar-keymap tab-line-add-map
   :doc "Local keymap to add `tab-line-mode' window tabs."
-  "<tab-line> <down-mouse-1>" #'tab-line-new-tab
-  "<tab-line> <down-mouse-2>" #'tab-line-new-tab
+  "<tab-line> <down-mouse-1>"      #'tab-line-new-tab
+  "<tab-line> <down-mouse-2>"      #'tab-line-new-tab
+  "<tab-line> <touchscreen-begin>" #'tab-line-new-tab
   "RET" #'tab-line-new-tab)
 
 (defvar-keymap tab-line-tab-close-map
   :doc "Local keymap to close `tab-line-mode' window tabs."
-  "<tab-line> <mouse-1>" #'tab-line-close-tab
-  "<tab-line> <mouse-2>" #'tab-line-close-tab)
+  "<tab-line> <mouse-1>"           #'tab-line-close-tab
+  "<tab-line> <mouse-2>"           #'tab-line-close-tab
+  "<tab-line> <touchscreen-begin>" #'tab-line-close-tab)
 
 (defvar-keymap tab-line-left-map
   :doc "Local keymap to scroll `tab-line-mode' window tabs to the left."
-  "<tab-line> <down-mouse-1>" #'tab-line-hscroll-left
-  "<tab-line> <down-mouse-2>" #'tab-line-hscroll-left
-  "RET" #'tab-line-new-tab)
+  "<tab-line> <down-mouse-1>"      #'tab-line-hscroll-left
+  "<tab-line> <down-mouse-2>"      #'tab-line-hscroll-left
+  "<tab-line> <touchscreen-begin>" #'tab-line-hscroll-left
+  "RET"                            #'tab-line-new-tab)
 
 (defvar-keymap tab-line-right-map
   :doc "Local keymap to scroll `tab-line-mode' window tabs to the right."
-  "<tab-line> <down-mouse-1>" #'tab-line-hscroll-right
-  "<tab-line> <down-mouse-2>" #'tab-line-hscroll-right
-  "RET" #'tab-line-new-tab)
+  "<tab-line> <down-mouse-1>"      #'tab-line-hscroll-right
+  "<tab-line> <down-mouse-2>"      #'tab-line-hscroll-right
+  "<tab-line> <touchscreen-begin>" #'tab-line-hscroll-right
+  "RET"                            #'tab-line-new-tab)
 
 
 (defcustom tab-line-new-tab-choice t
@@ -326,6 +331,48 @@ or by `tab-line-tabs-buffer-groups'."
   "Function to return a global list of buffers.
 Used only for `tab-line-tabs-mode-buffers' and `tab-line-tabs-buffer-groups'.")
 
+
+
+;;; Touch screen support.
+
+(defun tab-line-track-tap (event &optional function)
+  "Track a tap starting from EVENT.
+If EVENT is not a `touchscreen-begin' event, return t.
+Otherwise, return t if the tap completes successfully, and nil if
+the tap should be ignored.
+
+If FUNCTION is specified and the tap does not complete within
+`touch-screen-delay' seconds, display the appropriate context
+menu by calling FUNCTION with EVENT, and return nil."
+  (if (not (eq (car-safe event) 'touchscreen-begin))
+      t
+    (let ((result (catch 'context-menu
+                    (let (timer)
+                      (unwind-protect
+                          (progn
+                            (when function
+                              (setq timer
+                                    (run-at-time touch-screen-delay t
+                                                 #'throw 'context-menu
+                                                 'context-menu)))
+                            (touch-screen-track-tap event))
+                        (when timer
+                          (cancel-timer timer)))))))
+      (cond ((eq result 'context-menu)
+             (prog1 nil
+               (funcall function event)))
+            (result t)))))
+
+(defun tab-line-event-start (event)
+  "Like `event-start'.
+However, return the correct mouse position list if EVENT is a
+`touchscreen-begin' event."
+  (or (and (eq (car-safe event) 'touchscreen-begin)
+           (cdadr event))
+      (event-start event)))
+
+
+
 (defun tab-line-tabs-buffer-list ()
   (seq-filter (lambda (b) (and (buffer-live-p b)
                                (/= (aref (buffer-name b) 0) ?\s)))
@@ -719,17 +766,21 @@ the selected tab visible."
   "Scroll the tab line ARG positions to the right.
 Interactively, ARG is the prefix numeric argument and defaults to 1."
   (interactive (list current-prefix-arg last-nonmenu-event))
-  (let ((window (and (listp event) (posn-window (event-start event)))))
-    (tab-line-hscroll arg window)
-    (force-mode-line-update window)))
+  (when (tab-line-track-tap event)
+    (let ((window (and (listp event)
+                       (posn-window (tab-line-event-start event)))))
+      (tab-line-hscroll arg window)
+      (force-mode-line-update window))))
 
 (defun tab-line-hscroll-left (&optional arg event)
   "Scroll the tab line ARG positions to the left.
 Interactively, ARG is the prefix numeric argument and defaults to 1."
   (interactive (list current-prefix-arg last-nonmenu-event))
-  (let ((window (and (listp event) (posn-window (event-start event)))))
-    (tab-line-hscroll (- (or arg 1)) window)
-    (force-mode-line-update window)))
+  (when (tab-line-track-tap event)
+    (let ((window (and (listp event)
+                       (posn-window (tab-line-event-start event)))))
+      (tab-line-hscroll (- (or arg 1)) window)
+      (force-mode-line-update window))))
 
 
 (defun tab-line-new-tab (&optional event)
@@ -738,15 +789,16 @@ This command is usually invoked by clicking on the 
plus-shaped button
 on the tab line.  Switching to another buffer also adds a new tab
 corresponding to the new buffer shown in the window."
   (interactive (list last-nonmenu-event))
-  (if (functionp tab-line-new-tab-choice)
-      (funcall tab-line-new-tab-choice)
-    (let ((tab-line-tabs-buffer-groups mouse-buffer-menu-mode-groups))
-      (if (and (listp event)
-               (display-popup-menus-p)
-               (not tty-menu-open-use-tmm))
-          (mouse-buffer-menu event) ; like (buffer-menu-open)
-        ;; tty menu doesn't support mouse clicks, so use tmm
-        (tmm-prompt (mouse-buffer-menu-keymap))))))
+  (when (tab-line-track-tap event)
+    (if (functionp tab-line-new-tab-choice)
+        (funcall tab-line-new-tab-choice)
+      (let ((tab-line-tabs-buffer-groups mouse-buffer-menu-mode-groups))
+        (if (and (listp event)
+                 (display-popup-menus-p)
+                 (not tty-menu-open-use-tmm))
+            (mouse-buffer-menu event) ; like (buffer-menu-open)
+          ;; tty menu doesn't support mouse clicks, so use tmm
+          (tmm-prompt (mouse-buffer-menu-keymap)))))))
 
 (defun tab-line-select-tab (&optional event)
   "Switch to the buffer specified by the tab on which you click.
@@ -754,16 +806,17 @@ This command maintains the original order of prev/next 
buffers.
 So, for example, switching to a previous tab is equivalent to
 using the `previous-buffer' command."
   (interactive "e")
-  (let* ((posnp (event-start event))
-         (tab (tab-line--get-tab-property 'tab (car (posn-string posnp))))
-         (buffer (if (bufferp tab) tab (cdr (assq 'buffer tab)))))
-    (if buffer
-        (tab-line-select-tab-buffer buffer (posn-window posnp))
-      (let ((select (cdr (assq 'select tab))))
-        (when (functionp select)
-          (with-selected-window (posn-window posnp)
-            (funcall select)
-            (force-mode-line-update)))))))
+  (when (tab-line-track-tap event #'tab-line-tab-context-menu)
+    (let* ((posnp (tab-line-event-start event))
+           (tab (tab-line--get-tab-property 'tab (car (posn-string posnp))))
+           (buffer (if (bufferp tab) tab (cdr (assq 'buffer tab)))))
+      (if buffer
+          (tab-line-select-tab-buffer buffer (posn-window posnp))
+        (let ((select (cdr (assq 'select tab))))
+          (when (functionp select)
+            (with-selected-window (posn-window posnp)
+              (funcall select)
+              (force-mode-line-update))))))))
 
 (defun tab-line-select-tab-buffer (buffer &optional window)
   (let* ((window-buffer (window-buffer window))
@@ -869,25 +922,27 @@ This command is usually invoked by clicking on the close 
button on the
 right side of the tab.  This command buries the buffer, so it goes out of
 sight of the tab line."
   (interactive (list last-nonmenu-event))
-  (let* ((posnp (and (listp event) (event-start event)))
-         (window (and posnp (posn-window posnp)))
-         (tab (tab-line--get-tab-property 'tab (car (posn-string posnp))))
-         (buffer (if (bufferp tab) tab (cdr (assq 'buffer tab))))
-         (close-function (unless (bufferp tab) (cdr (assq 'close tab)))))
-    (with-selected-window (or window (selected-window))
-      (cond
-       ((functionp close-function)
-        (funcall close-function))
-       ((eq tab-line-close-tab-function 'kill-buffer)
-        (kill-buffer buffer))
-       ((eq tab-line-close-tab-function 'bury-buffer)
-        (if (eq buffer (current-buffer))
-            (bury-buffer)
-          (set-window-prev-buffers nil (assq-delete-all buffer 
(window-prev-buffers)))
-          (set-window-next-buffers nil (delq buffer (window-next-buffers)))))
-       ((functionp tab-line-close-tab-function)
-        (funcall tab-line-close-tab-function tab)))
-      (force-mode-line-update))))
+  (when (tab-line-track-tap event)
+    (let* ((posnp (and (listp event)
+                       (tab-line-event-start event)))
+           (window (and posnp (posn-window posnp)))
+           (tab (tab-line--get-tab-property 'tab (car (posn-string posnp))))
+           (buffer (if (bufferp tab) tab (cdr (assq 'buffer tab))))
+           (close-function (unless (bufferp tab) (cdr (assq 'close tab)))))
+      (with-selected-window (or window (selected-window))
+        (cond
+         ((functionp close-function)
+          (funcall close-function))
+         ((eq tab-line-close-tab-function 'kill-buffer)
+          (kill-buffer buffer))
+         ((eq tab-line-close-tab-function 'bury-buffer)
+          (if (eq buffer (current-buffer))
+              (bury-buffer)
+            (set-window-prev-buffers nil (assq-delete-all buffer 
(window-prev-buffers)))
+            (set-window-next-buffers nil (delq buffer (window-next-buffers)))))
+         ((functionp tab-line-close-tab-function)
+          (funcall tab-line-close-tab-function tab)))
+        (force-mode-line-update)))))
 
 (defun tab-line-tab-context-menu (&optional event)
   "Pop up the context menu for a tab-line tab."
diff --git a/lisp/term.el b/lisp/term.el
index 73f583ff718..5d43ea56791 100644
--- a/lisp/term.el
+++ b/lisp/term.el
@@ -1139,6 +1139,9 @@ Entry to this mode runs the hooks on `term-mode-hook'."
   (setq-local term-last-input-end (make-marker))
   (setq-local term-last-input-match "")
 
+  ;; Always display the onscreen keyboard.
+  (setq-local touch-screen-display-keyboard t)
+
   ;; These local variables are set to their local values:
   (make-local-variable 'term-saved-home-marker)
   (make-local-variable 'term-saved-cursor)
@@ -1729,7 +1732,12 @@ Nil if unknown.")
       (push (format "EMACS=%s (term:%s)" emacs-version term-protocol-version)
             process-environment))
     (apply #'start-process name buffer
-          "/bin/sh" "-c"
+           ;; On Android, /bin doesn't exist, and the default shell is
+           ;; found as /system/bin/sh.
+          (if (eq system-type 'android)
+               "/system/bin/sh"
+             "/bin/sh")
+           "-c"
           (format "stty -nl echo rows %d columns %d sane 2>%s;\
 if [ $1 = .. ]; then shift; fi; exec \"$@\""
                   term-height term-width null-device)
@@ -2961,7 +2969,7 @@ See `term-prompt-regexp'."
 ;; It emulates (most of the features of) a VT100/ANSI-style terminal.
 
 ;; References:
-;; [ctlseqs]: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+;; [ctlseqs]: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
 ;; [ECMA-48]: 
https://www.ecma-international.org/publications/standards/Ecma-048.htm
 ;; [vt100]: https://vt100.net/docs/vt100-ug/chapter3.html
 
diff --git a/lisp/term/android-win.el b/lisp/term/android-win.el
new file mode 100644
index 00000000000..d425ea401a9
--- /dev/null
+++ b/lisp/term/android-win.el
@@ -0,0 +1,237 @@
+;;; x-win.el --- parse relevant switches and set up for Android  -*- 
lexical-binding:t -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; Author: FSF
+;; Keywords: terminals, i18n, android
+
+;; 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/>.
+
+;;; Commentary:
+
+;; This file contains the support for initializing the Lisp side of
+;; Android windowing.
+
+;;; Code:
+
+
+(unless (featurep 'android)
+  (error "%s: Loading android-win without having Android"
+         invocation-name))
+
+;; Documentation-purposes only: actually loaded in loadup.el.
+(require 'frame)
+(require 'mouse)
+(require 'fontset)
+(require 'dnd)
+(require 'touch-screen)
+
+(add-to-list 'display-format-alist '(".*" . android))
+
+(declare-function android-get-connection "androidfns.c")
+
+;; Window system initialization.  This is extremely simple because all
+;; initialization is done in android_term_init.
+
+(cl-defmethod window-system-initialization (&context (window-system android)
+                                                     &optional _ignored)
+  "Set up the window system.  WINDOW-SYSTEM must be ANDROID.
+DISPLAY is ignored on Android."
+  ;; Create the default fontset.
+  (create-default-fontset)
+  ;; Just make sure the window system was initialized at startup.
+  (android-get-connection))
+
+(cl-defmethod frame-creation-function (params &context (window-system android))
+  (x-create-frame-with-faces params))
+
+(cl-defmethod handle-args-function (args &context (window-system android))
+  ;; Android has no command line to provide arguments on.
+  ;; However, call x-handle-args to handle file name args.
+  (x-handle-args args))
+
+
+;;; 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")
+(declare-function android-get-clipboard-targets "androidselect.c")
+(declare-function android-get-clipboard-data "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.  `STRING' means to
+return the contents of the clipboard as a string.  `TARGETS'
+means to return supported data types as a vector.
+
+Interpret any other symbol as a MIME type, and return its
+corresponding data."
+  (or (and (eq data-type 'STRING)
+           (android-get-clipboard))
+      (and (eq data-type 'TARGETS)
+           (android-clipboard-exists-p)
+           (vconcat [TARGETS STRING]
+                    (let ((i nil))
+                      (dolist (type (android-get-clipboard-targets))
+                        ;; Don't report plain text as a valid target.
+                        (unless (equal type "text/plain")
+                          (push (intern type) i)))
+                      (nreverse i))))
+      (and (symbolp data-type)
+           (android-get-clipboard-data (symbol-name data-type)))))
+
+(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)))))
+
+;;; Character composition display.
+
+(defvar android-preedit-overlay nil
+  "The overlay currently used to display preedit text from a compose 
sequence.")
+
+;; With some input methods, text gets inserted before Emacs is told to
+;; remove any preedit text that was displayed, which causes both the
+;; preedit overlay and the text to be visible for a brief period of
+;; time.  This pre-command-hook clears the overlay before any command
+;; and should be set whenever a preedit overlay is visible.
+(defun android-clear-preedit-text ()
+  "Clear the pre-edit overlay and remove itself from pre-command-hook.
+This function should be installed in `pre-command-hook' whenever
+preedit text is displayed."
+  (when android-preedit-overlay
+    (delete-overlay android-preedit-overlay)
+    (setq android-preedit-overlay nil))
+  (remove-hook 'pre-command-hook #'android-clear-preedit-text))
+
+(defun android-preedit-text (event)
+  "Display preedit text from a compose sequence in EVENT.
+EVENT is a preedit-text event."
+  (interactive "e")
+  (when android-preedit-overlay
+    (delete-overlay android-preedit-overlay)
+    (setq android-preedit-overlay nil)
+    (remove-hook 'pre-command-hook #'android-clear-preedit-text))
+  (when (nth 1 event)
+    (let ((string (propertize (nth 1 event) 'face '(:underline t))))
+      (setq android-preedit-overlay (make-overlay (point) (point)))
+      (add-hook 'pre-command-hook #'android-clear-preedit-text)
+      (overlay-put android-preedit-overlay 'window (selected-window))
+      (overlay-put android-preedit-overlay 'before-string string))))
+
+(define-key special-event-map [preedit-text] 'android-preedit-text)
+
+
+;; Android cursor shapes, named according to the X scheme.
+;; Many X cursors are missing.
+
+(defconst x-pointer-arrow 1000)
+(defconst x-pointer-left-ptr 1000)
+(defconst x-pointer-left-side 1020)
+(defconst x-pointer-sb-h-double-arrow 1014)
+(defconst x-pointer-sb-v-double-arrow 1015)
+(defconst x-pointer-watch 1004)
+(defconst x-pointer-xterm 1008)
+(defconst x-pointer-invisible 0)
+
+
+(provide 'android-win)
+;; android-win.el ends here.
diff --git a/lisp/textmodes/artist.el b/lisp/textmodes/artist.el
index e0f311552d0..08613db600c 100644
--- a/lisp/textmodes/artist.el
+++ b/lisp/textmodes/artist.el
@@ -2800,7 +2800,9 @@ EXTRA-ARGS for figlet, for the command line, may be 
specified."
 (defun artist-figlet-get-font-list ()
   "Read fonts in with the shell command.
 Returns a list of strings."
-  (let* ((cmd-interpreter "/bin/sh")
+  (let* ((cmd-interpreter (if (eq system-type 'android)
+                              "/system/bin/sh"
+                            "/bin/sh"))
         (ls-cmd          artist-figlet-list-fonts-command)
         (result          (artist-system cmd-interpreter ls-cmd nil))
         (exit-code       (elt result 0))
diff --git a/lisp/textmodes/conf-mode.el b/lisp/textmodes/conf-mode.el
index d15fba9c43a..4d0dde7752b 100644
--- a/lisp/textmodes/conf-mode.el
+++ b/lisp/textmodes/conf-mode.el
@@ -245,6 +245,7 @@ This variable is best set in the file local variables, or 
through
     ("^\\s-*\\(.+?\\)\\(?:\\[\\(.*?\\)\\]\\)?\\s-*="
      (1 'font-lock-variable-name-face)
      (2 'font-lock-constant-face nil t))
+    ;; Must be lower-case according to the TOML spec.
     ("\\_<false\\|true\\_>" 0 'font-lock-keyword-face))
   "Keywords to highlight in Conf TOML mode.")
 
@@ -435,6 +436,7 @@ The optional arg FONT-LOCK is the value for 
FONT-LOCK-KEYWORDS."
   (setq-local comment-start comment)
   (setq-local comment-start-skip
               (concat (regexp-quote comment-start) "+\\s *"))
+  (setq-local text-conversion-style t)
   (if font-lock
       (setq-local font-lock-defaults `(,font-lock nil t nil nil))))
 
@@ -643,7 +645,10 @@ For details see `conf-mode'.  Example:
 
 \[entry]
 value = \"some string\""
-  (conf-mode-initialize "#" 'conf-toml-font-lock-keywords)
+  (conf-mode-initialize "#")
+  ;; Booleans are "always lowercase", so we must *not* use case
+  ;; folding.  Therefore, we can't set it using `conf-mode-initialize´.
+  (setq-local font-lock-defaults `(,conf-toml-font-lock-keywords nil nil nil 
nil))
   (setq-local conf-assignment-column 0)
   (setq-local conf-assignment-sign ?=))
 
diff --git a/lisp/textmodes/less-css-mode.el b/lisp/textmodes/less-css-mode.el
index c3905afb4e7..2e4d71fb631 100644
--- a/lisp/textmodes/less-css-mode.el
+++ b/lisp/textmodes/less-css-mode.el
@@ -215,7 +215,7 @@ directory by default."
 ;;;###autoload (add-to-list 'auto-mode-alist '("\\.less\\'" . less-css-mode))
 ;;;###autoload
 (define-derived-mode less-css-mode css-mode "Less"
-  "Major mode for editing Less files (http://lesscss.org/).
+  "Major mode for editing Less files (https://lesscss.org/).
 Special commands:
 \\{less-css-mode-map}"
   (font-lock-add-keywords nil less-css-font-lock-keywords)
diff --git a/lisp/textmodes/reftex-global.el b/lisp/textmodes/reftex-global.el
index acf0891432f..b8b0ae6a061 100644
--- a/lisp/textmodes/reftex-global.el
+++ b/lisp/textmodes/reftex-global.el
@@ -39,8 +39,10 @@ The TAGS file is also immediately visited with 
`visit-tags-table'."
   (reftex-access-scan-info current-prefix-arg)
   (let* ((master (reftex-TeX-master-file))
          (files  (reftex-all-document-files))
-         (cmd    (format "etags %s" (mapconcat #'shell-quote-argument
-                                              files " "))))
+         (cmd    (format "%s %s"
+                         etags-program-name
+                         (mapconcat #'shell-quote-argument
+                                   files " "))))
     (with-current-buffer (reftex-get-file-buffer-force master)
       (message "Running etags to create TAGS file...")
       (shell-command cmd)
diff --git a/lisp/textmodes/remember.el b/lisp/textmodes/remember.el
index 95e18b7d1d5..84a0185f7b5 100644
--- a/lisp/textmodes/remember.el
+++ b/lisp/textmodes/remember.el
@@ -7,7 +7,7 @@
 ;; Created: 29 Mar 1999
 ;; Old-Version: 2.0
 ;; Keywords: data memory todo pim
-;; URL: http://gna.org/projects/remember-el/
+;; URL: http://gna.org/projects/remember-el/ [dead link]
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/textmodes/rst.el b/lisp/textmodes/rst.el
index 9d68feed0e0..6108e80363c 100644
--- a/lisp/textmodes/rst.el
+++ b/lisp/textmodes/rst.el
@@ -1473,7 +1473,7 @@ for modes derived from Text mode, like Mail mode."
   :version "21.1")
 
 ;; FIXME: Default must match suggestion in
-;;        http://sphinx-doc.org/rest.html#sections for Python documentation.
+;;        https://sphinx-doc.org/rest.html#sections for Python documentation.
 (defcustom rst-preferred-adornments '((?= over-and-under 1)
                                      (?= simple 0)
                                      (?- simple 0)
diff --git a/lisp/textmodes/sgml-mode.el b/lisp/textmodes/sgml-mode.el
index d80cdc34775..27f3b2acd1c 100644
--- a/lisp/textmodes/sgml-mode.el
+++ b/lisp/textmodes/sgml-mode.el
@@ -2680,7 +2680,6 @@ HTML Autoview mode is a buffer-local minor mode for use 
with
   "<html lang=\"en\">" \n
   "<head>" \n
   "<meta charset=\"utf-8\">" \n
-  "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">" \n
   "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" \n
   "<title>" (skeleton-read "Page Title: ") "</title>" \n
   "</head>" \n
diff --git a/lisp/textmodes/text-mode.el b/lisp/textmodes/text-mode.el
index 48cefc74d06..ccba1b063ab 100644
--- a/lisp/textmodes/text-mode.el
+++ b/lisp/textmodes/text-mode.el
@@ -41,6 +41,9 @@
   "Non-nil if this buffer's major mode is a variant of Text mode.")
 (make-obsolete-variable 'text-mode-variant 'derived-mode-p "27.1")
 
+;; Actually defined in textconv.c.
+(defvar text-conversion-style)
+
 (defvar text-mode-syntax-table
   (let ((st (make-syntax-table)))
     (modify-syntax-entry ?\" ".   " st)
@@ -125,6 +128,9 @@ You can thus get the full benefit of adaptive filling
 Turning on Text mode runs the normal hook `text-mode-hook'."
   (setq-local text-mode-variant t)
   (setq-local require-final-newline mode-require-final-newline)
+
+  ;; Enable text conversion in this buffer.
+  (setq-local text-conversion-style t)
   (add-hook 'context-menu-functions 'text-mode-context-menu 10 t))
 
 (define-derived-mode paragraph-indent-text-mode text-mode "Parindent"
diff --git a/lisp/textmodes/toml-ts-mode.el b/lisp/textmodes/toml-ts-mode.el
index 2c491034372..193c83f9aec 100644
--- a/lisp/textmodes/toml-ts-mode.el
+++ b/lisp/textmodes/toml-ts-mode.el
@@ -39,8 +39,8 @@
 (defcustom toml-ts-mode-indent-offset 2
   "Number of spaces for each indentation step in `toml-ts-mode'."
   :version "29.1"
-  :type 'integer
-  :safe 'integerp
+  :type 'natnum
+  :safe 'natnump
   :group 'toml)
 
 (defvar toml-ts-mode--syntax-table
diff --git a/lisp/tool-bar.el b/lisp/tool-bar.el
index 1a0faf3a584..ab81a21211d 100644
--- a/lisp/tool-bar.el
+++ b/lisp/tool-bar.el
@@ -83,6 +83,14 @@ buffer-locally and add the items you want to it with
 `tool-bar-add-item', `tool-bar-add-item-from-menu' and related
 functions.")
 
+(defvar secondary-tool-bar-map nil
+  "Optional secondary keymap for the tool bar.
+
+If non-nil, tool bar items defined within this map are displayed
+in a line below the tool bar if the `tool-bar-position' frame
+parameter is set to `top', and above the tool bar it is set to
+`bottom'.")
+
 (global-set-key [tool-bar]
                `(menu-item ,(purecopy "tool bar") ignore
                            :filter tool-bar-make-keymap))
@@ -91,15 +99,21 @@ functions.")
 
 (defconst tool-bar-keymap-cache (make-hash-table :test #'equal))
 
-(defun tool-bar--cache-key ()
+(defsubst tool-bar--cache-key ()
   (cons (frame-terminal) (sxhash-eq tool-bar-map)))
 
+(defsubst tool-bar--secondary-cache-key ()
+  (cons (frame-terminal) (sxhash-eq secondary-tool-bar-map)))
+
 (defun tool-bar--flush-cache ()
   "Remove all cached entries that refer to the current `tool-bar-map'."
   (let ((id (sxhash-eq tool-bar-map))
+        (secondary-id (and secondary-tool-bar-map
+                           (sxhash-eq secondary-tool-bar-map)))
         (entries nil))
     (maphash (lambda (k _)
-               (when (equal (cdr k) id)
+               (when (or (equal (cdr k) id)
+                         (equal (cdr k) secondary-id))
                  (push k entries)))
              tool-bar-keymap-cache)
     (dolist (k entries)
@@ -107,14 +121,54 @@ functions.")
 
 (defun tool-bar-make-keymap (&optional _ignore)
   "Generate an actual keymap from `tool-bar-map'.
+If `secondary-tool-bar-map' is non-nil, take it into account as well.
 Its main job is to figure out which images to use based on the display's
 color capability and based on the available image libraries."
-  (or (gethash (tool-bar--cache-key) tool-bar-keymap-cache)
-      (setf (gethash (tool-bar--cache-key) tool-bar-keymap-cache)
-            (tool-bar-make-keymap-1))))
-
-(defun tool-bar-make-keymap-1 ()
-  "Generate an actual keymap from `tool-bar-map', without caching."
+  (let* ((key (tool-bar--cache-key))
+         (base-keymap
+          (or (gethash key tool-bar-keymap-cache)
+              (setf (gethash key tool-bar-keymap-cache)
+                    (tool-bar-make-keymap-1))))
+        (secondary-keymap
+         (and secondary-tool-bar-map
+              (or (gethash (tool-bar--secondary-cache-key)
+                           tool-bar-keymap-cache)
+                  (setf (gethash (tool-bar--secondary-cache-key)
+                                 tool-bar-keymap-cache)
+                        (tool-bar-make-keymap-1
+                         secondary-tool-bar-map))))))
+    (if secondary-keymap
+        (or (ignore-errors
+              (progn
+                ;; Determine the value of the `tool-bar-position' frame
+                ;; parameter.
+                (let ((position (frame-parameter nil 'tool-bar-position)))
+                  (cond ((eq position 'top)
+                         ;; Place `base-keymap' above `secondary-keymap'.
+                         (append base-keymap (list (list (gensym)
+                                                         'menu-item
+                                                         "" 'ignore
+                                                         :wrap t))
+                                 (cdr secondary-keymap)))
+                        ((eq position 'bottom)
+                         ;; Place `secondary-keymap' above `base-keymap'.
+                         (append secondary-keymap (list (list (gensym)
+                                                              'menu-item
+                                                              "" 'ignore
+                                                              :wrap t))
+                                 (cdr base-keymap)))
+                        ;; If the tool bar position isn't known, don't
+                        ;; display the secondary keymap at all.
+                        (t base-keymap)))))
+            ;; If combining both keymaps fails, return the base
+            ;; keymap.
+            base-keymap)
+      base-keymap)))
+
+(defun tool-bar-make-keymap-1 (&optional map)
+  "Generate an actual keymap from `tool-bar-map', without caching.
+MAP is either a keymap to use as a source for menu items, or nil,
+in which case the value of `tool-bar-map' is used instead."
   (mapcar (lambda (bind)
             (let (image-exp plist)
               (when (and (eq (car-safe (cdr-safe bind)) 'menu-item)
@@ -136,7 +190,7 @@ color capability and based on the available image 
libraries."
                                        bind))
                    (plist-put plist :image image))))
              bind))
-         tool-bar-map))
+         (or map tool-bar-map)))
 
 ;;;###autoload
 (defun tool-bar-add-item (icon def key &rest props)
@@ -322,6 +376,207 @@ Customize `tool-bar-mode' if you want to show or hide the 
tool bar."
             (modify-all-frames-parameters
              (list (cons 'tool-bar-position val))))))
 
+
+
+;; Modifier bar mode.
+;; This displays a small tool bar containing modifier keys
+;; above or below the main tool bar itself.
+
+(defvar modifier-bar-modifier-list nil
+  "List of modifiers that are currently applied.
+Each symbol in this list represents a modifier button that has
+been pressed as part of decoding this key sequence.")
+
+(declare-function set-text-conversion-style "textconv.c")
+
+;; These functions are very similar to their counterparts in
+;; simple.el, but allow combining multiple modifier buttons together.
+
+(defun tool-bar-apply-modifiers (event modifiers)
+  "Apply the specified list of MODIFIERS to EVENT.
+MODIFIERS must be a list containing only the symbols `alt',
+`super', `hyper', `shift', `control' and `meta'.
+Return EVENT with the specified modifiers applied."
+  (dolist (modifier modifiers)
+    (cond
+     ((eq modifier 'alt)
+      (setq event (event-apply-modifier event 'alt 22 "A-")))
+     ((eq modifier 'super)
+      (setq event (event-apply-modifier event 'super 23 "s-")))
+     ((eq modifier 'hyper)
+      (setq event (event-apply-modifier event 'hyper 24 "H-")))
+     ((eq modifier 'shift)
+      (setq event (event-apply-modifier event 'shift 25 "S-")))
+     ((eq modifier 'control)
+      (setq event (event-apply-modifier event 'control 26 "C-")))
+     ((eq modifier 'meta)
+      (setq event (event-apply-modifier event 'meta 27 "M-")))))
+  event)
+
+(defvar overriding-text-conversion-style)
+
+(defun modifier-bar-button (init-modifier-list)
+  "Decode the key sequence associated with a modifier bar button.
+INIT-MODIFIER-LIST is a list of one symbol describing the button
+being pressed.
+
+Bind `modifier-bar-modifier-list' to INIT-MODIFIER-LIST.  Read
+events, adding each subsequent modifier bar event's associated
+modifier to that list while updating the tool bar to disable
+buttons that were pressed.  Return any other event read with all
+modifier keys read applied.
+
+Temporarily disable text conversion and display the on screen
+keyboard while doing so."
+  ;; Save the previously used text conversion style.
+  (let ((old-text-conversion-style text-conversion-style)
+        ;; Clear the list of modifiers currently pressed.
+        (modifier-bar-modifier-list init-modifier-list))
+    ;; Disable text conversion.
+    (when (fboundp 'set-text-conversion-style)
+      (set-text-conversion-style nil))
+    (unwind-protect
+        (progn
+          ;; Display the on screen keyboard.
+          (frame-toggle-on-screen-keyboard nil nil)
+          ;; Update the tool bar to disable this modifier key.
+          (force-mode-line-update)
+          (let* ((modifiers init-modifier-list) event1
+                 (overriding-text-conversion-style nil)
+                 (event (read-event)))
+            ;; Combine any more modifier key presses.
+            (while (eq event 'tool-bar)
+              (setq event1 (event-basic-type (read-event)))
+              ;; Reject unknown tool bar events.
+              (unless (memq event1 '(alt super hyper shift control meta))
+                (user-error "Unknown tool-bar event %s" event1))
+              ;; If `event' is the name of a modifier key, apply that
+              ;; modifier key as well.
+              (unless (memq event1 modifiers)
+                (push event1 modifiers)
+                ;; This list is used to check which tool bar buttons
+                ;; need to be enabled.
+                (push event1 modifier-bar-modifier-list))
+              ;; Update the tool bar to disable the modifier button
+              ;; that was read.
+              (force-mode-line-update)
+              (redisplay)
+              ;; Read another event.
+              (setq event (read-event)))
+            ;; EVENT is a keyboard event to which the specified list of
+            ;; modifier keys should be applied.
+            (vector (tool-bar-apply-modifiers event modifiers))))
+      ;; Re-enable text conversion if necessary.
+      (unless (or (not (fboundp 'set-text-conversion-style))
+                  (eq old-text-conversion-style text-conversion-style))
+        (set-text-conversion-style old-text-conversion-style t))
+      ;; Re-enable all modifier bar buttons which may have been
+      ;; disabled.
+      (force-mode-line-update))))
+
+(defun tool-bar-event-apply-alt-modifier (_ignore-prompt)
+  "Like `event-apply-alt-modifier'.
+However, take additional modifier tool bar items into account;
+apply any extra modifiers bound to subsequent `tool-bar' events."
+  (modifier-bar-button '(alt)))
+
+(defun tool-bar-event-apply-super-modifier (_ignore-prompt)
+  "Like `event-apply-super-modifier'.
+However, take additional modifier tool bar items into account;
+apply any extra modifiers bound to subsequent `tool-bar' events."
+  (modifier-bar-button '(super)))
+
+(defun tool-bar-event-apply-hyper-modifier (_ignore-prompt)
+  "Like `event-apply-hyper-modifier'.
+However, take additional modifier tool bar items into account;
+apply any extra modifiers bound to subsequent `tool-bar' events."
+  (modifier-bar-button '(hyper)))
+
+(defun tool-bar-event-apply-shift-modifier (_ignore-prompt)
+  "Like `event-apply-shift-modifier'.
+However, take additional modifier tool bar items into account;
+apply any extra modifiers bound to subsequent `tool-bar' events."
+  (modifier-bar-button '(shift)))
+
+(defun tool-bar-event-apply-control-modifier (_ignore-prompt)
+  "Like `event-apply-control-modifier'.
+However, take additional modifier tool bar items into account;
+apply any extra modifiers bound to subsequent `tool-bar' events."
+  (modifier-bar-button '(control)))
+
+(defun tool-bar-event-apply-meta-modifier (_ignore-prompt)
+  "Like `event-apply-meta-modifier'.
+However, take additional modifier tool bar items into account;
+apply any extra modifiers bound to subsequent `tool-bar' events."
+  (modifier-bar-button '(meta)))
+
+(defun modifier-bar-available-p (modifier)
+  "Return whether the modifier button for MODIFIER should be enabled.
+Return t if MODIFIER has not yet been selected as part of
+decoding the current key sequence, nil otherwise."
+  (not (memq modifier modifier-bar-modifier-list)))
+
+(define-minor-mode modifier-bar-mode
+  "Toggle display of the modifier bar.
+
+When enabled, a small tool bar will be displayed next to the tool
+bar containing items bound to
+`tool-bar-event-apply-control-modifier' and its related commands,
+which see."
+  :init-value nil
+  :global t
+  :group 'tool-bar
+  (if modifier-bar-mode
+      (progn
+        (setq secondary-tool-bar-map
+              ;; The commands specified in the menu items here are not
+              ;; used.  Instead, Emacs relies on each of the tool bar
+              ;; events being specified in `input-decode-map'.
+              `(keymap (control menu-item "Control Key"
+                                event-apply-control-modifier
+                                :help "Add Control modifier to the following 
event"
+                                :image ,(tool-bar--image-expression "ctrl")
+                                :enable (modifier-bar-available-p 'control))
+                       (shift menu-item "Shift Key"
+                              event-apply-shift-modifier
+                              :help "Add Shift modifier to the following event"
+                              :image ,(tool-bar--image-expression "shift")
+                              :enable (modifier-bar-available-p 'shift))
+                       (meta menu-item "Meta Key"
+                             event-apply-meta-modifier
+                             :help "Add Meta modifier to the following event"
+                             :image ,(tool-bar--image-expression "meta")
+                             :enable (modifier-bar-available-p 'meta))
+                       (alt menu-item "Alt Key"
+                            event-apply-alt-modifier
+                            :help "Add Alt modifier to the following event"
+                            :image ,(tool-bar--image-expression "alt")
+                            :enable (modifier-bar-available-p 'alt))
+                       (super menu-item "Super Key"
+                              event-apply-super-modifier
+                              :help "Add Super modifier to the following event"
+                              :image ,(tool-bar--image-expression "super")
+                              :enable (modifier-bar-available-p 'super))
+                       (hyper menu-item "Hyper Key"
+                              event-apply-hyper-modifier
+                              :help "Add Hyper modifier to the following event"
+                              :image ,(tool-bar--image-expression "hyper")
+                              :enable (modifier-bar-available-p 'hyper))))
+        (define-key input-decode-map [tool-bar control]
+                    #'tool-bar-event-apply-control-modifier)
+        (define-key input-decode-map [tool-bar shift]
+                    #'tool-bar-event-apply-shift-modifier)
+        (define-key input-decode-map [tool-bar meta]
+                    #'tool-bar-event-apply-meta-modifier)
+        (define-key input-decode-map [tool-bar alt]
+                    #'tool-bar-event-apply-alt-modifier)
+        (define-key input-decode-map [tool-bar super]
+                    #'tool-bar-event-apply-super-modifier)
+        (define-key input-decode-map [tool-bar hyper]
+                    #'tool-bar-event-apply-hyper-modifier))
+    (setq secondary-tool-bar-map nil))
+  ;; Update the mode line now.
+  (force-mode-line-update t))
 
 (provide 'tool-bar)
 
diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el
new file mode 100644
index 00000000000..0914071f2a0
--- /dev/null
+++ b/lisp/touch-screen.el
@@ -0,0 +1,1579 @@
+;;; touch-screen.el --- touch screen support for X and Android  -*- 
lexical-binding: t; -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; Maintainer: emacs-devel@gnu.org
+;; Package: emacs
+
+;; 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/>.
+
+;;; Commentary:
+
+;; This file provides code to recognize simple touch screen gestures.
+;; It is used on X and Android, currently the only systems where Emacs
+;; supports touch input.
+;;
+;; See (elisp)Touchscreen Events for a description of the details of
+;; touch events.
+
+;;; Code:
+
+(defvar touch-screen-current-tool nil
+  "The touch point currently being tracked, or nil.
+If non-nil, this is a list of nine elements: the ID of the touch
+point being tracked, the window where the touch began, a cons
+containing the last known position of the touch point, relative
+to that window, a field used to store data while tracking the
+touch point, the initial position of the touchpoint, and another
+four fields to used store data while tracking the touch point.
+See `touch-screen-handle-point-update' and
+`touch-screen-handle-point-up' for the meanings of the fourth
+element.")
+
+(defvar touch-screen-set-point-commands '(mouse-set-point)
+  "List of commands known to set the point.
+This is used to determine whether or not to display the on-screen
+keyboard after a mouse command is executed in response to a
+`touchscreen-end' event.")
+
+(defvar touch-screen-current-timer nil
+  "Timer used to track long-presses.
+This is always cleared upon any significant state change.")
+
+(defvar touch-screen-translate-prompt nil
+  "Prompt given to the touch screen translation function.
+If non-nil, the touch screen key event translation machinery
+is being called from `read-sequence' or some similar function.")
+
+(defcustom touch-screen-display-keyboard nil
+  "If non-nil, always display the on screen keyboard.
+A buffer local value means to always display the on screen
+keyboard when the buffer is selected."
+  :type 'boolean
+  :group 'mouse
+  :version "30.1")
+
+(defcustom touch-screen-delay 0.7
+  "Delay in seconds before Emacs considers a touch to be a long-press."
+  :type 'number
+  :group 'mouse
+  :version "30.1")
+
+(defcustom touch-screen-precision-scroll nil
+  "Whether or not to use precision scrolling for touch screens.
+See `pixel-scroll-precision-mode' for more details."
+  :type 'boolean
+  :group 'mouse
+  :version "30.1")
+
+(defcustom touch-screen-word-select nil
+  "Whether or not to select whole words while dragging to select.
+If non-nil, long-press events (see `touch-screen-delay') followed
+by dragging will try to select entire words."
+  :type 'boolean
+  :group 'mouse
+  :version "30.1")
+
+(defcustom touch-screen-extend-selection nil
+  "If non-nil, restart drag-to-select upon a tap on point or mark.
+When enabled, tapping on the character containing the point or
+mark will resume dragging where it left off while the region is
+active."
+  :type 'boolean
+  :group 'mouse
+  :version "30.1")
+
+(defcustom touch-screen-preview-select nil
+  "If non-nil, display a preview while selecting text.
+When enabled, a preview of the visible line within the window
+will be displayed in the echo area while dragging combined with
+an indication of the position of point within that line."
+  :type 'boolean
+  :group 'mouse
+  :version "30.1")
+
+(defvar-local touch-screen-word-select-bounds nil
+  "The start and end positions of the word last selected.
+Normally a cons of those two positions or nil if no word was
+selected.")
+
+(defvar-local touch-screen-word-select-initial-word nil
+  "The start and end positions of the first word to be selected.
+Used in an attempt to keep this word selected during later
+dragging.")
+
+
+
+;;; Scroll gesture.
+
+(defun touch-screen-relative-xy (posn window)
+  "Return the coordinates of POSN, a mouse position list.
+However, return the coordinates relative to WINDOW.
+
+If (posn-window posn) is the same as window, simply return the
+coordinates in POSN.  Otherwise, convert them to the frame, and
+then back again.
+
+If WINDOW is the symbol `frame', simply convert the coordinates
+to the frame that they belong in."
+  (if (or (eq (posn-window posn) window)
+          (and (eq window 'frame)
+               (framep (posn-window posn))))
+      (posn-x-y posn)
+    (let ((xy (posn-x-y posn))
+          (edges (and (windowp window)
+                      (window-inside-pixel-edges window))))
+      ;; Make the X and Y positions frame relative.
+      (when (windowp (posn-window posn))
+        (let ((edges (window-inside-pixel-edges
+                      (posn-window posn))))
+          (setq xy (cons (+ (car xy) (car edges))
+                         (+ (cdr xy) (cadr edges))))))
+      (if (eq window 'frame)
+          xy
+        ;; Make the X and Y positions window relative again.
+        (cons (- (car xy) (car edges))
+              (- (cdr xy) (cadr edges)))))))
+
+(defun touch-screen-handle-scroll (dx dy)
+  "Scroll the display assuming that a touch point has moved by DX and DY.
+Perform vertical scrolling by DY, using `pixel-scroll-precision'
+if `touch-screen-precision-scroll' is enabled.  Next, perform
+horizontal scrolling according to the movement in DX."
+  ;; Perform vertical scrolling first.  Do not ding at buffer limits.
+  ;; Show a message instead.
+  (condition-case nil
+      (if touch-screen-precision-scroll
+          (progn
+            (if (> dy 0)
+                (pixel-scroll-precision-scroll-down-page dy)
+              (pixel-scroll-precision-scroll-up-page (- dy)))
+            ;; Now set `lines-vscrolled' to an value that will result
+            ;; in hscroll being disabled if dy looks as if a
+            ;; significant amount of scrolling is about to take
+            ;; Otherwise, horizontal scrolling may then interfere with
+            ;; precision scrolling.
+            (when (> (abs dy) 10)
+              (setcar (nthcdr 7 touch-screen-current-tool) 10)))
+        ;; Start conventional scrolling.  First, determine the
+        ;; direction in which the scrolling is taking place.  Load the
+        ;; accumulator value.
+        (let ((accumulator (or (nth 5 touch-screen-current-tool) 0))
+              (window (cadr touch-screen-current-tool))
+              (lines-vscrolled (or (nth 7 touch-screen-current-tool) 0)))
+          (setq accumulator (+ accumulator dy)) ; Add dy.
+          ;; Figure out how much it has scrolled and how much remains
+          ;; on the top or bottom of the window.
+          (while (catch 'again
+                   (let* ((line-height (window-default-line-height window)))
+                     (if (and (< accumulator 0)
+                              (>= (- accumulator) line-height))
+                         (progn
+                           (setq accumulator (+ accumulator line-height))
+                           (scroll-down 1)
+                           (setq lines-vscrolled (1+ lines-vscrolled))
+                           (when (not (zerop accumulator))
+                             ;; If there is still an outstanding
+                             ;; amount to scroll, do this again.
+                             (throw 'again t)))
+                       (when (and (> accumulator 0)
+                                  (>= accumulator line-height))
+                         (setq accumulator (- accumulator line-height))
+                         (scroll-up 1)
+                         (setq lines-vscrolled (1+ lines-vscrolled))
+                         (when (not (zerop accumulator))
+                           ;; If there is still an outstanding amount
+                           ;; to scroll, do this again.
+                           (throw 'again t)))))
+                   ;; Scrolling is done.  Move the accumulator back to
+                   ;; touch-screen-current-tool and break out of the
+                   ;; loop.
+                   (setcar (nthcdr 5 touch-screen-current-tool) accumulator)
+                   (setcar (nthcdr 7 touch-screen-current-tool)
+                           lines-vscrolled)
+                   nil))))
+    (beginning-of-buffer
+     (message (error-message-string '(beginning-of-buffer))))
+    (end-of-buffer
+     (message (error-message-string '(end-of-buffer)))))
+
+  ;; Perform horizontal scrolling by DX, as this does not signal at
+  ;; the beginning of the buffer.
+  (let ((accumulator (or (nth 6 touch-screen-current-tool) 0))
+        (window (cadr touch-screen-current-tool))
+        (lines-vscrolled (or (nth 7 touch-screen-current-tool) 0))
+        (lines-hscrolled (or (nth 8 touch-screen-current-tool) 0)))
+    (setq accumulator (+ accumulator dx)) ; Add dx;
+    ;; Figure out how much it has scrolled and how much remains on the
+    ;; left or right of the window.  If a line has already been
+    ;; vscrolled but no hscrolling has happened, don't hscroll, as
+    ;; otherwise it is too easy to hscroll by accident.
+    (if (or (> lines-hscrolled 0)
+            (< lines-vscrolled 1))
+        (while (catch 'again
+                 (let* ((column-width (frame-char-width (window-frame 
window))))
+                   (if (and (< accumulator 0)
+                            (>= (- accumulator) column-width))
+                       (progn
+                         (setq accumulator (+ accumulator column-width))
+                         (scroll-right 1)
+                         (setq lines-hscrolled (1+ lines-hscrolled))
+                         (when (not (zerop accumulator))
+                           ;; If there is still an outstanding amount
+                           ;; to scroll, do this again.
+                           (throw 'again t)))
+                     (when (and (> accumulator 0)
+                                (>= accumulator column-width))
+                       (setq accumulator (- accumulator column-width))
+                       (scroll-left 1)
+                       (setq lines-hscrolled (1+ lines-hscrolled))
+                       (when (not (zerop accumulator))
+                         ;; If there is still an outstanding amount to
+                         ;; scroll, do this again.
+                         (throw 'again t)))))
+                 ;; Scrolling is done.  Move the accumulator back to
+                 ;; touch-screen-current-tool and break out of the
+                 ;; loop.
+                 (setcar (nthcdr 6 touch-screen-current-tool) accumulator)
+                 (setcar (nthcdr 8 touch-screen-current-tool) lines-hscrolled)
+                 nil)))))
+
+(defun touch-screen-scroll (event)
+  "Scroll the window within EVENT, a `touchscreen-scroll' event.
+If `touch-screen-precision-scroll', scroll the window vertically
+by the number of pixels specified within that event.  Else,
+scroll the window by one line for every
+`window-default-line-height' pixels worth of movement.
+
+If EVENT also specifies horizontal motion and no significant
+amount of vertical scrolling has taken place, also scroll the
+window horizontally in conjunction with the number of pixels in
+the event."
+  (interactive "e")
+  (let ((window (nth 1 event))
+        (dx (nth 2 event))
+        (dy (nth 3 event)))
+    (with-selected-window window
+      (touch-screen-handle-scroll dx dy))))
+
+(global-set-key [touchscreen-scroll] #'touch-screen-scroll)
+
+
+
+;;; Drag-to-select gesture.
+
+(defun touch-screen-hold (event)
+  "Handle a long press EVENT.
+Ding and select the window at EVENT, then activate the mark.  If
+`touch-screen-word-select' is enabled, try to select the whole
+word around EVENT; otherwise, set point to the location of EVENT."
+  (interactive "e")
+  (let* ((posn (cadr event))
+         (point (posn-point posn))
+         (window (posn-window posn)))
+    (when (and point
+               ;; Make sure WINDOW is not an inactive minibuffer
+               ;; window.
+               (or (not (eq window
+                            (minibuffer-window
+                             (window-frame window))))
+                   (minibuffer-window-active-p window)))
+      (beep)
+      (select-window window)
+      (if (or (not touch-screen-word-select)
+              (when-let* ((char (char-after point))
+                          (class (char-syntax char)))
+                ;; Don't select words if point isn't inside a word
+                ;; constituent or similar.
+                (not (or (eq class ?w) (eq class ?_)))))
+          (progn
+            ;; Set the mark and activate it.
+            (setq touch-screen-word-select-initial-word nil
+                  touch-screen-word-select-bounds nil)
+            (push-mark point)
+            (goto-char point)
+            (activate-mark))
+        ;; Start word selection by trying to obtain the position
+        ;; around point.
+        (let ((word-start nil)
+              (word-end nil))
+          (unless (posn-object posn)
+            ;; If there's an object under POSN avoid trying to
+            ;; ascertain the bounds of the word surrounding it.
+            (save-excursion
+              (goto-char point)
+              (forward-word-strictly)
+              ;; Set word-end to ZV if there is no word after this
+              ;; one.
+              (setq word-end (point))
+              ;; Now try to move backwards.  Set word-start to BEGV if
+              ;; this word is there.
+              (backward-word-strictly)
+              (setq word-start (point))))
+          ;; Check if word-start and word-end are identical, if there
+          ;; is an object under POSN, or if point is looking at or
+          ;; outside a word.
+          (if (or (eq word-start word-end)
+                  (>= word-start point))
+              (progn
+                ;; If so, clear the bounds and set and activate the
+                ;; mark.
+                (setq touch-screen-word-select-bounds nil
+                      touch-screen-word-select-initial-word nil)
+                (push-mark point)
+                (goto-char point)
+                (activate-mark))
+            ;; Otherwise, select the word.  Move point to either the
+            ;; end or the start of the word, depending on which is
+            ;; closer to EVENT.
+            (let ((diff-beg (- point word-start))
+                  (diff-end (- word-end point))
+                  use-end)
+              (if (> diff-beg diff-end)
+                  ;; Set the point to the end of the word.
+                  (setq use-end t)
+                (if (< diff-end diff-beg)
+                    (setq use-end nil)
+                  ;; POINT is in the middle of the word.  Use its
+                  ;; window coordinates to establish whether or not it
+                  ;; is closer to the start of the word or to the end
+                  ;; of the word.
+                  (let ((posn-beg (posn-at-point word-start))
+                        (posn-end (posn-at-point word-end)))
+                    ;; Give up if there's an object at either of those
+                    ;; positions, or they're not on the same row.
+                    ;; If one of the positions isn't visible, use the
+                    ;; window end.
+                    (if (and posn-beg posn-end
+                             (not (posn-object posn-beg))
+                             (not (posn-object posn-end))
+                             (eq (cdr (posn-col-row posn-beg))
+                                 (cdr (posn-col-row posn-end))))
+                        (setq use-end nil)
+                      ;; Compare the pixel positions.
+                      (setq point (car (posn-x-y posn))
+                            diff-beg (- point (car (posn-x-y posn-beg)))
+                            diff-end (- (car (posn-x-y posn-end)) point))
+                      ;; Now determine whether or not point should be
+                      ;; moved to the end.
+                      (setq use-end (>= diff-beg diff-end))))))
+              (if use-end
+                  (progn
+                    (push-mark word-start)
+                    (activate-mark)
+                    (goto-char word-end))
+                (progn
+                    (push-mark word-end)
+                    (activate-mark)
+                    (goto-char word-start)))
+              ;; Record the bounds of the selected word.
+              (setq touch-screen-word-select-bounds
+                    (cons word-start word-end)
+                    ;; Save this for the benefit of touch-screen-drag.
+                    touch-screen-word-select-initial-word
+                    (cons word-start word-end)))))))))
+
+(defun touch-screen-preview-select ()
+  "Display a preview of the line around point in the echo area.
+Unless the minibuffer is an active or the current line is
+excessively tall, display an indication of the position of point
+and the contents of the visible line around it within the echo
+area.
+
+If the selected window is hscrolled or lines may be truncated,
+attempt to find the extents of the text between column 0 and the
+right most column of the window using `posn-at-x-y'."
+  (interactive)
+  ;; First, establish that the minibuffer isn't active and the line
+  ;; isn't taller than two times the frame character height.
+  (unless (or (> (minibuffer-depth) 0)
+              ;; The code below doesn't adapt well to buffers
+              ;; containing long lines.
+              (long-line-optimizations-p)
+              (let ((window-line-height (window-line-height))
+                    (maximum-height (* 2 (frame-char-height))))
+                (unless window-line-height
+                  ;; `window-line-height' isn't available.
+                  ;; Redisplay first and try to ascertain the height
+                  ;; of the line again.
+                  (redisplay t)
+                  (setq window-line-height (window-line-height)))
+                ;; `window-line-height' might still be unavailable.
+                (and window-line-height
+                     (> (car window-line-height)
+                        maximum-height))))
+    (catch 'hscrolled-away
+      (let ((beg nil) end string y)
+        ;; Detect whether or not the window is hscrolled.  If it
+        ;; is, set beg to the location of the first column
+        ;; instead.
+        (when (> (window-hscroll) 0)
+          (setq y (+ (or (cdr (posn-x-y (posn-at-point)))
+                         (throw 'hscrolled-away t))
+                     (window-header-line-height)
+                     (window-tab-line-height)))
+          (let* ((posn (posn-at-x-y 0 y))
+                 (point (posn-point posn)))
+            (setq beg point)))
+        ;; Check if lines are being truncated; if so, use the
+        ;; character at the end of the window as the end of the
+        ;; text to be displayed, as the visual line may extend
+        ;; past the window.
+        (when (or truncate-lines beg) ; truncate-lines or hscroll.
+          (setq y (or y (+ (or (cdr (posn-x-y (posn-at-point)))
+                               (throw 'hscrolled-away t))
+                           (window-header-line-height)
+                           (window-tab-line-height))))
+          (let* ((posn (posn-at-x-y (1- (window-width nil t)) y))
+                 (point (posn-point posn)))
+            (setq end point)))
+        ;; Now find the rest of the visual line.
+        (save-excursion
+          (unless beg
+            (beginning-of-visual-line)
+            (setq beg (point)))
+          (unless end
+            (end-of-visual-line)
+            (setq end (point))))
+        ;; Obtain a substring containing the beginning of the
+        ;; visual line and the end.
+        (setq string (buffer-substring beg end))
+        ;; Hack `invisible' properties within the new string.
+        ;; Look for each change of the property that is a variable
+        ;; name and replace it with its actual value according to
+        ;; `buffer-invisibility-spec'.
+        (when (listp buffer-invisibility-spec)
+          (let ((index 0)
+                (property (get-text-property 0
+                                             'invisible
+                                             string))
+                index1 invisible)
+            (while index
+              ;; Find the end of this text property.
+              (setq index1 (next-single-property-change index
+                                                        'invisible
+                                                        string))
+              ;; Replace the property with whether or not it is
+              ;; non-nil.
+              (when property
+                (setq invisible nil)
+                (catch 'invisible
+                  (dolist (spec buffer-invisibility-spec)
+                    ;; Process one element of the buffer
+                    ;; invisibility specification.
+                    (if (consp spec)
+                        (when (eq (cdr spec) 't)
+                          ;; (ATOM . t) makes N invisible if N is
+                          ;; equal to ATOM or a list containing
+                          ;; ATOM.
+                          (when (or (eq (car spec) property)
+                                    (and (listp spec)
+                                         (memq (car spec) invisible)))
+                            (throw 'invisible (setq invisible t))))
+                      ;; Otherwise, N is invisible if SPEC is
+                      ;; equal to N.
+                      (when (eq spec property)
+                        (throw 'invisible (setq invisible t))))))
+                (put-text-property index (or index1
+                                             (- end beg))
+                                   'invisible invisible string))
+              ;; Set index to that of the next text property and
+              ;; continue.
+              (setq index index1
+                    property (and index1
+                                  (get-text-property index1
+                                                     'invisible
+                                                     string))))))
+        (let ((resize-mini-windows t) difference width
+              (message-log-max nil))
+          ;; Find the offset of point from beg and display a cursor
+          ;; below.
+          (setq difference (- (point) beg)
+                width (string-pixel-width
+                       (substring string 0 difference)))
+          (message "%s\n%s^" string
+                   (propertize " "
+                               'display (list 'space
+                                              :width (list width)))))
+        nil))))
+
+(defun touch-screen-drag (event)
+  "Handle a drag EVENT by setting the region to its new point.
+If `touch-screen-word-select' and EVENT lies outside the last
+word that was selected, select the word that now contains POINT.
+Scroll the window if EVENT's coordinates are outside its text
+area."
+  (interactive "e")
+  (let* ((posn (cadr event)) ; Position of the tool.
+         (point (posn-point posn)) ; Point of the event.
+         ;; Window where the tap originated.
+         (window (nth 1 touch-screen-current-tool))
+         ;; The currently selected window.  Used to redisplay within
+         ;; the correct window while scrolling.
+         (old-window (selected-window))
+         ;; Whether or not text should be selected word-by-word.
+         (word-select touch-screen-word-select)
+         ;; Cons containing the confines of the word initially
+         ;; selected when the touchpoint was first held down.
+         (initial touch-screen-word-select-initial-word)
+         initial-point)
+    ;; Keep dragging.
+    (with-selected-window window
+      ;; Figure out what character to go to.  If this posn is in the
+      ;; window, go to (posn-point posn).  If not, then go to the line
+      ;; before either window start or window end.
+      (setq initial-point (point))
+      (when (or (not point)
+                (not (eq point initial-point)))
+        (if (and (eq (posn-window posn) window)
+                 point
+                 ;; point must be visible in the window.  If it isn't,
+                 ;; the window must be scrolled.
+                 (pos-visible-in-window-p point))
+            (let* ((bounds touch-screen-word-select-bounds)
+                   (maybe-select-word (or (not touch-screen-word-select)
+                                          (or (not bounds)
+                                              (> point (cdr bounds))
+                                              (< point (car bounds))))))
+              (if (and word-select
+                       ;; point is now outside the last word selected.
+                       maybe-select-word
+                       (not (posn-object posn))
+                       (when-let* ((char (char-after point))
+                                   (class (char-syntax char)))
+                         ;; Don't select words if point isn't inside a
+                         ;; word constituent or similar.
+                         (or (eq class ?w) (eq class ?_))))
+                  ;; Determine the confines of the word containing
+                  ;; POINT.
+                  (let (word-start word-end)
+                    (save-excursion
+                      (goto-char point)
+                      (forward-word-strictly)
+                      ;; Set word-end to ZV if there is no word after
+                      ;; this one.
+                      (setq word-end (point))
+                      ;; Now try to move backwards.  Set word-start to
+                      ;; BEGV if this word is there.
+                      (backward-word-strictly)
+                      (setq word-start (point)))
+                    (let ((mark (mark)))
+                      ;; Extend the region to cover either word-end or
+                      ;; word-start; whether to goto word-end or
+                      ;; word-start is subject to the position of the
+                      ;; mark relative to point.
+                      (if (< word-start mark)
+                          ;; The start of the word is behind mark.
+                          ;; Extend the region towards the start.
+                          (goto-char word-start)
+                        ;; Else, go to the end of the word.
+                        (goto-char word-end))
+                      ;; If point is less than mark, which is is less
+                      ;; than the end of the word that was originally
+                      ;; selected, try to keep it selected by moving
+                      ;; mark there.
+                      (when (and initial (<= (point) mark)
+                                 (< mark (cdr initial)))
+                        (set-mark (cdr initial)))
+                      ;; Do the opposite when the converse is true.
+                      (when (and initial (>= (point) mark)
+                                 (> mark (car initial)))
+                        (set-mark (car initial))))
+                    (if bounds
+                        (progn (setcar bounds word-start)
+                               (setcdr bounds word-end))
+                      (setq touch-screen-word-select-bounds
+                            (cons word-start word-end))))
+                (when maybe-select-word
+                  (goto-char (posn-point posn))
+                  (when initial
+                    ;; If point is less than mark, which is is less
+                    ;; than the end of the word that was originally
+                    ;; selected, try to keep it selected by moving
+                    ;; mark there.
+                    (when (and (<= (point) (mark))
+                               (< (mark) (cdr initial)))
+                      (set-mark (cdr initial)))
+                    ;; Do the opposite when the converse is true.
+                    (when (and (>= (point) (mark))
+                               (> (mark) (car initial)))
+                      (set-mark (car initial))))
+                  (setq touch-screen-word-select-bounds nil)))
+              ;; Finally, display a preview of the line around point
+              ;; if requested by the user.
+              (when (and touch-screen-preview-select
+                         (not (eq (point) initial-point)))
+                (touch-screen-preview-select)))
+          ;; POSN is outside the window.  Scroll accordingly.
+          (let* ((relative-xy
+                  (touch-screen-relative-xy posn window))
+                 (xy (posn-x-y posn))
+                 ;; The height of the window's text area.
+                 (body-height (window-body-height nil t))
+                 ;; This is used to find the character closest to
+                 ;; POSN's column at the bottom of the window.
+                 (height (- body-height
+                            ;; Use the last row of the window, not its
+                            ;; last pixel.
+                            (frame-char-height)))
+                 (midpoint (/ body-height 2))
+                 (scroll-conservatively 101))
+            (cond
+             ((< (cdr relative-xy) midpoint)
+              ;; POSN is before half the window, yet POINT does not
+              ;; exist or is not completely visible within.  Scroll
+              ;; downwards.
+              (ignore-errors
+                ;; Scroll down by a single line.
+                (scroll-down 1)
+                ;; After scrolling, look up the new posn at EVENT's
+                ;; column and go there.
+                (setq posn (posn-at-x-y (car xy) 0)
+                      point (posn-point posn))
+                (if point
+                    (goto-char point)
+                  ;; If there's no buffer position at that column, go
+                  ;; to the window start.
+                  (goto-char (window-start)))
+                ;; If word selection is enabled, now try to keep the
+                ;; initially selected word within the active region.
+                (when word-select
+                  (when initial
+                    ;; If point is less than mark, which is is less
+                    ;; than the end of the word that was originally
+                    ;; selected, try to keep it selected by moving
+                    ;; mark there.
+                    (when (and (<= (point) (mark))
+                               (< (mark) (cdr initial)))
+                      (set-mark (cdr initial)))
+                    ;; Do the opposite when the converse is true.
+                    (when (and (>= (point) (mark))
+                               (> (mark) (car initial)))
+                      (set-mark (car initial))))
+                  (setq touch-screen-word-select-bounds nil))
+                ;; Display a preview of the line now around point if
+                ;; requested by the user.
+                (when touch-screen-preview-select
+                  (touch-screen-preview-select))
+                ;; Select old-window, so that redisplay doesn't
+                ;; display WINDOW as selected if it isn't already.
+                (with-selected-window old-window
+                  ;; Now repeat this every `mouse-scroll-delay' until
+                  ;; input becomes available, but scroll down a few
+                  ;; more lines.
+                  (while (sit-for mouse-scroll-delay)
+                    ;; Select WINDOW again.
+                    (with-selected-window window
+                      ;; Keep scrolling down until input becomes
+                      ;; available.
+                      (scroll-down 4)
+                      ;; After scrolling, look up the new posn at
+                      ;; EVENT's column and go there.
+                      (setq posn (posn-at-x-y (car xy) 0)
+                            point (posn-point posn))
+                      (if point
+                          (goto-char point)
+                        ;; If there's no buffer position at that
+                        ;; column, go to the window start.
+                        (goto-char (window-start)))
+                      ;; If word selection is enabled, now try to keep
+                      ;; the initially selected word within the active
+                      ;; region.
+                      (when word-select
+                        (when initial
+                          ;; If point is less than mark, which is is
+                          ;; less than the end of the word that was
+                          ;; originally selected, try to keep it
+                          ;; selected by moving mark there.
+                          (when (and (<= (point) (mark))
+                                     (< (mark) (cdr initial)))
+                            (set-mark (cdr initial)))
+                          ;; Do the opposite when the converse is true.
+                          (when (and (>= (point) (mark))
+                                     (> (mark) (car initial)))
+                            (set-mark (car initial))))
+                        (setq touch-screen-word-select-bounds nil))
+                      ;; Display a preview of the line now around
+                      ;; point if requested by the user.
+                      (when touch-screen-preview-select
+                        (touch-screen-preview-select))))))
+              (setq touch-screen-word-select-bounds nil))
+             ((>= (cdr relative-xy) midpoint)
+              ;; Default to scrolling upwards even if POSN is still
+              ;; within the confines of the window.  If POINT is
+              ;; partially visible, and the branch above hasn't been
+              ;; taken it must be somewhere at the bottom of the
+              ;; window, so scroll downwards.
+              (ignore-errors
+                ;; Scroll up by a single line.
+                (scroll-up 1)
+                ;; After scrolling, look up the new posn at EVENT's
+                ;; column and go there.
+                (setq posn (posn-at-x-y (car xy) height)
+                      point (posn-point posn))
+                (if point
+                    (goto-char point)
+                  ;; If there's no buffer position at that column, go
+                  ;; to the window start.
+                  (goto-char (window-end nil t)))
+                ;; If word selection is enabled, now try to keep
+                ;; the initially selected word within the active
+                ;; region.
+                (when word-select
+                  (when initial
+                    ;; If point is less than mark, which is is less
+                    ;; than the end of the word that was originally
+                    ;; selected, try to keep it selected by moving
+                    ;; mark there.
+                    (when (and (<= (point) (mark))
+                               (< (mark) (cdr initial)))
+                      (set-mark (cdr initial)))
+                    ;; Do the opposite when the converse is true.
+                    (when (and (>= (point) (mark))
+                               (> (mark) (car initial)))
+                      (set-mark (car initial))))
+                  (setq touch-screen-word-select-bounds nil))
+                ;; Display a preview of the line now around point if
+                ;; requested by the user.
+                (when touch-screen-preview-select
+                  (touch-screen-preview-select))
+                ;; Select old-window, so that redisplay doesn't
+                ;; display WINDOW as selected if it isn't already.
+                (with-selected-window old-window
+                  ;; Now repeat this every `mouse-scroll-delay' until
+                  ;; input becomes available, but scroll down a few
+                  ;; more lines.
+                  (while (sit-for mouse-scroll-delay)
+                    ;; Select WINDOW again.
+                    (with-selected-window window
+                      ;; Keep scrolling down until input becomes
+                      ;; available.
+                      (scroll-up 4)
+                      ;; After scrolling, look up the new posn at
+                      ;; EVENT's column and go there.
+                      (setq posn (posn-at-x-y (car xy) height)
+                            point (posn-point posn))
+                      (if point
+                          (goto-char point)
+                        ;; If there's no buffer position at that
+                        ;; column, go to the window start.
+                        (goto-char (window-end nil t)))
+                      ;; If word selection is enabled, now try to keep
+                      ;; the initially selected word within the active
+                      ;; region.
+                      (when word-select
+                        (when initial
+                          ;; If point is less than mark, which is is less
+                          ;; than the end of the word that was originally
+                          ;; selected, try to keep it selected by moving
+                          ;; mark there.
+                          (when (and (<= (point) (mark))
+                                     (< (mark) (cdr initial)))
+                            (set-mark (cdr initial)))
+                          ;; Do the opposite when the converse is true.
+                          (when (and (>= (point) (mark))
+                                     (> (mark) (car initial)))
+                            (set-mark (car initial))))
+                        (setq touch-screen-word-select-bounds nil))
+                      ;; Display a preview of the line now around
+                      ;; point if requested by the user.
+                      (when touch-screen-preview-select
+                        (touch-screen-preview-select))))))))))))))
+
+(defun touch-screen-restart-drag (event)
+  "Restart dragging to select text.
+Set point to the location of EVENT within its window while
+keeping the bounds of the region intact, and set up state for
+`touch-screen-drag'."
+  (interactive "e")
+  (let* ((posn (event-start event))
+         (window (posn-window posn))
+         (point (posn-point posn)))
+    (with-selected-window window
+      (let ((current-point (point))
+            (current-mark (mark)))
+        ;; Ensure that mark and point haven't changed since EVENT was
+        ;; generated, and the region is still active.
+        (when (or (eq point current-point)
+                  (eq point current-mark)
+                  (region-active-p))
+          (when (eq point current-mark)
+            ;; Exchange point and mark.
+            (exchange-point-and-mark))
+          ;; Clear the state necessary to set up dragging.  Don't try
+          ;; to select entire words immediately after dragging starts,
+          ;; to allow for fine grained selection inside a word.
+          (setq touch-screen-word-select-bounds nil
+                touch-screen-word-select-initial-word nil))))))
+
+(global-set-key [touchscreen-hold] #'touch-screen-hold)
+(global-set-key [touchscreen-drag] #'touch-screen-drag)
+(global-set-key [touchscreen-restart-drag] #'touch-screen-restart-drag)
+
+
+
+;; Touch screen event translation.  The code here translates raw touch
+;; screen events into `touchscreen-scroll' events and mouse events in
+;; a ``DWIM'' fashion, consulting the keymaps at the position of the
+;; mouse event to determine the best course of action, while also
+;; recognizing drag-to-select and other gestures.
+
+(defun touch-screen-handle-timeout (arg)
+  "Start the touch screen timeout or handle it depending on ARG.
+When ARG is nil, start the `touch-screen-current-timer' to go off
+in `touch-screen-delay' seconds, and call this function with ARG
+t.
+
+When ARG is t, set the fourth element of
+`touch-screen-current-tool' to `held', and generate a
+`touchscreen-hold' event at the original position of that tool."
+  (if (not arg)
+      ;; Cancel the touch screen long-press timer, if it is still
+      ;; there by any chance.
+      (progn
+        (when touch-screen-current-timer
+          (cancel-timer touch-screen-current-timer))
+        (setq touch-screen-current-timer
+              (run-at-time touch-screen-delay nil
+                           #'touch-screen-handle-timeout
+                           t)))
+    ;; Set touch-screen-current-timer to nil.
+    (setq touch-screen-current-timer nil)
+    (when touch-screen-current-tool
+      ;; Set the state to `held'.
+      (setcar (nthcdr 3 touch-screen-current-tool) 'held)
+      ;; Generate an input event at the original position of the mark.
+      ;; This assumes that the timer is running within
+      ;; `touch-screen-translate-touch'.
+      (let ((posn (nth 4 touch-screen-current-tool)))
+        (throw 'input-event (list 'touchscreen-hold posn))))))
+
+(defun touch-screen-handle-point-update (point)
+  "Notice that the touch point POINT has changed position.
+Perform the editing operations or throw to the input translation
+function with an input event tied to any gesture that is
+recognized.
+
+POINT must be the touch point currently being tracked as
+`touch-screen-current-tool'.
+
+If the fourth element of `touch-screen-current-tool' is nil, then
+the touch has just begun.  Determine how much POINT has moved.
+If POINT has moved upwards or downwards by a significant amount,
+then set the fourth element to `scroll'.  Then, generate a
+`touchscreen-scroll' event with the window that POINT was
+initially placed upon, and pixel deltas describing how much point
+has moved relative to its previous position in the X and Y axes.
+
+If the fourth element of `touchscreen-current-tool' is `scroll',
+then generate a `touchscreen-scroll' event with the window that
+qPOINT was initially placed upon, and pixel deltas describing how
+much point has moved relative to its previous position in the X
+and Y axes.
+
+If the fourth element of `touch-screen-current-tool' is
+`mouse-drag' and `track-mouse' is non-nil, then generate a
+`mouse-movement' event with the position of POINT.
+
+If the fourth element of `touch-screen-current-tool' is `held',
+then the touch has been held down for some time.  If motion
+happens, set the field to `drag'.  Then, generate a
+`touchscreen-drag' event.
+
+If the fourth element of `touch-screen-current-tool' is
+`restart-drag', set the field to `drag' and generate a
+`touchscreen-drag'.
+
+If the fourth element of `touch-screen-current-tool' is `drag',
+then move point to the position of POINT."
+  (let ((window (nth 1 touch-screen-current-tool))
+        (what (nth 3 touch-screen-current-tool)))
+    (cond ((null what)
+           (let* ((posn (cdr point))
+                  (last-posn (nth 2 touch-screen-current-tool))
+                  (original-posn (nth 4 touch-screen-current-tool))
+                  ;; Now get the position of X and Y relative to
+                  ;; WINDOW.
+                  (relative-xy
+                   (touch-screen-relative-xy posn window))
+                  (col (and (not (posn-area original-posn))
+                            (car (posn-col-row original-posn
+                                               (posn-window posn)))))
+                  ;; Don't start horizontal scrolling if the touch
+                  ;; point originated within two columns of the window
+                  ;; edges, as systems like Android use those two
+                  ;; columns to implement gesture navigation.
+                  (diff-x-eligible
+                   (and col (> col 2)
+                        (< col (- (window-width window) 2))))
+                  (diff-x (- (car last-posn) (car relative-xy)))
+                  (diff-y (- (cdr last-posn) (cdr relative-xy))))
+             (when (or (> diff-y 10)
+                       (and diff-x-eligible
+                            (> diff-x (frame-char-width)))
+                       (< diff-y -10)
+                       (and diff-x-eligible
+                            (< diff-x (- (frame-char-width)))))
+               (setcar (nthcdr 3 touch-screen-current-tool)
+                       'scroll)
+               (setcar (nthcdr 2 touch-screen-current-tool)
+                       relative-xy)
+               ;; Cancel the touch screen long-press timer, if it is
+               ;; still there by any chance.
+               (when touch-screen-current-timer
+                 (cancel-timer touch-screen-current-timer)
+                 (setq touch-screen-current-timer nil))
+               ;; Generate a `touchscreen-scroll' event with `diff-x'
+               ;; and `diff-y'.
+               (throw 'input-event
+                      (list 'touchscreen-scroll
+                            window diff-x diff-y)))))
+          ((eq what 'scroll)
+           ;; Cancel the touch screen long-press timer, if it is still
+           ;; there by any chance.
+           (when touch-screen-current-timer
+             (cancel-timer touch-screen-current-timer)
+             (setq touch-screen-current-timer nil))
+           (let* ((posn (cdr point))
+                  (last-posn (nth 2 touch-screen-current-tool))
+                  ;; Now get the position of X and Y relative to
+                  ;; WINDOW.
+                  (relative-xy
+                   (touch-screen-relative-xy posn window))
+                  (diff-x (- (car last-posn) (car relative-xy)))
+                  (diff-y (- (cdr last-posn) (cdr relative-xy))))
+             (setcar (nthcdr 3 touch-screen-current-tool)
+                     'scroll)
+             (setcar (nthcdr 2 touch-screen-current-tool)
+                     relative-xy)
+             (unless (and (zerop diff-x) (zerop diff-y))
+               (throw 'input-event
+                      ;; Generate a `touchscreen-scroll' event with
+                      ;; `diff-x' and `diff-y'.
+                      (list 'touchscreen-scroll
+                            window diff-x diff-y)))))
+          ((eq what 'mouse-drag)
+           ;; There was a `down-mouse-1' event bound at the starting
+           ;; point of the event.  Generate a mouse-motion event if
+           ;; mouse movement is being tracked.
+           (when track-mouse
+             (throw 'input-event (list 'mouse-movement
+                                       (cdr point)))))
+          ((eq what 'held)
+           (let* ((posn (cdr point)))
+             ;; Now start dragging.
+             (setcar (nthcdr 3 touch-screen-current-tool)
+                     'drag)
+             ;; Generate a (touchscreen-drag POSN) event.
+             ;; `touchscreen-hold' was generated when the timeout
+             ;; fired.
+             (throw 'input-event (list 'touchscreen-drag posn))))
+          ((eq what 'restart-drag)
+           (let* ((posn (cdr point)))
+             ;; Now start dragging.
+             (setcar (nthcdr 3 touch-screen-current-tool)
+                     'drag)
+             ;; Generate a (touchscreen-drag POSN) event.
+             ;; `touchscreen-restart-drag' was generated when the
+             ;; timeout fired.
+             (throw 'input-event (list 'touchscreen-drag posn))))
+          ((eq what 'drag)
+           (let* ((posn (cdr point)))
+             ;; Generate a (touchscreen-drag POSN) event.
+             (throw 'input-event (list 'touchscreen-drag posn)))))))
+
+(defun touch-screen-window-selection-changed (frame)
+  "Notice that FRAME's selected window has changed.
+Cancel any timer that is supposed to hide the keyboard in
+response to the minibuffer being closed."
+  (with-selected-frame frame
+    (unless (and (or buffer-read-only
+                     (get-text-property (point) 'read-only))
+                 ;; Don't hide the on-screen keyboard if it's always
+                 ;; supposed to be displayed.
+                 (not touch-screen-display-keyboard))
+      ;; Prevent hiding the minibuffer from hiding the on screen
+      ;; keyboard.
+      (when minibuffer-on-screen-keyboard-timer
+        (cancel-timer minibuffer-on-screen-keyboard-timer)
+        (setq minibuffer-on-screen-keyboard-timer nil)))))
+
+(defun touch-screen-handle-point-up (point prefix)
+  "Notice that POINT has been removed from the screen.
+POINT should be the point currently tracked as
+`touch-screen-current-tool'.
+PREFIX should be a virtual function key used to look up key
+bindings.
+
+If the fourth element of `touch-screen-current-tool' is nil or
+`restart-drag', move point to the position of POINT, selecting
+the window under POINT as well, and deactivate the mark; if there
+is a button or link at POINT, call the command bound to `mouse-2'
+there.  Otherwise, call the command bound to `mouse-1'.
+
+If the fourth element of `touch-screen-current-tool' is
+`mouse-drag', then generate either a `mouse-1' or a
+`drag-mouse-1' event depending on how far the position of POINT
+is from the starting point of the touch.
+
+If the fourth element of `touch-screen-current-tool' is
+`mouse-1-menu', then generate a `down-mouse-1' event at the
+original position of the tool to display its bound keymap as a
+menu.
+
+If the command being executed is listed in
+`touch-screen-set-point-commands' also display the on-screen
+keyboard if the current buffer and the character at the new point
+is not read-only."
+  (let ((what (nth 3 touch-screen-current-tool))
+        (posn (cdr point)) window point)
+    (cond ((or (null what)
+               ;; If dragging has been restarted but the touch point
+               ;; hasn't been moved, translate the sequence into a
+               ;; regular mouse click.
+               (eq what 'restart-drag))
+           (when (windowp (posn-window posn))
+             (setq point (posn-point posn)
+                   window (posn-window posn))
+             ;; Select the window that was tapped given that it isn't
+             ;; an inactive minibuffer window.
+             (when (or (not (eq window
+                                (minibuffer-window
+                                 (window-frame window))))
+                       (minibuffer-window-active-p window))
+               (select-window window))
+             ;; Now simulate a mouse click there.  If there is a link
+             ;; or a button, use mouse-2 to push it.
+             (let* ((event (list (if (or (mouse-on-link-p posn)
+                                         (and point (button-at point)))
+                                     'mouse-2
+                                   'mouse-1)
+                                 posn))
+                    ;; Look for the command bound to this event.
+                    (command (key-binding (if prefix
+                                                 (vector prefix
+                                                         (car event))
+                                            (vector (car event)))
+                                          t nil posn)))
+               (deactivate-mark)
+               (when point
+                 ;; This is necessary for following links.
+                 (goto-char point))
+               ;; Figure out if the on screen keyboard needs to be
+               ;; displayed.
+               (when command
+                 (if (memq command touch-screen-set-point-commands)
+                     (if touch-screen-translate-prompt
+                         ;; When a `mouse-set-point' command is
+                         ;; encountered and
+                         ;; `touch-screen-handle-touch' is being
+                         ;; called from the keyboard command loop,
+                         ;; call it immediately so that point is set
+                         ;; prior to the on screen keyboard being
+                         ;; displayed.
+                         (call-interactively command nil
+                                             (vector event))
+                       (if (and (or (not buffer-read-only)
+                                    touch-screen-display-keyboard)
+                                ;; Detect the splash screen and avoid
+                                ;; displaying the on screen keyboard
+                                ;; there.
+                                (not (equal (buffer-name) "*GNU Emacs*")))
+                           ;; Once the on-screen keyboard has been
+                           ;; opened, add
+                           ;; `touch-screen-window-selection-changed'
+                           ;; as a window selection change function
+                           ;; This then prevents it from being hidden
+                           ;; after exiting the minibuffer.
+                           (progn
+                             (add-hook 'window-selection-change-functions
+                                       #'touch-screen-window-selection-changed)
+                             (frame-toggle-on-screen-keyboard (selected-frame)
+                                                              nil))
+                         ;; Otherwise, hide the on screen keyboard
+                         ;; now.
+                         (frame-toggle-on-screen-keyboard (selected-frame) t))
+                       ;; But if it's being called from `describe-key'
+                       ;; or some such, return it as a key sequence.
+                       (throw 'input-event event)))
+                 ;; If not, return the event.
+                 (throw 'input-event event)))))
+          ((eq what 'mouse-drag)
+           ;; Generate a corresponding `mouse-1' event.
+           (let* ((new-window (posn-window posn))
+                  (new-point (posn-point posn))
+                  (old-posn (nth 4 touch-screen-current-tool))
+                  (old-window (posn-window posn))
+                  (old-point (posn-point posn)))
+             (throw 'input-event
+                    ;; If the position of the touch point hasn't
+                    ;; changed, or it doesn't start or end on a
+                    ;; window...
+                    (if (and (eq new-window old-window)
+                             (eq new-point old-point)
+                             (windowp new-window)
+                             (windowp old-window))
+                        ;; ... generate a mouse-1 event...
+                        (list 'mouse-1 posn)
+                      ;; ... otherwise, generate a drag-mouse-1 event.
+                      (list 'drag-mouse-1 (cons old-window
+                                                old-posn)
+                            (cons new-window posn))))))
+          ((eq what 'mouse-1-menu)
+           ;; Generate a `down-mouse-1' event at the position the tap
+           ;; took place.
+           (throw 'input-event
+                  (list 'down-mouse-1
+                        (nth 4 touch-screen-current-tool)))))))
+
+(defun touch-screen-handle-touch (event prefix &optional interactive)
+  "Handle a single touch EVENT, and perform associated actions.
+EVENT can either be a `touchscreen-begin', `touchscreen-update' or
+`touchscreen-end' event.
+PREFIX is either nil, or a symbol specifying a virtual function
+key to apply to EVENT.
+
+If INTERACTIVE, execute the command associated with any event
+generated instead of throwing `input-event'.  Otherwise, throw
+`input-event' with a single input event if that event should take
+the place of EVENT within the key sequence being translated, or
+`nil' if all tools have been released."
+  (interactive "e\ni\np")
+  (if interactive
+      ;; Called interactively (probably from wid-edit.el.)
+      ;; Add any event generated to `unread-command-events'.
+      (let ((event1
+             (let ((current-key-remap-sequence (vector event)))
+               (touch-screen-translate-touch nil))))
+        (when (vectorp event1)
+          (setq unread-command-events
+                (nconc unread-command-events
+                       (nreverse (append event1 nil))))))
+    (cond
+     ((eq (car event) 'touchscreen-begin)
+      ;; A tool was just pressed against the screen.  Figure out the
+      ;; window where it is and make it the tool being tracked on the
+      ;; window.
+      (let* ((touchpoint (caadr event))
+             (position (cdadr event))
+             (window (posn-window position))
+             (point (posn-point position))
+             binding tool-list)
+        ;; Cancel the touch screen timer, if it is still there by any
+        ;; chance.
+        (when touch-screen-current-timer
+          (cancel-timer touch-screen-current-timer)
+          (setq touch-screen-current-timer nil))
+        ;; Replace any previously ongoing gesture.  If POSITION has no
+        ;; window or position, make it nil instead.
+        (setq tool-list (and (windowp window)
+                             (list touchpoint window
+                                   (posn-x-y position)
+                                   nil position
+                                   nil nil nil nil))
+              touch-screen-current-tool tool-list)
+
+        ;; Select the window underneath the event as the checks below
+        ;; will look up keymaps and markers inside its buffer.
+        (save-selected-window
+          ;; Check if `touch-screen-extend-selection' is enabled, the
+          ;; tap lies on the point or the mark, and the region is
+          ;; active.  If that's the case, set the fourth element of
+          ;; `touch-screen-current-tool' to `restart-drag', then
+          ;; generate a `touchscreen-restart-drag' event.
+          (when tool-list
+            ;; tool-list is always non-nil where the selected window
+            ;; matters.
+            (select-window window)
+            (when (and touch-screen-extend-selection
+                       (or (eq point (point))
+                           (eq point (mark)))
+                       (region-active-p)
+                       ;; Only restart drag-to-select if the tap falls
+                       ;; on the same row as the selection.  This
+                       ;; prevents dragging from starting if the tap
+                       ;; is below the last window line with text and
+                       ;; `point' is at ZV, as the user most likely
+                       ;; meant to scroll the window instead.
+                       (when-let* ((posn-point (posn-at-point point))
+                                   (posn-row (cdr (posn-col-row posn-point))))
+                         (eq (cdr (posn-col-row position)) posn-row)))
+              ;; Indicate that a drag is about to restart.
+              (setcar (nthcdr 3 tool-list) 'restart-drag)
+              ;; Generate the `restart-drag' event.
+              (throw 'input-event (list 'touchscreen-restart-drag
+                                        position))))
+          ;; Determine if there is a command bound to `down-mouse-1'
+          ;; at the position of the tap and that command is not a
+          ;; command whose functionality is replaced by the long-press
+          ;; mechanism.  If so, set the fourth element of
+          ;; `touch-screen-current-tool' to `mouse-drag' and generate
+          ;; an emulated `mouse-1' event.
+          ;;
+          ;; If the command in question is a keymap, set that element
+          ;; to `mouse-1-menu' instead of `mouse-drag', and don't
+          ;; generate a `down-mouse-1' event immediately.  Instead,
+          ;; wait for the touch point to be released.
+          (if (and tool-list
+                   (and (setq binding
+                              (key-binding (if prefix
+                                               (vector prefix
+                                                       'down-mouse-1)
+                                             [down-mouse-1])
+                                           t nil position))
+                        (not (and (symbolp binding)
+                                  (get binding 'ignored-mouse-command)))))
+              (if (or (keymapp binding)
+                      (and (symbolp binding)
+                           (get binding 'mouse-1-menu-command)))
+                  ;; binding is a keymap, or a command that does
+                  ;; almost the same thing.  If a `mouse-1' event is
+                  ;; generated after the keyboard command loop
+                  ;; displays it as a menu, that event could cause
+                  ;; unwanted commands to be run.  Set what to
+                  ;; `mouse-1-menu' instead and wait for the up event
+                  ;; to display the menu.
+                  (setcar (nthcdr 3 tool-list) 'mouse-1-menu)
+                (progn (setcar (nthcdr 3 tool-list) 'mouse-drag)
+                       (throw 'input-event (list 'down-mouse-1 position))))
+            (and point
+                 ;; Start the long-press timer.
+                 (touch-screen-handle-timeout nil))))))
+     ((eq (car event) 'touchscreen-update)
+      (unless touch-screen-current-tool
+        ;; If a stray touchscreen-update event arrives (most likely
+        ;; from the menu bar), stop translating this sequence.
+        (throw 'input-event nil))
+      ;; The positions of tools currently pressed against the screen
+      ;; have changed.  If there is a tool being tracked as part of a
+      ;; gesture, look it up in the list of tools.
+      (let ((new-point (assq (car touch-screen-current-tool)
+                             (cadr event))))
+        (when new-point
+          (touch-screen-handle-point-update new-point))))
+     ((eq (car event) 'touchscreen-end)
+      ;; A tool has been removed from the screen.  If it is the tool
+      ;; currently being tracked, clear `touch-screen-current-tool'.
+      (when (eq (caadr event) (car touch-screen-current-tool))
+        ;; Cancel the touch screen long-press timer, if it is still
+        ;; there by any chance.
+        (when touch-screen-current-timer
+          (cancel-timer touch-screen-current-timer)
+          (setq touch-screen-current-timer nil))
+        (unwind-protect
+            ;; Don't perform any actions associated with releasing the
+            ;; tool if the touch sequence was intercepted by another
+            ;; program.
+            (unless (caddr event)
+              (touch-screen-handle-point-up (cadr event) prefix))
+          ;; Make sure the tool list is cleared even if
+          ;; `touch-screen-handle-point-up' throws.
+          (setq touch-screen-current-tool nil)))
+      ;; Throw to the key translation function.
+      (throw 'input-event nil)))))
+
+;; Mark `mouse-drag-region' as ignored for the purposes of mouse click
+;; emulation.
+
+(put 'mouse-drag-region 'ignored-mouse-command t)
+
+(defun touch-screen-translate-touch (prompt)
+  "Translate touch screen events into a sequence of mouse events.
+PROMPT is the prompt string given to `read-key-sequence', or nil
+if this function is being called from the keyboard command loop.
+Value is a new key sequence.
+
+Read the touch screen event within `current-key-remap-sequence'
+and give it to `touch-screen-handle-touch'.  Return any key
+sequence signaled.
+
+If `touch-screen-handle-touch' does not signal for an event to be
+returned after the last element of the key sequence is read,
+continue reading touch screen events until
+`touch-screen-handle-touch' signals.  Return a sequence
+consisting of the first event encountered that is not a touch
+screen event.
+
+In addition to non-touchscreen events read, key sequences
+returned may contain any one of the following events:
+
+  (touchscreen-scroll WINDOW DX DY)
+
+where WINDOW specifies a window to scroll, and DX and DY are
+integers describing how many pixels to be scrolled horizontally
+and vertically,
+
+  (touchscreen-hold POSN)
+  (touchscreen-drag POSN)
+
+where POSN is the position of the long-press or touchpoint
+motion,
+
+  (touchscreen-restart-drag POSN)
+
+where POSN is the position of the tap,
+
+  (down-mouse-1 POSN)
+  (drag-mouse-1 POSN)
+
+where POSN is the position of the mouse button press or click,
+
+  (mouse-1 POSN)
+  (mouse-2 POSN)
+
+where POSN is the position of the mouse click, either `mouse-2'
+if POSN is on a link or a button, or `mouse-1' otherwise."
+  (unwind-protect
+      ;; Save the virtual function key if this is a mode line event.
+      (let* ((prefix-specified
+              ;; Virtual prefix keys can be nil for events that fall
+              ;; outside a frame or within its internal border.
+              (> (length current-key-remap-sequence) 1))
+             (prefix (and prefix-specified
+                          (aref current-key-remap-sequence 0)))
+             (touch-screen-translate-prompt prompt)
+             (event (catch 'input-event
+                      ;; First, process the one event already within
+                      ;; `current-key-remap-sequence'.
+                      (touch-screen-handle-touch
+                       (aref current-key-remap-sequence
+                             (if prefix-specified 1 0))
+                       prefix)
+                      ;; Next, continue reading input events.
+                      (while t
+                        (let ((event1 (read-event)))
+                          ;; If event1 is a virtual function key, make
+                          ;; it the new prefix.
+                          (if (memq event1 '(mode-line tab-line nil
+                                             vertical-line
+                                             header-line tool-bar tab-bar
+                                             left-fringe right-fringe
+                                             left-margin right-margin
+                                             right-divider bottom-divider))
+                              (setq prefix event1)
+                            ;; If event1 is not a touch screen event,
+                            ;; return it.
+                            (if (not (memq (car-safe event1)
+                                           '(touchscreen-begin
+                                             touchscreen-end
+                                             touchscreen-update)))
+                                (throw 'input-event event1)
+                              ;; Process this event as well.
+                              (touch-screen-handle-touch event1 prefix))))))))
+        ;; Return a key sequence consisting of event
+        ;; or an empty vector if it is nil, meaning that
+        ;; no key events have been translated.
+        (if event (or (and prefix (consp event)
+                           ;; Only generate virtual function keys for
+                           ;; mouse events.
+                           (memq (car event)
+                                 '(down-mouse-1 mouse-1
+                                   mouse-2 mouse-movement))
+                           ;; If this is a mode line event, then
+                           ;; generate the appropriate function key.
+                           (vector prefix event))
+                      (vector event))
+          ""))
+    ;; Cancel the touch screen long-press timer, if it is still there
+    ;; by any chance.  If the timer is to operate correctly, it must
+    ;; fire within the catch block above.
+    (when touch-screen-current-timer
+      (cancel-timer touch-screen-current-timer)
+      (setq touch-screen-current-timer nil))))
+
+(define-key function-key-map [touchscreen-begin]
+            #'touch-screen-translate-touch)
+(define-key function-key-map [touchscreen-update]
+            #'touch-screen-translate-touch)
+(define-key function-key-map [touchscreen-end]
+            #'touch-screen-translate-touch)
+
+(define-key function-key-map [mode-line touchscreen-begin]
+            #'touch-screen-translate-touch)
+(define-key function-key-map [mode-line touchscreen-end]
+            #'touch-screen-translate-touch)
+
+;; These are used to translate events sent from the internal border or
+;; from outside the frame.
+
+(define-key function-key-map [nil touchscreen-begin]
+            #'touch-screen-translate-touch)
+(define-key function-key-map [nil touchscreen-end]
+            #'touch-screen-translate-touch)
+
+(define-key function-key-map [header-line touchscreen-begin]
+            #'touch-screen-translate-touch)
+(define-key function-key-map [header-line touchscreen-end]
+            #'touch-screen-translate-touch)
+
+(define-key function-key-map [bottom-divider touchscreen-begin]
+            #'touch-screen-translate-touch)
+(define-key function-key-map [bottom-divider touchscreen-end]
+            #'touch-screen-translate-touch)
+
+(define-key function-key-map [right-divider touchscreen-begin]
+            #'touch-screen-translate-touch)
+(define-key function-key-map [right-divider touchscreen-end]
+            #'touch-screen-translate-touch)
+
+(define-key function-key-map [right-divider touchscreen-begin]
+            #'touch-screen-translate-touch)
+(define-key function-key-map [right-divider touchscreen-end]
+            #'touch-screen-translate-touch)
+
+(define-key function-key-map [left-fringe touchscreen-begin]
+            #'touch-screen-translate-touch)
+(define-key function-key-map [left-fringe touchscreen-end]
+            #'touch-screen-translate-touch)
+
+(define-key function-key-map [right-fringe touchscreen-begin]
+            #'touch-screen-translate-touch)
+(define-key function-key-map [right-fringe touchscreen-end]
+            #'touch-screen-translate-touch)
+
+(define-key function-key-map [left-margin touchscreen-begin]
+            #'touch-screen-translate-touch)
+(define-key function-key-map [left-margin touchscreen-end]
+            #'touch-screen-translate-touch)
+
+(define-key function-key-map [right-margin touchscreen-begin]
+            #'touch-screen-translate-touch)
+(define-key function-key-map [right-margin touchscreen-end]
+            #'touch-screen-translate-touch)
+
+(define-key function-key-map [tool-bar touchscreen-begin]
+            #'touch-screen-translate-touch)
+(define-key function-key-map [tool-bar touchscreen-end]
+            #'touch-screen-translate-touch)
+
+(define-key function-key-map [tab-bar touchscreen-begin]
+            #'touch-screen-translate-touch)
+(define-key function-key-map [tab-bar touchscreen-end]
+            #'touch-screen-translate-touch)
+
+(define-key function-key-map [tab-line touchscreen-begin]
+            #'touch-screen-translate-touch)
+(define-key function-key-map [tab-line touchscreen-end]
+            #'touch-screen-translate-touch)
+
+(define-key function-key-map [vertical-line touchscreen-begin]
+            #'touch-screen-translate-touch)
+(define-key function-key-map [vertical-line touchscreen-end]
+            #'touch-screen-translate-touch)
+
+(define-key function-key-map [nil touchscreen-begin]
+            #'touch-screen-translate-touch)
+(define-key function-key-map [nil touchscreen-end]
+            #'touch-screen-translate-touch)
+
+
+;; Exports.  These functions are intended for use externally.
+
+(defun touch-screen-track-tap (event &optional update data)
+  "Track a single tap starting from EVENT.
+EVENT should be a `touchscreen-begin' event.
+
+Read touch screen events until a `touchscreen-end' event is
+received with the same ID as in EVENT.  If UPDATE is non-nil and
+a `touchscreen-update' event is received in the mean time and
+contains a touch point with the same ID as in EVENT, call UPDATE
+with that event and DATA.
+
+Return nil immediately if any other kind of event is received;
+otherwise, return t once the `touchscreen-end' event arrives."
+  (let ((disable-inhibit-text-conversion t))
+    (catch 'finish
+      (while t
+        (let ((new-event (read-event nil)))
+          (cond
+           ((eq (car-safe new-event) 'touchscreen-update)
+            (when (and update (assq (caadr event) (cadr new-event)))
+              (funcall update new-event data)))
+           ((eq (car-safe new-event) 'touchscreen-end)
+            (throw 'finish
+                   ;; Now determine whether or not the `touchscreen-end'
+                   ;; event has the same ID as EVENT.  If it doesn't,
+                   ;; then this is another touch, so return nil.
+                   (eq (caadr event) (caadr new-event))))
+           (t (throw 'finish nil))))))))
+
+(defun touch-screen-track-drag (event update &optional data)
+  "Track a single drag starting from EVENT.
+EVENT should be a `touchscreen-begin' event.
+
+Read touch screen events until a `touchscreen-end' event is
+received with the same ID as in EVENT.  For each
+`touchscreen-update' event received in the mean time containing a
+touch point with the same ID as in EVENT, call UPDATE with the
+touch point in event and DATA, once the touch point has moved
+significantly by at least 5 pixels from where it was in EVENT.
+
+Return nil immediately if any other kind of event is received;
+otherwise, return either t or `no-drag' once the
+`touchscreen-end' event arrives; return `no-drag' returned if the
+touch point in EVENT did not move significantly, and t otherwise."
+  (let ((return-value 'no-drag)
+        (start-xy (touch-screen-relative-xy (cdadr event)
+                                            'frame))
+        (disable-inhibit-text-conversion t))
+    (catch 'finish
+      (while t
+        (let ((new-event (read-event nil)))
+          (cond
+           ((eq (car-safe new-event) 'touchscreen-update)
+            (when-let* ((tool (assq (caadr event) (nth 1 new-event)))
+                        (xy (touch-screen-relative-xy (cdr tool) 'frame)))
+              (when (or (> (- (car xy) (car start-xy)) 5)
+                        (< (- (car xy) (car start-xy)) -5)
+                        (> (- (cdr xy) (cdr start-xy)) 5)
+                        (< (- (cdr xy) (cdr start-xy)) -5))
+                (setq return-value t))
+              (when (and update tool (eq return-value t))
+                (funcall update new-event data))))
+           ((eq (car-safe new-event) 'touchscreen-end)
+            (throw 'finish
+                   ;; Now determine whether or not the `touchscreen-end'
+                   ;; event has the same ID as EVENT.  If it doesn't,
+                   ;; then this is another touch, so return nil.
+                   (and (eq (caadr event) (caadr new-event))
+                        return-value)))
+           (t (throw 'finish nil))))))))
+
+
+
+;;; Event handling exports.  These functions are intended for use by
+;;; Lisp commands bound to touch screen gesture events.
+
+(defun touch-screen-inhibit-drag ()
+  "Inhibit subsequent `touchscreen-drag' events from being sent.
+Prevent `touchscreen-drag' and translated mouse events from being
+sent until the touch sequence currently being translated ends.
+Must be called from a command bound to a `touchscreen-hold' or
+`touchscreen-drag' event."
+  (let* ((tool touch-screen-current-tool)
+         (current-what (nth 4 tool)))
+    ;; Signal an error if no hold or drag is in progress.
+    (when (and (not (eq current-what 'hold)
+                    (eq current-what 'drag)))
+      (error "Calling `touch-screen-inhibit-drag' outside hold or drag"))
+    ;; Now set the fourth element of tool to `command-inhibit'.
+    (setcar (nthcdr 3 tool) 'command-inhibit)))
+
+
+
+(provide 'touch-screen)
+
+;;; touch-screen ends here
diff --git a/lisp/use-package/bind-key.el b/lisp/use-package/bind-key.el
index b216c668d83..c45b9e546f7 100644
--- a/lisp/use-package/bind-key.el
+++ b/lisp/use-package/bind-key.el
@@ -196,6 +196,7 @@ can safely be called at any time."
                                (key-description ,namevar))
                              (if (symbolp ,keymap) ,keymap (quote ,keymap))))
             (,bindingvar (lookup-key ,kmapvar ,keyvar)))
+       (require 'bind-key)      ; ensure `personal-keybindings' is in scope
        (let ((entry (assoc ,kdescvar personal-keybindings))
              (details (list ,command
                             (unless (numberp ,bindingvar)
diff --git a/lisp/use-package/use-package-core.el 
b/lisp/use-package/use-package-core.el
index e0e16134ed3..21a831ce26d 100644
--- a/lisp/use-package/use-package-core.el
+++ b/lisp/use-package/use-package-core.el
@@ -327,12 +327,15 @@ Must be set before loading `use-package'."
       (set-default sym value))
   :group 'use-package)
 
+;; Redundant in Emacs 26 or later, which already highlights macro names.
 (defconst use-package-font-lock-keywords
   '(("(\\(use-package\\)\\_>[ \t']*\\(\\(?:\\sw\\|\\s_\\)+\\)?"
      (1 font-lock-keyword-face)
      (2 font-lock-constant-face nil t))))
-
-(font-lock-add-keywords 'emacs-lisp-mode use-package-font-lock-keywords)
+(make-obsolete-variable 'use-package-font-lock-keywords
+                        'lisp-el-font-lock-keywords "30.1")
+(when (< emacs-major-version 26)
+  (font-lock-add-keywords 'emacs-lisp-mode use-package-font-lock-keywords))
 
 (defcustom use-package-compute-statistics nil
   "If non-nil, compute statistics concerned `use-package' declarations.
@@ -1055,6 +1058,7 @@ meaning:
 (define-derived-mode use-package-statistics-mode tabulated-list-mode
   "use-package statistics"
   "Show current statistics gathered about `use-package' declarations."
+  :interactive nil
   (setq tabulated-list-format
         ;; The sum of column width is 80 characters:
         [("Package" 25 t)
diff --git a/lisp/version.el b/lisp/version.el
index 9cadc59237f..ca61f8cfeee 100644
--- a/lisp/version.el
+++ b/lisp/version.el
@@ -26,6 +26,31 @@
 
 ;;; Code:
 
+
+
+(defun android-read-build-system ()
+  "Obtain the host name of the system on which Emacs was built.
+Use the data stored in the special file `/assets/build_info'.
+Value is the string ``Unknown'' upon failure, else the hostname
+of the build system."
+  (with-temp-buffer
+    (insert-file-contents "/assets/build_info")
+    (let ((string (buffer-substring 1 (line-end-position))))
+      (and (not (equal string "Unknown")) string))))
+
+(defun android-read-build-time ()
+  "Obtain the time at which Emacs was built.
+Use the data stored in the special file `/assets/build_info'.
+Value is nil upon failure, else the time in the same format as
+returned by `current-time'."
+  (with-temp-buffer
+    (insert-file-contents "/assets/build_info")
+    (end-of-line)
+    (let ((number (read (current-buffer))))
+      (time-convert number 'list))))
+
+
+
 (defconst emacs-major-version
   (progn (string-match "^[0-9]+" emacs-version)
          (string-to-number (match-string 0 emacs-version)))
@@ -36,10 +61,15 @@
          (string-to-number (match-string 1 emacs-version)))
   "Minor version number of this version of Emacs.")
 
-(defconst emacs-build-system (system-name)
+(defconst emacs-build-system (or (and (eq system-type 'android)
+                                      (android-read-build-system))
+                                 (system-name))
   "Name of the system on which Emacs was built, or nil if not available.")
 
-(defconst emacs-build-time (if emacs-build-system (current-time))
+(defconst emacs-build-time (if emacs-build-system
+                               (or (and (eq system-type 'android)
+                                        (android-read-build-time))
+                                   (current-time)))
   "Time at which Emacs was dumped out, or nil if not available.")
 
 (defconst emacs-build-number 1          ; loadup.el may increment this
@@ -130,9 +160,22 @@ or if we could not determine the revision.")
                  (looking-at "[[:xdigit:]]\\{40\\}"))
           (match-string 0)))))
 
+(defun emacs-repository-version-android ()
+  "Return the Emacs repository revision Emacs was built from.
+Value is nil if Emacs was not built from a repository checkout.
+Use information from the `/assets/version' special file."
+  (with-temp-buffer
+    (insert-file-contents "/assets/version")
+    (let ((string (buffer-substring 1 (line-end-position))))
+      (and (not (equal string "Unknown")) string))))
+
 (defun emacs-repository-get-version (&optional dir _external)
   "Try to return as a string the repository revision of the Emacs sources.
 The format of the returned string is dependent on the VCS in use.
+
+If Emacs is built for Android, use the version information
+embedded in the Emacs installation package.
+
 Value is nil if the sources do not seem to be under version
 control, or if we could not determine the revision.  Note that
 this reports on the current state of the sources, which may not
@@ -140,13 +183,27 @@ correspond to the running Emacs.
 
 Optional argument DIR is a directory to use instead of `source-directory'.
 Optional argument EXTERNAL is ignored."
-  (emacs-repository-version-git (or dir source-directory)))
+  (cond ((eq system-type 'android)
+         (emacs-repository-version-android))
+        (t (emacs-repository-version-git
+            (or dir source-directory)))))
 
 (defvar emacs-repository-branch nil
   "String giving the repository branch from which this Emacs was built.
 Value is nil if Emacs was not built from a repository checkout,
 or if we could not determine the branch.")
 
+(defun emacs-repository-branch-android ()
+  "Return the Emacs repository branch Emacs was built from.
+Value is nil if Emacs was not built from a repository checkout.
+Use information from the `/assets/version' special file."
+  (with-temp-buffer
+    (insert-file-contents "/assets/version")
+    (end-of-line)
+    (forward-char)
+    (let ((string (buffer-substring (point) (line-end-position))))
+      (and (not (equal string "Unknown")) string))))
+
 (defun emacs-repository-branch-git (dir)
   "Ask git itself for the branch information for directory DIR."
   (message "Waiting for git...")
@@ -162,12 +219,19 @@ or if we could not determine the branch.")
 (defun emacs-repository-get-branch (&optional dir)
   "Try to return as a string the repository branch of the Emacs sources.
 The format of the returned string is dependent on the VCS in use.
+
+If Emacs is built for Android, use the version information
+embedded in the Emacs installation package.
+
 Value is nil if the sources do not seem to be under version
 control, or if we could not determine the branch.  Note that
 this reports on the current state of the sources, which may not
 correspond to the running Emacs.
 
 Optional argument DIR is a directory to use instead of `source-directory'."
-  (emacs-repository-branch-git (or dir source-directory)))
+  (cond ((eq system-type 'android)
+         (emacs-repository-branch-android))
+        (t (emacs-repository-branch-git
+            (or dir source-directory)))))
 
 ;;; version.el ends here
diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el
index 88f8a362521..47531113ba8 100644
--- a/lisp/wid-edit.el
+++ b/lisp/wid-edit.el
@@ -65,8 +65,11 @@
 ;;; Compatibility.
 
 (defun widget-event-point (event)
-  "Character position of the end of event if that exists, or nil."
-  (posn-point (event-end event)))
+  "Character position of the end of event if that exists, or nil.
+EVENT can either be a mouse event or a touch screen event."
+  (if (eq (car-safe event) 'touchscreen-begin)
+      (posn-point (cdadr event))
+    (posn-point (event-end event))))
 
 (defun widget-button-release-event-p (event)
   "Non-nil if EVENT is a mouse-button-release event object."
@@ -1025,6 +1028,7 @@ button end points."
     (define-key map [backtab] 'widget-backward)
     (define-key map [down-mouse-2] 'widget-button-click)
     (define-key map [down-mouse-1] 'widget-button-click)
+    (define-key map [touchscreen-begin] 'widget-button-click)
     ;; The following definition needs to avoid using escape sequences that
     ;; might get converted to ^M when building loaddefs.el
     (define-key map [(control ?m)] 'widget-button-press)
@@ -1080,8 +1084,18 @@ Note that such modes will need to require wid-edit.")
   "If non-nil, `widget-button-click' moves point to a button after invoking it.
 If nil, point returns to its original position after invoking a button.")
 
+(defun widget-event-start (event)
+  "Return the start of EVENT.
+If EVENT is not a touchscreen event, simply return its
+`event-start'.  Otherwise, it is a touchscreen event, so return
+the posn of its touchpoint."
+  (if (eq (car event) 'touchscreen-begin)
+      (cdadr event)
+    (event-start event)))
+
 (defun widget-button--check-and-call-button (event button)
   "Call BUTTON if BUTTON is a widget and EVENT is correct for it.
+EVENT can either be a mouse event or a touchscreen-begin event.
 If nothing was called, return non-nil."
   (let* ((oevent event)
          (mouse-1 (memq (event-basic-type event) '(mouse-1 down-mouse-1)))
@@ -1092,49 +1106,58 @@ If nothing was called, return non-nil."
       ;; in a save-excursion so that the click on the button
       ;; doesn't change point.
       (save-selected-window
-        (select-window (posn-window (event-start event)))
+        (select-window (posn-window (widget-event-start event)))
         (save-excursion
-         (goto-char (posn-point (event-start event)))
+         (goto-char (posn-point (widget-event-start event)))
          (let* ((overlay (widget-get button :button-overlay))
                 (pressed-face (or (widget-get button :pressed-face)
                                   widget-button-pressed-face))
                 (face (overlay-get overlay 'face))
                 (mouse-face (overlay-get overlay 'mouse-face)))
            (unwind-protect
-               ;; Read events, including mouse-movement
-               ;; events, waiting for a release event.  If we
-               ;; began with a mouse-1 event and receive a
-               ;; movement event, that means the user wants
-               ;; to perform drag-selection, so cancel the
-               ;; button press and do the default mouse-1
-               ;; action.  For mouse-2, just highlight/
-               ;; unhighlight the button the mouse was
-               ;; initially on when we move over it.
+               ;; Read events, including mouse-movement events,
+               ;; waiting for a release event.  If we began with a
+               ;; mouse-1 event and receive a movement event, that
+               ;; means the user wants to perform drag-selection, so
+               ;; cancel the button press and do the default mouse-1
+               ;; action.  For mouse-2, just highlight/ unhighlight
+               ;; the button the mouse was initially on when we move
+               ;; over it.
+                ;;
+                ;; If this function was called in response to a
+                ;; touchscreen event, then wait for a corresponding
+                ;; touchscreen-end event instead.
                (save-excursion
                  (when face            ; avoid changing around image
                    (overlay-put overlay 'face pressed-face)
                    (overlay-put overlay 'mouse-face pressed-face))
-                 (unless (widget-apply button :mouse-down-action event)
-                   (let ((track-mouse t))
-                     (while (not (widget-button-release-event-p event))
-                        (setq event (read--potential-mouse-event))
-                       (when (and mouse-1 (mouse-movement-p event))
-                         (push event unread-command-events)
-                         (setq event oevent)
-                         (throw 'button-press-cancelled t))
-                       (unless (or (integerp event)
-                                   (memq (car event)
-                                          '(switch-frame select-window))
-                                   (eq (car event) 'scroll-bar-movement))
-                         (setq pos (widget-event-point event))
-                         (if (and pos
-                                  (eq (get-char-property pos 'button)
-                                      button))
-                             (when face
-                               (overlay-put overlay 'face pressed-face)
-                               (overlay-put overlay 'mouse-face pressed-face))
-                           (overlay-put overlay 'face face)
-                           (overlay-put overlay 'mouse-face mouse-face))))))
+                  (if (eq (car event) 'touchscreen-begin)
+                      ;; This a touchscreen event and must be handled
+                      ;; specially through `touch-screen-track-tap'.
+                      (progn
+                        (unless (touch-screen-track-tap event)
+                          (throw 'button-press-cancelled t)))
+                    (unless (widget-apply button :mouse-down-action event)
+                      (let ((track-mouse t))
+                        (while (not (widget-button-release-event-p event))
+                          (setq event (read--potential-mouse-event))
+                          (when (and mouse-1 (mouse-movement-p event))
+                            (push event unread-command-events)
+                            (setq event oevent)
+                            (throw 'button-press-cancelled t))
+                          (unless (or (integerp event)
+                                      (memq (car event)
+                                            '(switch-frame select-window))
+                                      (eq (car event) 'scroll-bar-movement))
+                            (setq pos (widget-event-point event))
+                            (if (and pos
+                                     (eq (get-char-property pos 'button)
+                                         button))
+                                (when face
+                                  (overlay-put overlay 'face pressed-face)
+                                  (overlay-put overlay 'mouse-face 
pressed-face))
+                              (overlay-put overlay 'face face)
+                              (overlay-put overlay 'mouse-face 
mouse-face)))))))
 
                  ;; When mouse is released over the button, run
                  ;; its action function.
@@ -1156,37 +1179,44 @@ If nothing was called, return non-nil."
   (if (widget-event-point event)
       (let* ((mouse-1 (memq (event-basic-type event) '(mouse-1 down-mouse-1)))
             (pos (widget-event-point event))
-            (start (event-start event))
+            (start (widget-event-start event))
              (button (get-char-property
                      pos 'button (and (windowp (posn-window start))
                                       (window-buffer (posn-window start))))))
 
        (when (or (null button)
                   (widget-button--check-and-call-button event button))
-         (let ((up t)
+         (let ((up (not (eq (car event) 'touchscreen-begin)))
                 command)
            ;; Mouse click not on a widget button.  Find the global
            ;; command to run, and check whether it is bound to an
            ;; up event.
-           (if mouse-1
-               (cond ((setq command    ;down event
-                            (lookup-key widget-global-map [down-mouse-1]))
-                      (setq up nil))
-                     ((setq command    ;up event
-                            (lookup-key widget-global-map [mouse-1]))))
-             (cond ((setq command      ;down event
-                          (lookup-key widget-global-map [down-mouse-2]))
-                    (setq up nil))
-                   ((setq command      ;up event
-                          (lookup-key widget-global-map [mouse-2])))))
+            (cond
+             ((eq (car event) 'touchscreen-begin)
+              (setq command 'touch-screen-handle-touch))
+             (mouse-1 (cond ((setq command     ;down event
+                                   (lookup-key widget-global-map 
[down-mouse-1]))
+                             (setq up nil))
+                            ((setq command     ;up event
+                                   (lookup-key widget-global-map [mouse-1])))))
+             (t (cond ((setq command   ;down event
+                             (lookup-key widget-global-map [down-mouse-2]))
+                       (setq up nil))
+                      ((setq command   ;up event
+                             (lookup-key widget-global-map [mouse-2]))))))
            (when up
              ;; Don't execute up events twice.
-             (while (not (widget-button-release-event-p event))
+             (while (not (and (widget-button-release-event-p event)))
                (setq event (read--potential-mouse-event))))
            (when command
              (call-interactively command)))))
     (message "You clicked somewhere weird.")))
 
+;; Make sure `touch-screen-handle-touch' abstains from emulating
+;; down-mouse-1 events for `widget-button-click'.
+
+(put 'widget-button-click 'ignored-mouse-command t)
+
 (defun widget-button-press (pos &optional event)
   "Invoke button at POS."
   (interactive "@d")
diff --git a/m4/getdelim.m4 b/m4/getdelim.m4
new file mode 100644
index 00000000000..60555b9718b
--- /dev/null
+++ b/m4/getdelim.m4
@@ -0,0 +1,114 @@
+# getdelim.m4 serial 19
+
+dnl Copyright (C) 2005-2007, 2009-2023 Free Software Foundation, Inc.
+dnl
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_PREREQ([2.59])
+
+AC_DEFUN([gl_FUNC_GETDELIM],
+[
+  AC_REQUIRE([gl_STDIO_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST])
+
+  dnl Persuade glibc <stdio.h> to declare getdelim().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  AC_CHECK_DECLS_ONCE([getdelim])
+
+  gl_CHECK_FUNCS_ANDROID([getdelim], [[#include <stdio.h>]])
+  if test $ac_cv_func_getdelim = yes; then
+    HAVE_GETDELIM=1
+    dnl Found it in some library.  Verify that it works.
+    AC_CACHE_CHECK([for working getdelim function],
+      [gl_cv_func_working_getdelim],
+      [case "$host_os" in
+         darwin*)
+           dnl On macOS 10.13, valgrind detected an out-of-bounds read during
+           dnl the GNU sed test suite:
+           dnl   Invalid read of size 16
+           dnl      at 0x100EE6A05: _platform_memchr$VARIANT$Base (in 
/usr/lib/system/libsystem_platform.dylib)
+           dnl      by 0x100B7B0BD: getdelim (in 
/usr/lib/system/libsystem_c.dylib)
+           dnl      by 0x10000B0BE: ck_getdelim (utils.c:254)
+           gl_cv_func_working_getdelim=no ;;
+         *)
+           echo fooNbarN | tr -d '\012' | tr N '\012' > conftest.data
+           AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#    include <stdio.h>
+#    include <stdlib.h>
+#    include <string.h>
+    int main ()
+    {
+      FILE *in = fopen ("./conftest.data", "r");
+      if (!in)
+        return 1;
+      {
+        /* Test result for a NULL buffer and a zero size.
+           Based on a test program from Karl Heuer.  */
+        char *line = NULL;
+        size_t siz = 0;
+        int len = getdelim (&line, &siz, '\n', in);
+        if (!(len == 4 && line && strcmp (line, "foo\n") == 0))
+          { free (line); fclose (in); return 2; }
+        free (line);
+      }
+      {
+        /* Test result for a NULL buffer and a non-zero size.
+           This crashes on FreeBSD 8.0.  */
+        char *line = NULL;
+        size_t siz = (size_t)(~0) / 4;
+        if (getdelim (&line, &siz, '\n', in) == -1)
+          { fclose (in); return 3; }
+        free (line);
+      }
+      fclose (in);
+      return 0;
+    }
+    ]])],
+             [gl_cv_func_working_getdelim=yes],
+             [gl_cv_func_working_getdelim=no],
+             [dnl We're cross compiling.
+              dnl Guess it works on glibc2 systems and musl systems.
+              AC_EGREP_CPP([Lucky GNU user],
+                [
+#include <features.h>
+#ifdef __GNU_LIBRARY__
+ #if (__GLIBC__ >= 2) && !defined __UCLIBC__
+  Lucky GNU user
+ #endif
+#endif
+                ],
+                [gl_cv_func_working_getdelim="guessing yes"],
+                [case "$host_os" in
+                   *-musl* | midipix*) gl_cv_func_working_getdelim="guessing 
yes" ;;
+                   *)                  
gl_cv_func_working_getdelim="$gl_cross_guess_normal" ;;
+                 esac
+                ])
+             ])
+           ;;
+       esac
+      ])
+    case "$gl_cv_func_working_getdelim" in
+      *yes) ;;
+      *) REPLACE_GETDELIM=1 ;;
+    esac
+  else
+    HAVE_GETDELIM=0
+    case "$gl_cv_onwards_func_getdelim" in
+      future*) REPLACE_GETDELIM=1 ;;
+    esac
+  fi
+
+  if test $ac_cv_have_decl_getdelim = no; then
+    HAVE_DECL_GETDELIM=0
+  fi
+])
+
+# Prerequisites of lib/getdelim.c.
+AC_DEFUN([gl_PREREQ_GETDELIM],
+[
+  AC_CHECK_FUNCS([flockfile funlockfile])
+  AC_CHECK_DECLS([getc_unlocked])
+])
diff --git a/m4/getline.m4 b/m4/getline.m4
new file mode 100644
index 00000000000..83e7e9315f6
--- /dev/null
+++ b/m4/getline.m4
@@ -0,0 +1,111 @@
+# getline.m4 serial 33
+
+dnl Copyright (C) 1998-2003, 2005-2007, 2009-2023 Free Software Foundation,
+dnl Inc.
+dnl
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_PREREQ([2.59])
+
+dnl See if there's a working, system-supplied version of the getline function.
+dnl We can't just do AC_REPLACE_FUNCS([getline]) because some systems
+dnl have a function by that name in -linet that doesn't have anything
+dnl to do with the function we need.
+AC_DEFUN([gl_FUNC_GETLINE],
+[
+  AC_REQUIRE([gl_STDIO_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+  dnl Persuade glibc <stdio.h> to declare getline().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  AC_CHECK_DECLS_ONCE([getline])
+
+  gl_CHECK_FUNCS_ANDROID([getline], [[#include <stdio.h>]])
+  if test $ac_cv_func_getline = yes; then
+    dnl Found it in some library.  Verify that it works.
+    AC_CACHE_CHECK([for working getline function],
+      [am_cv_func_working_getline],
+      [echo fooNbarN | tr -d '\012' | tr N '\012' > conftest.data
+       AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#    include <stdio.h>
+#    include <stdlib.h>
+#    include <string.h>
+    int main ()
+    {
+      FILE *in = fopen ("./conftest.data", "r");
+      if (!in)
+        return 1;
+      {
+        /* Test result for a NULL buffer and a zero size.
+           Based on a test program from Karl Heuer.  */
+        char *line = NULL;
+        size_t siz = 0;
+        int len = getline (&line, &siz, in);
+        if (!(len == 4 && line && strcmp (line, "foo\n") == 0))
+          { free (line); fclose (in); return 2; }
+        free (line);
+      }
+      {
+        /* Test result for a NULL buffer and a non-zero size.
+           This crashes on FreeBSD 8.0.  */
+        char *line = NULL;
+        size_t siz = (size_t)(~0) / 4;
+        if (getline (&line, &siz, in) == -1)
+          { fclose (in); return 3; }
+        free (line);
+      }
+      fclose (in);
+      return 0;
+    }
+    ]])],
+         [am_cv_func_working_getline=yes],
+         [am_cv_func_working_getline=no],
+         [dnl We're cross compiling.
+          dnl Guess it works on glibc2 systems and musl systems.
+          AC_EGREP_CPP([Lucky GNU user],
+            [
+#include <features.h>
+#ifdef __GNU_LIBRARY__
+ #if (__GLIBC__ >= 2) && !defined __UCLIBC__
+  Lucky GNU user
+ #endif
+#endif
+            ],
+            [am_cv_func_working_getline="guessing yes"],
+            [case "$host_os" in
+               *-musl* | midipix*) am_cv_func_working_getline="guessing yes" ;;
+               *)                  
am_cv_func_working_getline="$gl_cross_guess_normal" ;;
+             esac
+            ])
+         ])
+      ])
+  else
+    am_cv_func_working_getline=no
+    case "$gl_cv_onwards_func_getline" in
+      future*) REPLACE_GETLINE=1 ;;
+    esac
+  fi
+
+  if test $ac_cv_have_decl_getline = no; then
+    HAVE_DECL_GETLINE=0
+  fi
+
+  case "$am_cv_func_working_getline" in
+    *yes) ;;
+    *)
+      dnl Set REPLACE_GETLINE always: Even if we have not found the broken
+      dnl getline function among $LIBS, it may exist in libinet and the
+      dnl executable may be linked with -linet.
+      REPLACE_GETLINE=1
+      ;;
+  esac
+])
+
+# Prerequisites of lib/getline.c.
+AC_DEFUN([gl_PREREQ_GETLINE],
+[
+  :
+])
diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4
index 882b5869755..72adcf90d5e 100644
--- a/m4/gnulib-comp.m4
+++ b/m4/gnulib-comp.m4
@@ -104,8 +104,10 @@ AC_DEFUN([gl_EARLY],
   # Code from module fsync:
   # Code from module futimens:
   # Code from module gen-header:
+  # Code from module getdelim:
   # Code from module getdtablesize:
   # Code from module getgroups:
+  # Code from module getline:
   # Code from module getloadavg:
   # Code from module getopt-gnu:
   # Code from module getopt-posix:
@@ -179,6 +181,7 @@ AC_DEFUN([gl_EARLY],
   gl_STDIO_H_EARLY
   # Code from module stdlib:
   # Code from module stpcpy:
+  # Code from module stpncpy:
   # Code from module string:
   # Code from module strnlen:
   # Code from module strtoimax:
@@ -346,6 +349,12 @@ AC_DEFUN([gl_INIT],
   gl_CONDITIONAL([GL_COND_OBJ_FUTIMENS],
                  [test $HAVE_FUTIMENS = 0 || test $REPLACE_FUTIMENS = 1])
   gl_SYS_STAT_MODULE_INDICATOR([futimens])
+  gl_FUNC_GETLINE
+  gl_CONDITIONAL([GL_COND_OBJ_GETLINE], [test $REPLACE_GETLINE = 1])
+  AM_COND_IF([GL_COND_OBJ_GETLINE], [
+    gl_PREREQ_GETLINE
+  ])
+  gl_STDIO_MODULE_INDICATOR([getline])
   AC_REQUIRE([AC_CANONICAL_HOST])
   gl_GETLOADAVG
   gl_CONDITIONAL([GL_COND_OBJ_GETLOADAVG],
@@ -558,6 +567,13 @@ AC_DEFUN([gl_INIT],
     gl_PREREQ_STPCPY
   ])
   gl_STRING_MODULE_INDICATOR([stpcpy])
+  gl_FUNC_STPNCPY
+  gl_CONDITIONAL([GL_COND_OBJ_STPNCPY],
+                 [test $HAVE_STPNCPY = 0 || test $REPLACE_STPNCPY = 1])
+  AM_COND_IF([GL_COND_OBJ_STPNCPY], [
+    gl_PREREQ_STPNCPY
+  ])
+  gl_STRING_MODULE_INDICATOR([stpncpy])
   gl_STRING_H
   gl_STRING_H_REQUIRE_DEFAULTS
   AC_PROG_MKDIR_P
@@ -642,6 +658,7 @@ AC_DEFUN([gl_INIT],
   gl_gnulib_enabled_dirfd=false
   gl_gnulib_enabled_925677f0343de64b89a9f0c790b4104c=false
   gl_gnulib_enabled_euidaccess=false
+  gl_gnulib_enabled_getdelim=false
   gl_gnulib_enabled_getdtablesize=false
   gl_gnulib_enabled_getgroups=false
   gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false
@@ -713,6 +730,19 @@ AC_DEFUN([gl_INIT],
       func_gl_gnulib_m4code_6099e9737f757db36c47fa9d9f02e88c
     fi
   }
+  func_gl_gnulib_m4code_getdelim ()
+  {
+    if $gl_gnulib_enabled_getdelim; then :; else
+      gl_FUNC_GETDELIM
+      gl_CONDITIONAL([GL_COND_OBJ_GETDELIM],
+                     [test $HAVE_GETDELIM = 0 || test $REPLACE_GETDELIM = 1])
+      AM_COND_IF([GL_COND_OBJ_GETDELIM], [
+        gl_PREREQ_GETDELIM
+      ])
+      gl_STDIO_MODULE_INDICATOR([getdelim])
+      gl_gnulib_enabled_getdelim=true
+    fi
+  }
   func_gl_gnulib_m4code_getdtablesize ()
   {
     if $gl_gnulib_enabled_getdtablesize; then :; else
@@ -978,6 +1008,9 @@ AC_DEFUN([gl_INIT],
   if test $HAVE_FUTIMENS = 0 || test $REPLACE_FUTIMENS = 1; then
     func_gl_gnulib_m4code_utimens
   fi
+  if test $REPLACE_GETLINE = 1; then
+    func_gl_gnulib_m4code_getdelim
+  fi
   if case $host_os in mingw*) false;; *) test $HAVE_GETLOADAVG = 0 || test 
$REPLACE_GETLOADAVG = 1;; esac; then
     func_gl_gnulib_m4code_open
   fi
@@ -1017,6 +1050,7 @@ AC_DEFUN([gl_INIT],
   AM_CONDITIONAL([gl_GNULIB_ENABLED_dirfd], [$gl_gnulib_enabled_dirfd])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_925677f0343de64b89a9f0c790b4104c], 
[$gl_gnulib_enabled_925677f0343de64b89a9f0c790b4104c])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_euidaccess], 
[$gl_gnulib_enabled_euidaccess])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_getdelim], [$gl_gnulib_enabled_getdelim])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_getdtablesize], 
[$gl_gnulib_enabled_getdtablesize])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], 
[$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36])
@@ -1284,8 +1318,10 @@ AC_DEFUN([gl_FILE_LIST], [
   lib/ftoastr.h
   lib/futimens.c
   lib/get-permissions.c
+  lib/getdelim.c
   lib/getdtablesize.c
   lib/getgroups.c
+  lib/getline.c
   lib/getloadavg.c
   lib/getopt-cdefs.in.h
   lib/getopt-core.h
@@ -1386,6 +1422,7 @@ AC_DEFUN([gl_FILE_LIST], [
   lib/stdio.in.h
   lib/stdlib.in.h
   lib/stpcpy.c
+  lib/stpncpy.c
   lib/str-two-way.h
   lib/strftime.h
   lib/string.in.h
@@ -1462,8 +1499,10 @@ AC_DEFUN([gl_FILE_LIST], [
   m4/fsusage.m4
   m4/fsync.m4
   m4/futimens.m4
+  m4/getdelim.m4
   m4/getdtablesize.m4
   m4/getgroups.m4
+  m4/getline.m4
   m4/getloadavg.m4
   m4/getopt.m4
   m4/getrandom.m4
@@ -1530,6 +1569,7 @@ AC_DEFUN([gl_FILE_LIST], [
   m4/stdio_h.m4
   m4/stdlib_h.m4
   m4/stpcpy.m4
+  m4/stpncpy.m4
   m4/string_h.m4
   m4/strnlen.m4
   m4/strtoimax.m4
diff --git a/m4/ndk-build.m4 b/m4/ndk-build.m4
new file mode 100644
index 00000000000..8769e294452
--- /dev/null
+++ b/m4/ndk-build.m4
@@ -0,0 +1,480 @@
+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])])
+
+AC_ARG_WITH([ndk_cxx_shared],
+  [AS_HELP_STRING([--with-ndk-cxx-shared],
+    [name of the C++ standard library included with the NDK])])
+
+AC_ARG_WITH([ndk_cxx],
+  [AS_HELP_STRING([--with-ndk-cxx],
+    [name of the C++ compiler included with the NDK])])
+
+# ndk_INIT(ABI, API, DIR, CFLAGS)
+# -------------------------------
+# Initialize the Android NDK.  ABI is the ABI being built for.
+# API is the API version being built for.
+# CFLAGS is a list of compiler flags.
+# 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([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
+
+AC_REQUIRE_AUX_FILE([ndk-build-helper.mk])
+ndk_AUX_DIR=$ac_aux_dir
+ndk_ABI=$1
+ndk_MODULES=
+ndk_MAKEFILES=
+ndk_INITIALIZED=yes
+ndk_API=$2
+ndk_DIR=$3
+ndk_ANY_CXX=
+ndk_BUILD_CFLAGS="$4"
+ndk_working_cxx=no
+
+AS_CASE(["$ndk_ABI"],
+  [*arm64*], [ndk_ARCH=arm64],
+  [*arm*], [ndk_ARCH=arm],
+  [*x86_64*], [ndk_ARCH=x86_64],
+  [*x86*], [ndk_ARCH=x86],
+  [*mips64*], [ndk_ARCH=mips64],
+  [*mips*], [ndk_ARCH=mips],
+  [AC_MSG_ERROR([Failed to determine Android device architecture])])
+
+# This is a map between pkg-config style package names and Android
+# ones.
+
+ndk_package_map="libwebpdemux:webpdemux libxml-2.0:libxml2 jansson:libjansson"
+ndk_package_map="$ndk_package_map sqlite3:libsqlite_static_minimal"
+ndk_package_map="$ndk_package_map MagickWand:libmagickwand-7 lcms2:liblcms2"
+
+# 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=`AS_ECHO([$ndk_stuff]) | cut -d: -f1`
+    ndk_value=`AS_ECHO([$ndk_stuff]) | cut -d: -f2`
+
+    if test "$ndk_key" = "$ndk_module"; then
+      ndk_module="$ndk_value"
+      break
+    fi
+  done
+}
+
+# Run the Makefile helper script for the Android.mk file.
+
+ndk_run_test () {
+  # Figure out where the helper Makefile is.
+  ndk_build_helper_file="${ndk_AUX_DIR}ndk-build-helper.mk"
+  ndk_module_extract_awk="${ndk_AUX_DIR}ndk-module-extract.awk"
+  ndk_dir=`AS_DIRNAME([$ndk_android_mk])`
+
+  # Now call Make with the right arguments.
+  "$MAKE" -s -f "$ndk_build_helper_file" EMACS_SRCDIR=`pwd`            \
+    EMACS_ABI="$ndk_ABI" ANDROID_MAKEFILE="$ndk_android_mk"            \
+    NDK_BUILD_DIR="$ndk_DIR" NDK_ROOT="/tmp"                           \
+    ANDROID_MODULE_DIRECTORY="$ndk_dir" BUILD_AUXDIR=$ndk_AUX_DIR      \
+    NDK_BUILD_ARCH="$ndk_ARCH" 2>&AS_MESSAGE_LOG_FD >conftest.ndk
+
+  # Read the output.
+  cat conftest.ndk | awk -f "$ndk_module_extract_awk" MODULE="$ndk_module"
+
+  # Remove the temporary file.
+  rm -f conftest.ndk
+}
+
+# ndk_parse_pkg_config_string PKG_CONFIG_STRING
+# ---------------------------------------------
+# 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=`AS_ECHO_N(["$ndk_input"]) | cut -f1 -d' '`
+    ndk_input=`AS_ECHO_N(["$ndk_input"]) | cut -s -f2- -d' '`
+
+    if test "$ndk_str" = ">=" || test "$ndk_str" = "<=" \
+      || test "$ndk_str" = ">" || test "$ndk_str" = "<" \
+      || test "$ndk_str" = "!="; then
+      ndk_input=`AS_ECHO_N(["$ndk_input"]) | cut -s -f2- -d' '`
+    else
+      ndk_modules="$ndk_modules$ndk_str "
+    fi
+  done
+}
+
+# ndk_resolve_import_module MODULE
+# --------------------------------
+# Resolve MODULE, a single import.  Prepend its makefile to
+# ndk_MAKEFILES if found.  Also, prepend all includes to the variable
+# ndk_import_includes.
+
+ndk_resolve_import_module () {
+  module_name=
+  ndk_module=[$]1
+
+  AC_MSG_CHECKING([for imported $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=`ndk_run_test`
+
+    AS_IF([test -n "${ndk_commands//\n }"], [eval "$ndk_commands"])
+
+    if test -n "$module_name"; then
+      break;
+    fi
+  done
+
+  AS_IF([test -z "$module_name"],
+    [AC_MSG_RESULT([no])
+     AC_MSG_ERROR([The module currently being built depends on [$]1, but \
+that could not be found in the list of directories specified in \
+`--with-ndk-path'.])])
+
+  if test -n "$module_cxx_deps"; then
+    ndk_ANY_CXX=yes
+  fi
+
+  AS_IF([test "$ndk_ANY_CXX" = "yes" && test -z "$with_ndk_cxx_shared"],
+    [AC_MSG_ERROR([The module [$]1 requires the C++ standard library \
+(libc++_shared.so), but it was not found.])])
+
+  AS_IF([test "$ndk_ANY_CXX" = "yes" && test "$ndk_working_cxx" != "yes"],
+    [AC_MSG_ERROR([The module [$]1 requires the C++ standard library \
+(libc++_shared.so), but a working C++ compiler was not found.])])
+
+  AC_MSG_RESULT([yes])
+
+  # Make sure the module is prepended.
+  ndk_MAKEFILES="$ndk_android_mk $ndk_MAKEFILES"
+  ndk_import_includes="$module_includes $ndk_import_includes"
+
+  # Now recursively resolve this module's imports.
+  for ndk_module in $module_imports; do
+    ndk_resolve_import_module $ndk_module
+  done
+}
+
+# ndk_filter_cc_for_cxx
+# ---------------------
+# Run through $CC, removing any options that are not suitable for
+# use in a C++ compiler.
+
+ndk_filter_cc_for_cxx () {
+  for ndk_word in $CC; do
+    AS_CASE([$ndk_word], [*-std=*], [],
+      [AS_ECHO_N(["$ndk_word "])])
+  done
+}
+
+# ndk_subst_cc_onto_cxx
+# ---------------------
+# Print the value of $CXX, followed by any innocent looking options
+# in $CC.
+
+ndk_subst_cc_onto_cxx () {
+  AS_ECHO_N(["$CXX "])
+  ndk_flag=
+  for ndk_word in `AS_ECHO_N(["$CC"]) | cut -s -f2- -d' '`; do
+    AS_IF([test "$ndk_flag" = "yes"],
+      [AS_ECHO_N(["$ndk_word "])
+       ndk_flag=no],
+      [AS_CASE([$ndk_word],
+        [*-sysroot=*],
+          [AS_ECHO_N(["$ndk_word "])],
+       [*-isystem*],
+          [AS_ECHO_N(["$ndk_word "])
+          ndk_flag=yes],
+       [*-sysroot*],
+         [AS_ECHO_N(["$ndk_word "])
+          ndk_flag=yes],
+       [-D__ANDROID_API__*],
+         [AS_ECHO_N(["$ndk_word "])])])
+  done
+}
+
+# Look for a suitable ar and ranlib in the same directory as the C
+# compiler.
+ndk_cc_firstword=`AS_ECHO(["$CC"]) | cut -d' ' -f1`
+ndk_where_cc=`which $ndk_cc_firstword`
+ndk_ar_search_path=$PATH
+ndk_ranlib_search_path=$RANLIB
+
+# First, try to find $host_alias-ar in PATH.
+AC_PATH_PROGS([AR], [$host_alias-ar], [], [$ndk_ar_search_path])
+
+AS_IF([test -z "$AR"],[
+  # Next, try finding either that or llvm-ar in the directory holding
+  # CC.
+  ndk_ar_search_path="`AS_DIRNAME([$ndk_where_cc])`:$ndk_ar_search_path"
+  AC_PATH_PROGS([AR], [$host_alias-ar llvm-ar], [], [$ndk_ar_search_path])])
+
+# First, try to find $host_alias-ranlib in PATH.
+AC_PATH_PROGS([RANLIB], [$host_alias-ranlib], [], [$ndk_ranlib_search_path])
+
+AS_IF([test -z "$RANLIB"],[
+  # Next, try finding either that or llvm-ranlib in the directory
+  # holding CC.
+  
ndk_ranlib_search_path="`AS_DIRNAME([$ndk_where_cc])`:$ndk_ranlib_search_path"
+  AC_PATH_PROGS([RANLIB], [$host_alias-ranlib llvm-ranlib], [],
+    [$ndk_ranlib_search_path])])
+
+NDK_BUILD_NASM=
+
+# Next, try to find nasm on x86.  This doesn't ship with the NDK.
+AS_IF([test "$ndk_ARCH" = "x86" || test "$ndk_ARCH" = "x86_64"],
+  [AC_CHECK_PROGS([NDK_BUILD_NASM], [nasm])])
+
+# Look for a file named ``libc++_shared.so'' in a subdirectory of
+# $ndk_where_cc if it was not specified.
+AC_MSG_CHECKING([for libc++_shared.so])
+
+ndk_where_toolchain=
+AS_IF([test -z "$with_ndk_cxx_shared" && test -n "$ndk_where_cc"],[
+  # Find the NDK root directory.  Go to $ndk_where_cc.
+  SAVE_PWD=`pwd`
+  cd `AS_DIRNAME(["$ndk_where_cc"])`
+
+  # Now, keep moving backwards until pwd ends with ``toolchains''.
+  while :; do
+    if test "`pwd`" = "/"; then
+      cd "$SAVE_PWD"
+      break
+    fi
+
+    ndk_pwd=`pwd`
+    if test "`AS_BASENAME([$ndk_pwd])`" = "toolchains"; then
+      ndk_where_toolchain=$ndk_pwd
+      cd "$SAVE_PWD"
+      break
+    fi
+
+    cd ..
+  done
+
+  ndk_matching_libcxx_shared_so=
+
+  # The toolchain directory should be in "$ndk_where_toolchain".
+  AS_IF([test -n "$ndk_where_toolchain"],[
+    # Now, look in the directory behind it.
+    ndk_cxx_shared_so=`find "$ndk_where_toolchain" -name libc++_shared.so`
+
+    # Look for one with the correct architecture.
+    for ndk_candidate in $ndk_cxx_shared_so; do
+      AS_CASE([$ndk_candidate],
+        [*arm-linux-android*],
+         [AS_IF([test "$ndk_ARCH" = "arm"],
+           [ndk_matching_libcxx_shared_so=$ndk_candidate])],
+       [*aarch64-linux-android*],
+         [AS_IF([test "$ndk_ARCH" = "arm64"],
+           [ndk_matching_libcxx_shared_so=$ndk_candidate])],
+       [*i[[3-6]]86-linux-android*],
+         [AS_IF([test "$ndk_ARCH" = "x86"],
+           [ndk_matching_libcxx_shared_so=$ndk_candidate])],
+       [*x86_64-linux-android*],
+         [AS_IF([test "$ndk_ARCH" = "x86_64"],
+           [ndk_matching_libcxx_shared_so=$ndk_candidate])])
+
+      AS_IF([test -n "$ndk_matching_libcxx_shared_so"],
+        [with_ndk_cxx_shared=$ndk_matching_libcxx_shared_so])
+    done])])
+
+AS_IF([test -z "$with_ndk_cxx_shared"],[AC_MSG_RESULT([no])
+  AC_MSG_WARN([The C++ standard library could not be found.  \
+If you try to build Emacs with a dependency that requires the C++ standard \
+library, Emacs will not build correctly, unless you manually specify the \
+name of an appropriate ``libc++_shared.so'' binary.])],
+  [AC_MSG_RESULT([$with_ndk_cxx_shared])])
+
+ndk_CXX_SHARED=$with_ndk_cxx_shared
+
+# These variables have now been found.  Now look for a C++ compiler.
+# Upon failure, pretend the C compiler is a C++ compiler and use that
+# instead.
+
+ndk_cc_name=`AS_BASENAME(["${ndk_cc_firstword}"])`
+ndk_cxx_name=
+
+AS_CASE([$ndk_cc_name], [*-gcc],
+  [ndk_cxx_name=`AS_ECHO([$ndk_cc_name]) | sed 's/gcc/g++/'`],
+  [ndk_cxx_name="${ndk_cc_name}++"])
+
+AS_IF([test -n "$with_ndk_cxx"], [CXX=$with_ndk_cxx],
+  [AC_PATH_PROGS([CXX], [$ndk_cxx_name],
+     [], [`AS_DIRNAME(["$ndk_where_cc"])`:$PATH])
+   AS_IF([test -z "$CXX"], [CXX=`ndk_filter_cc_for_cxx`],
+     [CXX=`ndk_subst_cc_onto_cxx`])])
+])
+
+# ndk_LATE
+# --------
+# Perform late initialization of the ndk-build system by checking for
+# required C and C++ headers.
+
+AC_DEFUN([ndk_LATE],
+[dnl
+dnl This calls AC_REQUIRE([AC_PROG_CXX]), leading to configure looking
+dnl for a C++ compiler.  However, the language is not restored
+dnl afterwards if not `$ndk_INITIALIZED'.
+AS_IF([test "$ndk_INITIALIZED" = "yes"],[
+  AS_IF([test -n "$CXX"], [AC_LANG_PUSH([C++])
+    AC_CHECK_HEADER([string], [ndk_working_cxx=yes],
+      [AC_MSG_WARN([Your C++ compiler is not properly set up, and\
+ the standard library headers could not be found.])])
+    AC_LANG_POP([C++])])])
+dnl Thus, manually switch back to C here.
+AC_LANG([C])
+])
+
+# 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_MAKEFILES.
+# Set NAME_CFLAGS and NAME_LIBS to the appropriate values.  Then, call
+# ACTION-IF-FOUND, or ACTION-IF-NOT-FOUND upon failure.
+#
+# Resolve any imports specified by MODULE, and expand AC_MSG_ERROR
+# with a suitable error message if imports were not found.
+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=`ndk_run_test`
+
+  eval "$ndk_commands"
+  if test -n "$module_name"; then
+    break;
+  fi
+done
+
+if test -z "$module_name"; then
+  AC_MSG_RESULT([no])
+  $4
+else
+  if test -n "$module_cxx_deps"; then
+    ndk_ANY_CXX=yes
+  fi
+
+  AS_IF([test "$ndk_ANY_CXX" = "yes" && test -z "$with_ndk_cxx_shared"],
+    [AC_MSG_ERROR([The module $1 requires the C++ standard library \
+(libc++_shared.so), but it was not found.])])
+
+  AS_IF([test "$ndk_ANY_CXX" = "yes" && test "$ndk_working_cxx" != "yes"],
+    [AC_MSG_ERROR([The module [$]1 requires the C++ standard library \
+(libc++_shared.so), but a working C++ compiler was not found.])])
+
+  $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
+
+  # Now, resolve imports.  Make sure the imports' Makefiles comes
+  # before ndk_MAKEFILES; likewise for its includes.
+  ndk_import_includes=
+  for ndk_module in $module_imports; do
+    ndk_resolve_import_module $ndk_module
+    $2[]_CFLAGS="$ndk_import_includes [$]$2[]_CFLAGS"
+  done
+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_CXX=$CXX
+    NDK_BUILD_AR=$AR
+    NDK_BUILD_MODULES="$ndk_MODULES"
+    NDK_BUILD_CXX_SHARED="$ndk_CXX_SHARED"
+    NDK_BUILD_ANY_CXX_MODULE=$ndk_ANY_CXX
+    NDK_BUILD_CFLAGS="$ndk_BUILD_CFLAGS"
+
+    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_CXX])
+    AC_SUBST([NDK_BUILD_AR])
+    AC_SUBST([NDK_BUILD_NASM])
+    AC_SUBST([NDK_BUILD_MODULES])
+    AC_SUBST([NDK_BUILD_CXX_SHARED])
+    AC_SUBST([NDK_BUILD_ANY_CXX_MODULE])
+    AC_SUBST([NDK_BUILD_CFLAGS])
+
+    AC_CONFIG_FILES([$ndk_DIR/Makefile])
+    AC_CONFIG_FILES([$ndk_DIR/ndk-build.mk])
+  fi
+])
diff --git a/m4/printf-posix-rpl.m4 b/m4/printf-posix-rpl.m4
new file mode 100644
index 00000000000..36156d43502
--- /dev/null
+++ b/m4/printf-posix-rpl.m4
@@ -0,0 +1,26 @@
+# printf-posix-rpl.m4 serial 4
+dnl Copyright (C) 2007-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_PRINTF_POSIX],
+[
+  AC_REQUIRE([gl_FUNC_VFPRINTF_POSIX])
+  if test $gl_cv_func_vfprintf_posix = no; then
+    gl_REPLACE_PRINTF
+  fi
+])
+
+AC_DEFUN([gl_REPLACE_PRINTF],
+[
+  AC_REQUIRE([gl_STDIO_H_DEFAULTS])
+  AC_REQUIRE([gl_ASM_SYMBOL_PREFIX])
+  AC_LIBOBJ([printf])
+  REPLACE_PRINTF=1
+  AC_DEFINE([REPLACE_PRINTF_POSIX], [1],
+    [Define if printf is overridden by a POSIX compliant gnulib 
implementation.])
+  gl_PREREQ_PRINTF
+])
+
+AC_DEFUN([gl_PREREQ_PRINTF], [:])
diff --git a/m4/stpncpy.m4 b/m4/stpncpy.m4
new file mode 100644
index 00000000000..073607004be
--- /dev/null
+++ b/m4/stpncpy.m4
@@ -0,0 +1,108 @@
+# stpncpy.m4 serial 22
+dnl Copyright (C) 2002-2003, 2005-2007, 2009-2023 Free Software Foundation,
+dnl Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_STPNCPY],
+[
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+  dnl Persuade glibc <string.h> to declare stpncpy().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  dnl The stpncpy() declaration in lib/string.in.h uses 'restrict'.
+  AC_REQUIRE([AC_C_RESTRICT])
+
+  AC_REQUIRE([gl_STRING_H_DEFAULTS])
+
+  dnl Both glibc and AIX (4.3.3, 5.1) have an stpncpy() function
+  dnl declared in <string.h>. Its side effects are the same as those
+  dnl of strncpy():
+  dnl      stpncpy (dest, src, n)
+  dnl overwrites dest[0..n-1], min(strlen(src),n) bytes coming from src,
+  dnl and the remaining bytes being NULs.  However, the return value is
+  dnl   in glibc:   dest + min(strlen(src),n)
+  dnl   in AIX:     dest + max(0,n-1)
+  dnl Only the glibc return value is useful in practice.
+
+  AC_CHECK_DECLS_ONCE([stpncpy])
+  gl_CHECK_FUNCS_ANDROID([stpncpy], [[#include <string.h>]])
+  if test $ac_cv_func_stpncpy = yes; then
+    AC_CACHE_CHECK([for working stpncpy], [gl_cv_func_stpncpy], [
+      AC_RUN_IFELSE(
+        [AC_LANG_SOURCE([[
+#include <stdlib.h>
+#include <string.h> /* for strcpy */
+/* The stpncpy prototype is missing in <string.h> on AIX 4.  */
+#if !HAVE_DECL_STPNCPY
+extern
+# ifdef __cplusplus
+"C"
+# endif
+char *stpncpy (char *dest, const char *src, size_t n);
+#endif
+int main ()
+{
+  int result = 0;
+  const char *src = "Hello";
+  char dest[10];
+  /* AIX 4.3.3 and AIX 5.1 stpncpy() returns dest+1 here.  */
+  {
+    strcpy (dest, "\377\377\377\377\377\377");
+    if (stpncpy (dest, src, 2) != dest + 2)
+      result |= 1;
+  }
+  /* AIX 4.3.3 and AIX 5.1 stpncpy() returns dest+4 here.  */
+  {
+    strcpy (dest, "\377\377\377\377\377\377");
+    if (stpncpy (dest, src, 5) != dest + 5)
+      result |= 2;
+  }
+  /* AIX 4.3.3 and AIX 5.1 stpncpy() returns dest+6 here.  */
+  {
+    strcpy (dest, "\377\377\377\377\377\377");
+    if (stpncpy (dest, src, 7) != dest + 5)
+      result |= 4;
+  }
+  return result;
+}
+]])],
+        [gl_cv_func_stpncpy=yes],
+        [gl_cv_func_stpncpy=no],
+        [dnl Guess yes on glibc systems and musl systems.
+         AC_EGREP_CPP([Thanks for using GNU], [
+#include <features.h>
+#ifdef __GNU_LIBRARY__
+  Thanks for using GNU
+#endif
+],         [gl_cv_func_stpncpy="guessing yes"],
+           [case "$host_os" in
+              *-musl* | midipix*) gl_cv_func_stpncpy="guessing yes" ;;
+              *)                  gl_cv_func_stpncpy="$gl_cross_guess_normal" 
;;
+            esac
+           ])
+        ])
+    ])
+    case "$gl_cv_func_stpncpy" in
+      *yes)
+        AC_DEFINE([HAVE_STPNCPY], [1],
+          [Define if you have the stpncpy() function and it works.])
+        ;;
+      *)
+        REPLACE_STPNCPY=1
+        ;;
+    esac
+  else
+    HAVE_STPNCPY=0
+    case "$gl_cv_onwards_func_stpncpy" in
+      future*) REPLACE_STPNCPY=1 ;;
+    esac
+  fi
+])
+
+# Prerequisites of lib/stpncpy.c.
+AC_DEFUN([gl_PREREQ_STPNCPY], [
+  :
+])
diff --git a/make-dist b/make-dist
index 80c672dbf3a..a3b7219af3a 100755
--- a/make-dist
+++ b/make-dist
@@ -356,7 +356,8 @@ possibly_non_vc_files="
   $top_level_ChangeLog
   MANIFEST aclocal.m4 configure
   admin/charsets/jisx2131-filter
-  src/config.in
+  src/config.in exec/configure
+  exec/config.h.in
 "$(
   find admin doc etc lisp \
    \( -name '*.el' -o -name '*.elc' -o -name '*.map' -o -name '*.stamp' \
diff --git a/msdos/sed1v2.inp b/msdos/sed1v2.inp
index 162ccb3e8d8..1b96cc7ddc4 100644
--- a/msdos/sed1v2.inp
+++ b/msdos/sed1v2.inp
@@ -56,6 +56,9 @@ s/ *@LIBPNG@//
 s/ *@LIBGIF@//
 s/ *@LIBXPM@//
 s/ *@WEBP_LIBS@//
+/^GIF_CFLAGS *=/s/@GIF_CFLAGS@//
+/^JPEG_CFLAGS *=/s/@JPEG_CFLAGS@//
+/^TIFF_CFLAGS *=/s/@TIFF_CFLAGS@//
 /^HAVE_NATIVE_COMP *=/s/@HAVE_NATIVE_COMP@/no/
 /^HAVE_PDUMPER *=/s/@HAVE_PDUMPER@/no/
 /^HAVE_BE_APP *=/s/@HAVE_BE_APP@/no/
@@ -200,6 +203,14 @@ s/ *@WEBP_LIBS@//
 /^PAXCTL_dumped *=/s/=.*$/=/
 /^PAXCTL_notdumped *=/s/=.*$/=/
 /^DUMPING *=/s/@DUMPING@/unexec/
+/^ANDROID_OBJ *=/s/@ANDROID_OBJ@//
+/^ANDROID_LIBS *=/s/@ANDROID_LIBS@//
+/^ANDROID_LDFLAGS *=/s/@ANDROID_LDFLAGS@//
+/^ANDROID_BUILD_CFLAGS *=/s/@ANDROID_BUILD_CFLAGS@//
+/^LIBGMP_CFLAGS *=/s/@LIBGMP_CFLAGS@//
+/^SQLITE3_CFLAGS *=/s/@SQLITE3_CFLAGS@//
+/^LIBSELINUX_CFLAGS *=/s/@LIBSELINUX_CFLAGS@//
+/^XCONFIGURE *=/s/@XCONFIGURE@//
 /^[ \t]*MAKE_PDUMPER_FINGERPRINT = *$/c\
 MAKE_PDUMPER_FINGERPRINT =
 /^lisp\.mk:/,/^$/c\
@@ -283,3 +294,4 @@ s| -I\. -I\$(srcdir)| -I.|
 /^      *test "X/d
 /\$(CC) -o \$@.tmp/s/\$@.tmp/\$@/
 /mv \$@.tmp \$@/d
+/^top_builddir =*/s/@top_builddir@/../
diff --git a/msdos/sed3v2.inp b/msdos/sed3v2.inp
index 9688a27b066..0699fb68b02 100644
--- a/msdos/sed3v2.inp
+++ b/msdos/sed3v2.inp
@@ -57,3 +57,4 @@
 /^GETOPT_H *=/s!@GETOPT_H@!getopt.h!
 /^GETOPTOBJS *=/s!@GETOPTOBJS@!getopt.o getopt1.o!
 /^INSTALLABLES/s/emacsclient[^ ]* *//
+/^XCONFIGURE *=/s/@XCONFIGURE@//
diff --git a/msdos/sedlibcf.inp b/msdos/sedlibcf.inp
index 931ceb8f044..8966e799a38 100644
--- a/msdos/sedlibcf.inp
+++ b/msdos/sedlibcf.inp
@@ -20,3 +20,4 @@
 # ----------------------------------------------------------------------
 s/c++defs/cxxdefs/g
 s/\([a-zA-Z0-9_]*\)\.in\.h/\1.in-h/g
+/^XCONFIGURE *=/s/@XCONFIGURE@//
diff --git a/msdos/sedlibmk.inp b/msdos/sedlibmk.inp
index c3f410bd74d..4300be47d0b 100644
--- a/msdos/sedlibmk.inp
+++ b/msdos/sedlibmk.inp
@@ -156,6 +156,7 @@ s/@PACKAGE@/emacs/
 /^HYBRID_MALLOC *=/s/@HYBRID_MALLOC@//
 /^WARN_CFLAGS *=/s/@WARN_CFLAGS@//
 /^WERROR_CFLAGS *=/s/@WERROR_CFLAGS@//
+/^ANDROID_BUILD_CFLAGS *=/s/@ANDROID_BUILD_CFLAGS@//
 /^DEFS *=/s/@[^@\n]*@//
 /^DEPDIR *=/s/@[^@\n]*@/deps/
 /^ECHO_N *=/s/@[^@\n]*@/-n/
@@ -199,6 +200,9 @@ s/@PACKAGE@/emacs/
 /^GL_GNULIB_[^ =]* *= *@/s/@[^@\n]*@/0/
 /^GL_GSETTINGS_CFLAGS *=/s/@[^@\n]*@//
 /^GL_GSETTINGS_LIBS *=/s/@[^@\n]*@//
+# Miscellaneous variables.
+/^DIR_HAS_FD_MEMBER *=/s/@DIR_HAS_FD_MEMBER@/0/
+/^LOCALE_FR_UTF8 *=/s/@LOCALE_FR_UTF8@/none/
 #
 # Edit the HAVE_foo variables
 /^HAVE_ATOLL *=/s/@HAVE_ATOLL@/0/
@@ -299,8 +303,10 @@ s/@PACKAGE@/emacs/
 /^NEXT_DIRENT_H *=/s/@[^@\n]*@/<dirent.h>/
 /^NEXT_ERRNO_H *=/s/@[^@\n]*@//
 /^NEXT_FCNTL_H *=/s/@[^@\n]*@/<fcntl.h>/
+/^NEXT_FLOAT_H *=/s/@[^@\n]*@//
 /^NEXT_GETOPT_H *=/s/@[^@\n]*@/<getopt.h>/
 /^NEXT_LIMITS_H *=/s/@[^@\n]*@/<limits.h>/
+/^NEXT_MATH_H *=/s/@[^@\n]*@//
 /^NEXT_SIGNAL_H *=/s/@[^@\n]*@/<signal.h>/
 /^NEXT_STDDEF_H *=/s/@[^@\n]*@/<stddef.h>/
 /^NEXT_STDIO_H *=/s/@[^@\n]*@/<stdio.h>/
@@ -309,9 +315,11 @@ s/@PACKAGE@/emacs/
 /^NEXT_STRING_H *=/s/@[^@\n]*@/<string.h>/
 /^NEXT_SYS_SELECT_H *=/s/@[^@\n]*@//
 /^NEXT_SYS_STAT_H *=/s!@[^@\n]*@!<sys/stat.h>!
+/^NEXT_SYS_RANDOM_H *=/s/@[^@\n]*@//
 /^NEXT_SYS_TIME_H *=/s/@[^@\n]*@//
 /^NEXT_SYS_TYPES_H *=/s!@[^@\n]*@!<sys/types.h>!
 /^NEXT_TIME_H *=/s/@[^@\n]*@/<time.h>/
+/^NEXT_INTTYPES_H *=/s/@[^@\n]*@//
 /^NEXT_UNISTD_H *=/s/@[^@\n]*@/<unistd.h>/
 /^OBJEXT *=/s/@[^@\n]*@/o/
 /^PRAGMA_COLUMNS *=/s/@[^@\n]*@//
@@ -331,6 +339,7 @@ s/@PACKAGE@/emacs/
 /^DIRENT_H *=/s/@[^@\n]*@//
 /^ERRNO_H *=/s/@[^@\n]*@//
 /^EXECINFO_H *=/s/@[^@\n]*@/execinfo.h/
+/^FLOAT_H *=/s/@[^@\n]*@//
 /^GETOPT_CDEFS_H *=/s/@[^@\n]*@/getopt-cdefs.h/
 /^GMP_H *=/s/@[^@\n]*@/gmp.h/
 /^LIMITS_H *=/s/@[^@\n]*@/limits.h/
@@ -427,7 +436,6 @@ s/= @GL_GENERATE_STDDEF_H_CONDITION@/= 1/
 s/= @GL_GENERATE_STDINT_H_CONDITION@/= 1/
 s/= @GL_GENERATE_LIMITS_H_CONDITION@/= 1/
 s/= @GL_GENERATE_ERRNO_H_CONDITION@/= /
-s/= @GL_GENERATE_LIMITS_H_CONDITION@/= /
 s/= @GL_GENERATE_GETOPT_CDEFS_H_CONDITION@/= 1/
 s/= @GL_GENERATE_GETOPT_H_CONDITION@/= 1/
 s/= @GL_GENERATE_GMP_H_CONDITION@/= 1/
@@ -436,6 +444,8 @@ s/= @GL_GENERATE_MINI_GMP_H_CONDITION@/= 1/
 s/= @GL_GENERATE_STDCKDINT_H_CONDITION@/= 1/
 s/= @GL_COND_OBJ_STDIO_READ_CONDITION@/= /
 s/= @GL_COND_OBJ_STDIO_WRITE_CONDITION@/= /
+s/= @GL_COND_OBJ_STPNCPY_CONDITION@/= /
+s/= @GL_COND_OBJ_.*@/= 1/
 s/\$\(MKDIR_P\) malloc//
 #
 # Determine which modules to build and which to omit
@@ -455,6 +465,8 @@ OMIT_GNULIB_MODULE_fcntl = true\
 OMIT_GNULIB_MODULE_fdopendir = true\
 OMIT_GNULIB_MODULE_fstatat = true\
 OMIT_GNULIB_MODULE_fsync = true\
+OMIT_GNULIB_MODULE_getline = true\
+OMIT_GNULIB_MODULE_getdelim = true\
 OMIT_GNULIB_MODULE_getdtablesize = true\
 OMIT_GNULIB_MODULE_getgroups = true\
 OMIT_GNULIB_MODULE_gettimeofday = true\
@@ -462,6 +474,7 @@ OMIT_GNULIB_MODULE_group-member = true\
 OMIT_GNULIB_MODULE_inttypes-incomplete = true\
 OMIT_GNULIB_MODULE_localtime-buffer = true\
 OMIT_GNULIB_MODULE_lstat = true\
+OMIT_GNULIB_MODULE_math = true\
 OMIT_GNULIB_MODULE_nanosleep = true\
 OMIT_GNULIB_MODULE_open = true\
 OMIT_GNULIB_MODULE_pipe2 = true\
@@ -470,12 +483,13 @@ OMIT_GNULIB_MODULE_putenv = true\
 OMIT_GNULIB_MODULE_qcopy-acl = true\
 OMIT_GNULIB_MODULE_readlink = true\
 OMIT_GNULIB_MODULE_readlinkat = true\
+OMIT_GNULIB_MODULE_stpcpy = true\
 OMIT_GNULIB_MODULE_strtoimax = true\
 OMIT_GNULIB_MODULE_strtoll = true\
 OMIT_GNULIB_MODULE_symlink = true\
 OMIT_GNULIB_MODULE_sys_select = true\
 OMIT_GNULIB_MODULE_sys_time = true\
-OMIT_GNULIB_MODULE_crypto\/md5 = true
+OMIT_GNULIB_MODULE_crypto\/md5 = true\
 /^arg-nonnull\.h:/,/^[         ][      ]*mv /c\
 arg-nonnull.h: $(top_srcdir)/build-aux/snippet/arg-nonnull.h\
        sed -n -e '/GL_ARG_NONNULL/,$$p' < 
$(top_srcdir)/build-aux/snippet/arg-nonnull.h > $@
diff --git a/nt/gnulib-cfg.mk b/nt/gnulib-cfg.mk
index eca3778f203..d7241976f83 100644
--- a/nt/gnulib-cfg.mk
+++ b/nt/gnulib-cfg.mk
@@ -44,34 +44,55 @@
 OMIT_GNULIB_MODULE_acl-permissions = true
 OMIT_GNULIB_MODULE_allocator = true
 OMIT_GNULIB_MODULE_at-internal = true
+OMIT_GNULIB_MODULE_canonicalize-lgpl = true
 OMIT_GNULIB_MODULE_careadlinkat = true
 OMIT_GNULIB_MODULE_dirent = true
 OMIT_GNULIB_MODULE_dirfd = true
+OMIT_GNULIB_MODULE_fchmodat = true
 OMIT_GNULIB_MODULE_fcntl = true
 OMIT_GNULIB_MODULE_fcntl-h = true
+OMIT_GNULIB_MODULE_file-has-acl = true
+OMIT_GNULIB_MODULE_float = true
+OMIT_GNULIB_MODULE_fpucw = true
 OMIT_GNULIB_MODULE_free-posix = true
+OMIT_GNULIB_MODULE_frexp-nolibm = true
+OMIT_GNULIB_MODULE_frexpl-nolibm = true
+OMIT_GNULIB_MODULE_fseterr = true
 OMIT_GNULIB_MODULE_fsusage = true
+OMIT_GNULIB_MODULE_futimens = true
+OMIT_GNULIB_MODULE_getdelim = true
+OMIT_GNULIB_MODULE_getline = true
 OMIT_GNULIB_MODULE_inttypes-incomplete = true
+OMIT_GNULIB_MODULE_isnand-nolibm = true
+OMIT_GNULIB_MODULE_isnanf-nolibm = true
+OMIT_GNULIB_MODULE_isnanl-nolibm = true
+OMIT_GNULIB_MODULE_lchmod = true
 OMIT_GNULIB_MODULE_malloc-posix = true
+OMIT_GNULIB_MODULE_math = true
+OMIT_GNULIB_MODULE_nanosleep = true
+OMIT_GNULIB_MODULE_nproc = true
 OMIT_GNULIB_MODULE_open = true
 OMIT_GNULIB_MODULE_pipe2 = true
+OMIT_GNULIB_MODULE_printf-frexp = true
+OMIT_GNULIB_MODULE_printf-frexpl = true
+OMIT_GNULIB_MODULE_printf-posix = true
 OMIT_GNULIB_MODULE_realloc-gnu = true
 OMIT_GNULIB_MODULE_realloc-posix = true
 OMIT_GNULIB_MODULE_secure_getenv = true
 OMIT_GNULIB_MODULE_signal-h = true
+OMIT_GNULIB_MODULE_signbit = true
+OMIT_GNULIB_MODULE_size_max = true
 OMIT_GNULIB_MODULE_stdio = true
 OMIT_GNULIB_MODULE_stdlib = true
+OMIT_GNULIB_MODULE_stpncpy = true
 OMIT_GNULIB_MODULE_sys_select = true
 OMIT_GNULIB_MODULE_sys_stat = true
 OMIT_GNULIB_MODULE_sys_time = true
 OMIT_GNULIB_MODULE_sys_types = true
 OMIT_GNULIB_MODULE_unistd = true
-OMIT_GNULIB_MODULE_canonicalize-lgpl = true
 OMIT_GNULIB_MODULE_utimens = true
-OMIT_GNULIB_MODULE_fchmodat = true
-OMIT_GNULIB_MODULE_lchmod = true
-OMIT_GNULIB_MODULE_futimens = true
 OMIT_GNULIB_MODULE_utimensat = true
-OMIT_GNULIB_MODULE_file-has-acl = true
-OMIT_GNULIB_MODULE_nproc = true
-OMIT_GNULIB_MODULE_nanosleep = true
+OMIT_GNULIB_MODULE_vasnprintf = true
+OMIT_GNULIB_MODULE_vasprintf = true
+OMIT_GNULIB_MODULE_vfprintf-posix = true
+OMIT_GNULIB_MODULE_xsize = true
diff --git a/src/ChangeLog.12 b/src/ChangeLog.12
index 74df9809261..fdb7a2e659b 100644
--- a/src/ChangeLog.12
+++ b/src/ChangeLog.12
@@ -7077,7 +7077,7 @@
 
        * .gdbinit: Use "set $dummy = ..." to avoid warnings from GDB 7.5
        and later about non-assignments with no effect.  See discussion at
-       http://sourceware.org/ml/gdb-patches/2012-08/msg00518.html for
+       https://sourceware.org/ml/gdb-patches/2012-08/msg00518.html for
        details.
 
 2012-08-20  Dmitry Antipov  <dmantipov@yandex.ru>
diff --git a/src/Makefile.in b/src/Makefile.in
index e6072ffaaf6..87f03261a41 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -33,6 +33,20 @@ top_builddir = @top_builddir@
 # MinGW CPPFLAGS may use this.
 abs_top_srcdir=@abs_top_srcdir@
 VPATH = $(srcdir)
+
+# This is not empty if this is a Makefile that will be copied to
+# cross/src.
+XCONFIGURE = @XCONFIGURE@
+
+ifneq ($(XCONFIGURE),)
+vpath %.c := $(srcdir)
+vpath %.h := $(srcdir)
+endif
+
+# abs_top_builddir is the name of the toplevel build directory under
+# cross-compiled builds.
+abs_top_builddir = @abs_top_builddir@
+
 CC = @CC@
 CXX = @CXX@
 CFLAGS = @CFLAGS@
@@ -48,6 +62,7 @@ LIBOBJS = @LIBOBJS@
 
 lispsource = $(top_srcdir)/lisp
 lib = ../lib
+hostlib = $(top_builddir)/lib
 libsrc = ../lib-src
 etc = ../etc
 oldXMenudir = ../oldXMenu
@@ -127,6 +142,10 @@ LIB_PTHREAD=@LIB_PTHREAD@
 
 LIBIMAGE=@LIBTIFF@ @LIBJPEG@ @LIBPNG@ @LIBGIF@ @LIBXPM@ @WEBP_LIBS@
 
+GIF_CFLAGS=@GIF_CFLAGS@
+JPEG_CFLAGS=@JPEG_CFLAGS@
+TIFF_CFLAGS=@TIFF_CFLAGS@
+
 XCB_LIBS=@XCB_LIBS@
 XFT_LIBS=@XFT_LIBS@
 XRENDER_LIBS=@XRENDER_LIBS@
@@ -241,6 +260,7 @@ LIBXML2_LIBS = @LIBXML2_LIBS@
 LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
 
 SQLITE3_LIBS = @SQLITE3_LIBS@
+SQLITE3_CFLAGS = @SQLITE3_CFLAGS@
 
 GETADDRINFO_A_LIBS = @GETADDRINFO_A_LIBS@
 
@@ -327,12 +347,13 @@ W32_RES_LINK=@W32_RES_LINK@
 ## if HAVE_HARFBUZZ, hbfont.o is added regardless of the rest
 FONT_OBJ=@FONT_OBJ@
 
-## Empty for MinGW, cm.o for the rest.
+## Empty for MinGW and Android, cm.o for the rest.
 CM_OBJ=@CM_OBJ@
 
 LIBGPM = @LIBGPM@
 
 LIBSELINUX_LIBS = @LIBSELINUX_LIBS@
+LIBSELINUX_CFLAGS = @LIBSELINUX_CFLAGS@
 
 LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@
 LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@
@@ -371,6 +392,13 @@ HAIKU_CXX_OBJ = @HAIKU_CXX_OBJ@
 HAIKU_LIBS = @HAIKU_LIBS@
 HAIKU_CFLAGS = @HAIKU_CFLAGS@
 
+ANDROID_OBJ = @ANDROID_OBJ@
+ANDROID_LIBS = @ANDROID_LIBS@
+ANDROID_LDFLAGS = @ANDROID_LDFLAGS@
+ANDROID_BUILD_CFLAGS = @ANDROID_BUILD_CFLAGS@
+
+LIBGMP_CFLAGS = @LIBGMP_CFLAGS@
+
 DUMPING=@DUMPING@
 CHECK_STRUCTS = @CHECK_STRUCTS@
 HAVE_PDUMPER = @HAVE_PDUMPER@
@@ -412,7 +440,9 @@ EMACS_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \
   $(HARFBUZZ_CFLAGS) $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
   $(LIBSYSTEMD_CFLAGS) $(JSON_CFLAGS) $(XSYNC_CFLAGS) $(TREE_SITTER_CFLAGS) \
   $(LIBGNUTLS_CFLAGS) $(NOTIFY_CFLAGS) $(CAIRO_CFLAGS) \
-  $(WERROR_CFLAGS) $(HAIKU_CFLAGS) $(XCOMPOSITE_CFLAGS) $(XSHAPE_CFLAGS)
+  $(WERROR_CFLAGS) $(HAIKU_CFLAGS) $(XCOMPOSITE_CFLAGS) $(XSHAPE_CFLAGS) \
+  $(ANDROID_BUILD_CFLAGS) $(GIF_CFLAGS) $(JPEG_CFLAGS) $(SQLITE3_CFLAGS) \
+  $(LIBGMP_CFLAGS) $(TIFF_CFLAGS) $(LIBSELINUX_CFLAGS)
 ALL_CFLAGS = $(EMACS_CFLAGS) $(WARN_CFLAGS) $(CFLAGS)
 ALL_OBJC_CFLAGS = $(EMACS_CFLAGS) \
   $(filter-out $(NON_OBJC_CFLAGS),$(WARN_CFLAGS)) $(CFLAGS) \
@@ -451,7 +481,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o 
$(XMENU_OBJ) window.o     \
        $(if $(HYBRID_MALLOC),sheap.o)                                         \
        $(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ)        \
        $(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) $(JSON_OBJ)                \
-       $(HAIKU_OBJ) $(PGTK_OBJ)
+       $(HAIKU_OBJ) $(PGTK_OBJ) $(ANDROID_OBJ)
 doc_obj = $(base_obj) $(NS_OBJC_OBJ)
 obj = $(doc_obj) $(HAIKU_CXX_OBJ)
 
@@ -468,7 +498,8 @@ SOME_MACHINE_OBJECTS = dosfns.o msdos.o \
   w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \
   w16select.o widget.o xfont.o ftfont.o xftfont.o gtkutil.o \
   xsettings.o xgselect.o termcap.o hbfont.o \
-  haikuterm.o haikufns.o haikumenu.o haikufont.o
+  haikuterm.o haikufns.o haikumenu.o haikufont.o androidterm.o androidfns.o \
+  androidfont.o androidselect.c sfntfont-android.c sfntfont.c
 
 ## gmalloc.o if !SYSTEM_MALLOC && !DOUG_LEA_MALLOC, else empty.
 GMALLOC_OBJ=@GMALLOC_OBJ@
@@ -539,15 +570,19 @@ endif
 ## we need to remove leim-list, site-init, and site-load by hand.
 ## There's not much to choose between these two approaches,
 ## but the second one seems like it could be more future-proof.
+##
+## This list is placed in the toplevel build directory to prevent it
+## from being unnecessarily regenerated by the successive use of this
+## Makefile within cross/Makefile.
 shortlisp =
-lisp.mk: $(lispsource)/loadup.el
+$(abs_top_builddir)/src/lisp.mk: $(lispsource)/loadup.el
        ${AM_V_GEN}( printf 'shortlisp = \\\n'; \
        sed -n 's/^[ \t]*(load "\([^"]*\)".*/\1/p' $< | \
          sed -e 's/$$/.elc \\/' -e 's/\.el\.elc/.el/'; \
        echo "" ) > $@.tmp
        $(AM_V_at)mv -f $@.tmp $@
 
--include lisp.mk
+-include $(abs_top_builddir)/src/lisp.mk
 shortlisp_filter = leim/leim-list.el site-load.elc site-init.elc
 shortlisp := $(filter-out ${shortlisp_filter},${shortlisp})
 ## Place loaddefs.el first, so it gets generated first, since it is on
@@ -571,15 +606,18 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(PGTK_LIBS) 
$(LIBX_BASE) $(LIBIMAGE
    $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) $(LCMS2_LIBS) \
    $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) \
    $(JSON_LIBS) $(LIBGMP) $(LIBGCCJIT_LIBS) $(XINPUT_LIBS) $(HAIKU_LIBS) \
-   $(TREE_SITTER_LIBS) $(SQLITE3_LIBS) $(XCOMPOSITE_LIBS) $(XSHAPE_LIBS)
+   $(TREE_SITTER_LIBS) $(SQLITE3_LIBS) $(XCOMPOSITE_LIBS) $(XSHAPE_LIBS) \
+   $(ANDROID_LIBS)
 
 ## FORCE it so that admin/unidata can decide whether this file is
 ## up-to-date.  Although since charprop depends on bootstrap-emacs,
 ## and emacs depends on charprop, in practice this rule was always run
 ## anyway.
+ifneq ($(XCONFIGURE),android)
 $(lispsource)/international/charprop.el: \
   FORCE | bootstrap-emacs$(EXEEXT) $(bootstrap_pdmp)
        $(MAKE) -C ../admin/unidata all EMACS="../$(bootstrap_exe)"
+endif
 
 ## We require charprop.el to exist before ucs-normalize.el is
 ## byte-compiled, because ucs-normalize.el needs to load 2 uni-*.el files.
@@ -613,7 +651,7 @@ SYSTEM_TYPE = @SYSTEM_TYPE@
 ## since not all pieces are used on all platforms.  But DOC depends
 ## on all of $lisp, and emacs depends on DOC, so it is ok to use $lisp here.
 emacs$(EXEEXT): temacs$(EXEEXT) \
-                lisp.mk $(etc)/DOC $(lisp) \
+                $(abs_top_builddir)/src/lisp.mk $(etc)/DOC $(lisp) \
                 $(lispsource)/international/charprop.el ${charsets}
 ifeq ($(SYSTEM_TYPE),cygwin)
        find ${top_builddir} -name '*.eln' | rebase -v -O -T -
@@ -660,7 +698,7 @@ $(etc)/DOC: $(libsrc)/make-docfile$(EXEEXT) $(doc_obj)
          $(SOME_MACHINE_OBJECTS) $(doc_obj) > $(etc)/DOC
 
 $(libsrc)/make-docfile$(EXEEXT) $(libsrc)/make-fingerprint$(EXEEXT): \
-  $(lib)/libgnu.a
+  $(hostlib)/libgnu.a
        $(MAKE) -C $(dir $@) $(notdir $@)
 
 buildobj.h: Makefile
@@ -721,6 +759,47 @@ ifeq ($(DUMPING),unexec)
   endif
 endif
 
+ifeq ($(XCONFIGURE),android)
+## The Android package internally links to a shared library named
+## `libemacs.so' at startup.  It is built almost the same way temacs
+## is.  But it is position independent, and 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)
+
+## Builds using libemacs.so (Android) don't dump Emacs within this
+## Makefile, but on device.  Make sure the library hash changes for
+## each change in shortlisp by linking an object that changes
+## accordingly to it.
+BUILD_COUNTER_OBJ = build-counter.o
+
+# This file is then compiled into libemacs.so
+build-counter.c: $(abs_top_builddir)/src/lisp.mk $(lisp)
+       $(AM_V_GEN) $(top_srcdir)/build-aux/makecounter.sh $@
+
+libemacs.so: $(ALLOBJS) $(BUILD_COUNTER_OBJ) $(LIBEGNU_ARCHIVE) \
+            $(EMACSRES) $(MAKE_PDUMPER_FINGERPRINT) \
+            $(NDK_BUILD_SHARED) $(NDK_BUILD_STATIC) $(etc)/DOC
+       $(AM_V_CCLD)$(CC) -o $@ $(ALL_CFLAGS) $(TEMACS_LDFLAGS) \
+         $(ANDROID_LDFLAGS) $(LDFLAGS) -shared $(ALLOBJS) \
+         $(BUILD_COUNTER_OBJ) $(LIBEGNU_ARCHIVE) $(LIBES)
+       $(AM_V_at)$(MAKE_PDUMPER_FINGERPRINT) $@
+
+# There is also a binary named `android-emacs' which simply calls
+# emacs.so.  It need not link against libemacs because app_process
+# will do that instead.
+
+android-emacs: android-emacs.c
+       $(AM_V_CCLD)$(CC) $(lastword $^) -o $@ \
+         $(ALL_CFLAGS) $(LDFLAGS)             \
+         $(LIBEGNU_ARCHIVE)
+endif
+
 ## The following oldxmenu-related rules are only (possibly) used if
 ## HAVE_X11 && !USE_GTK, but there is no harm in always defining them.
 $(lwlibdir)/liblw.a: $(config_h) globals.h lisp.h FORCE
@@ -749,7 +828,8 @@ ns-app: emacs$(EXEEXT) $(pdmp)
 .PHONY: versionclean
 
 mostlyclean:
-       rm -f temacs$(EXEEXT) core ./*.core \#* ./*.o
+       rm -f android-emacs libemacs.so
+       rm -f temacs$(EXEEXT) core ./*.core \#* ./*.o build-counter.c
        rm -f dmpstruct.h
        rm -f emacs.pdmp
        rm -f ../etc/DOC
@@ -838,10 +918,16 @@ tags: TAGS ../lisp/TAGS $(lwlibdir)/TAGS $(lib)/TAGS
 ## To solve the freshness issue, in the past we tried various clever tricks,
 ## but now that we require GNU make, we can simply specify
 ## bootstrap-emacs$(EXEEXT) as an order-only prerequisite.
+##
+## bootstrap-emacs doesn't have to be built when cross-compiling
+## libemacs.so for Android, however, as the Lisp files have already
+## been compiled by the top level `src' Makefile.
 
+ifneq ($(XCONFIGURE),android)
 %.elc: %.el | bootstrap-emacs$(EXEEXT) $(bootstrap_pdmp)
        @$(MAKE) $(AM_V_NO_PD) -C ../lisp EMACS="$(bootstrap_exe)"\
                THEFILE=$< $<c
+endif
 
 ifeq ($(HAVE_NATIVE_COMP):$(NATIVE_DISABLED),yes:)
 ## The following rules are used only when building a source tarball
@@ -895,8 +981,10 @@ NATIVE_COMPILATION_AOT = @NATIVE_COMPILATION_AOT@
        fi
 endif
 
+ifneq ($(XCONFIGURE),android)
 $(lispsource)/loaddefs.el: | bootstrap-emacs$(EXEEXT) $(bootstrap_pdmp)
        $(MAKE) -C ../lisp autoloads EMACS="$(bootstrap_exe)"
+endif
 
 ## Dump an Emacs executable named bootstrap-emacs containing the
 ## files from loadup.el in source form.
diff --git a/src/alloc.c b/src/alloc.c
index f1ce838940c..3365186f250 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -50,6 +50,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include TERM_HEADER
 #endif /* HAVE_WINDOW_SYSTEM */
 
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#include "sfntfont.h"
+#endif
+
 #ifdef HAVE_TREE_SITTER
 #include "treesit.h"
 #endif
@@ -3391,6 +3395,15 @@ cleanup_vector (struct Lisp_Vector *vector)
              drv->close_font (font);
            }
        }
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+      /* The Android font driver needs the ability to associate extra
+        information with font entities.  */
+      if (((vector->header.size & PSEUDOVECTOR_SIZE_MASK)
+          == FONT_ENTITY_MAX)
+         && PSEUDOVEC_STRUCT (vector, font_entity)->is_android)
+       android_finalize_font_entity (PSEUDOVEC_STRUCT (vector, font_entity));
+#endif
     }
   else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_THREAD))
     finalize_one_thread (PSEUDOVEC_STRUCT (vector, thread_state));
@@ -5674,6 +5687,88 @@ check_pure_size (void)
             pure_bytes_used + pure_bytes_used_before_overflow);
 }
 
+/* Find the byte sequence {DATA[0], ..., DATA[NBYTES-1], '\0'} from
+   the non-Lisp data pool of the pure storage, and return its start
+   address.  Return NULL if not found.  */
+
+static char *
+find_string_data_in_pure (const char *data, ptrdiff_t nbytes)
+{
+  int i;
+  ptrdiff_t skip, bm_skip[256], last_char_skip, infinity, start, start_max;
+  const unsigned char *p;
+  char *non_lisp_beg;
+
+  if (pure_bytes_used_non_lisp <= nbytes)
+    return NULL;
+
+  /* The Android GCC generates code like:
+
+   0xa539e755 <+52>:   lea    0x430(%esp),%esi
+=> 0xa539e75c <+59>:   movdqa %xmm0,0x0(%ebp)
+   0xa539e761 <+64>:   add    $0x10,%ebp
+
+   but data is not aligned appropriately, so a GP fault results.  */
+
+#if defined __i386__                           \
+  && defined HAVE_ANDROID                      \
+  && !defined ANDROID_STUBIFY                  \
+  && !defined (__clang__)
+  if ((intptr_t) data & 15)
+    return NULL;
+#endif
+
+  /* Set up the Boyer-Moore table.  */
+  skip = nbytes + 1;
+  for (i = 0; i < 256; i++)
+    bm_skip[i] = skip;
+
+  p = (const unsigned char *) data;
+  while (--skip > 0)
+    bm_skip[*p++] = skip;
+
+  last_char_skip = bm_skip['\0'];
+
+  non_lisp_beg = purebeg + pure_size - pure_bytes_used_non_lisp;
+  start_max = pure_bytes_used_non_lisp - (nbytes + 1);
+
+  /* See the comments in the function `boyer_moore' (search.c) for the
+     use of `infinity'.  */
+  infinity = pure_bytes_used_non_lisp + 1;
+  bm_skip['\0'] = infinity;
+
+  p = (const unsigned char *) non_lisp_beg + nbytes;
+  start = 0;
+  do
+    {
+      /* Check the last character (== '\0').  */
+      do
+       {
+         start += bm_skip[*(p + start)];
+       }
+      while (start <= start_max);
+
+      if (start < infinity)
+       /* Couldn't find the last character.  */
+       return NULL;
+
+      /* No less than `infinity' means we could find the last
+        character at `p[start - infinity]'.  */
+      start -= infinity;
+
+      /* Check the remaining characters.  */
+      if (memcmp (data, non_lisp_beg + start, nbytes) == 0)
+       /* Found.  */
+       return non_lisp_beg + start;
+
+      start += last_char_skip;
+    }
+  while (start <= start_max);
+
+  return NULL;
+}
+
+
 /* Return a string allocated in pure space.  DATA is a buffer holding
    NCHARS characters, and NBYTES bytes of string data.  MULTIBYTE
    means make the result string multibyte.
@@ -5951,16 +6046,45 @@ mark_pinned_objects (void)
     mark_object (pobj->object);
 }
 
+#if defined HAVE_ANDROID && !defined (__clang__)
+
+/* The Android gcc is broken and needs the following version of
+   make_lisp_symbol.  Otherwise a mysterious ICE pops up.  */
+
+#define make_lisp_symbol android_make_lisp_symbol
+
+static Lisp_Object
+android_make_lisp_symbol (struct Lisp_Symbol *sym)
+{
+  intptr_t symoffset;
+
+  symoffset = (intptr_t) sym;
+  INT_SUBTRACT_WRAPV (symoffset, (intptr_t) &lispsym,
+                     &symoffset);
+
+  {
+    Lisp_Object a = TAG_PTR (Lisp_Symbol, symoffset);
+    return a;
+  }
+}
+
+#endif
+
 static void
 mark_pinned_symbols (void)
 {
   struct symbol_block *sblk;
-  int lim = (symbol_block_pinned == symbol_block
-            ? symbol_block_index : SYMBOL_BLOCK_SIZE);
+  int lim;
+  struct Lisp_Symbol *sym, *end;
+
+  if (symbol_block_pinned == symbol_block)
+    lim = symbol_block_index;
+  else
+    lim = SYMBOL_BLOCK_SIZE;
 
   for (sblk = symbol_block_pinned; sblk; sblk = sblk->next)
     {
-      struct Lisp_Symbol *sym = sblk->symbols, *end = sym + lim;
+      sym = sblk->symbols, end = sym + lim;
       for (; sym < end; ++sym)
        if (sym->u.s.pinned)
          mark_object (make_lisp_symbol (sym));
@@ -6259,6 +6383,13 @@ garbage_collect (void)
   mark_xselect ();
 #endif
 
+#ifdef HAVE_ANDROID
+  mark_androidterm ();
+#ifndef ANDROID_STUBIFY
+  mark_sfntfont ();
+#endif
+#endif
+
 #ifdef HAVE_NS
   mark_nsterm ();
 #endif
@@ -6667,6 +6798,11 @@ static void
 mark_frame (struct Lisp_Vector *ptr)
 {
   struct frame *f = (struct frame *) ptr;
+#ifdef HAVE_TEXT_CONVERSION
+  struct text_conversion_action *tem;
+#endif
+
+
   mark_vectorlike (&ptr->header);
   mark_face_cache (f->face_cache);
 #ifdef HAVE_WINDOW_SYSTEM
@@ -6678,6 +6814,15 @@ mark_frame (struct Lisp_Vector *ptr)
         mark_vectorlike (&font->header);
     }
 #endif
+
+#ifdef HAVE_TEXT_CONVERSION
+  mark_object (f->conversion.compose_region_start);
+  mark_object (f->conversion.compose_region_end);
+  mark_object (f->conversion.compose_region_overlay);
+
+  for (tem = f->conversion.actions; tem; tem = tem->next)
+    mark_object (tem->data);
+#endif
 }
 
 static void
diff --git a/src/android-asset.h b/src/android-asset.h
new file mode 100644
index 00000000000..4fb309f1645
--- /dev/null
+++ b/src/android-asset.h
@@ -0,0 +1,422 @@
+/* Android initialization for GNU Emacs.
+
+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 <android/log.h>
+
+/* This file contains an emulation of the Android asset manager API
+   used on builds for Android 2.2.  It is included by android.c
+   whenever appropriate.
+
+   The replacements in this file are not thread safe and must only be
+   called from the creating thread.  */
+
+struct android_asset_manager
+{
+  /* JNI environment.  */
+  JNIEnv *env;
+
+  /* Asset manager class and functions.  */
+  jclass class;
+  jmethodID open_fd;
+
+  /* Asset file descriptor class and functions.  */
+  jclass fd_class;
+  jmethodID get_length;
+  jmethodID create_input_stream;
+  jmethodID close;
+
+  /* Input stream class and functions.  */
+  jclass input_stream_class;
+  jmethodID read;
+  jmethodID stream_close;
+
+  /* Associated asset manager object.  */
+  jobject asset_manager;
+};
+
+typedef struct android_asset_manager AAssetManager;
+
+struct android_asset
+{
+  /* The asset manager.  */
+  AAssetManager *manager;
+
+  /* The length of the asset, or -1.  */
+  jlong length;
+
+  /* The asset file descriptor and input stream.  */
+  jobject fd, stream;
+
+  /* The mode.  */
+  int mode;
+};
+
+typedef struct android_asset AAsset;
+
+static AAssetManager *
+AAssetManager_fromJava (JNIEnv *env, jobject java_manager)
+{
+  AAssetManager *manager;
+  jclass temp;
+
+  manager = malloc (sizeof *manager);
+
+  if (!manager)
+    return NULL;
+
+  manager->env = env;
+  manager->asset_manager
+    = (*env)->NewGlobalRef (env, java_manager);
+
+  if (!manager->asset_manager)
+    {
+      free (manager);
+      return NULL;
+    }
+
+  manager->class
+    = (*env)->FindClass (env, "android/content/res/AssetManager");
+  assert (manager->class);
+
+  manager->open_fd
+    = (*env)->GetMethodID (env, manager->class, "openFd",
+                          "(Ljava/lang/String;)"
+                          "Landroid/content/res/AssetFileDescriptor;");
+  assert (manager->open);
+
+  manager->fd_class
+    = (*env)->FindClass (env, "android/content/res/AssetFileDescriptor");
+  assert (manager->fd_class);
+
+  manager->get_length
+    = (*env)->GetMethodID (env, manager->fd_class, "getLength",
+                          "()J");
+  assert (manager->get_length);
+
+  manager->create_input_stream
+    = (*env)->GetMethodID (env, manager->fd_class,
+                          "createInputStream",
+                          "()Ljava/io/FileInputStream;");
+  assert (manager->create_input_stream);
+
+  manager->close
+    = (*env)->GetMethodID (env, manager->fd_class,
+                          "close", "()V");
+  assert (manager->close);
+
+  manager->input_stream_class
+    = (*env)->FindClass (env, "java/io/InputStream");
+  assert (manager->input_stream_class);
+
+  manager->read
+    = (*env)->GetMethodID (env, manager->input_stream_class,
+                          "read", "([B)I");
+  assert (manager->read);
+
+  manager->stream_close
+    = (*env)->GetMethodID (env, manager->input_stream_class,
+                          "close", "()V");
+  assert (manager->stream_close);
+
+  /* Now convert all the class references to global ones.  */
+  temp = manager->class;
+  manager->class
+    = (*env)->NewGlobalRef (env, temp);
+  assert (manager->class);
+  (*env)->DeleteLocalRef (env, temp);
+  temp = manager->fd_class;
+  manager->fd_class
+    = (*env)->NewGlobalRef (env, temp);
+  assert (manager->fd_class);
+  (*env)->DeleteLocalRef (env, temp);
+  temp = manager->input_stream_class;
+  manager->input_stream_class
+    = (*env)->NewGlobalRef (env, temp);
+  assert (manager->input_stream_class);
+  (*env)->DeleteLocalRef (env, temp);
+
+  /* Return the asset manager.  */
+  return manager;
+}
+
+enum
+  {
+    AASSET_MODE_STREAMING = 0,
+    AASSET_MODE_BUFFER   = 1,
+  };
+
+static AAsset *
+AAssetManager_open (AAssetManager *manager, const char *c_name,
+                   int mode)
+{
+  jobject desc;
+  jstring name;
+  AAsset *asset;
+
+  /* Push a local frame.  */
+  asset = NULL;
+
+  (*(manager->env))->PushLocalFrame (manager->env, 3);
+
+  if ((*(manager->env))->ExceptionCheck (manager->env))
+    goto fail;
+
+  /* Encoding issues can be ignored for now as there are only ASCII
+     file names in Emacs.  */
+  name = (*(manager->env))->NewStringUTF (manager->env, c_name);
+
+  if (!name)
+    goto fail;
+
+  /* Now try to open an ``AssetFileDescriptor''.  */
+  desc = (*(manager->env))->CallObjectMethod (manager->env,
+                                             manager->asset_manager,
+                                             manager->open_fd,
+                                             name);
+
+  if (!desc)
+    goto fail;
+
+  /* Allocate the asset.  */
+  asset = calloc (1, sizeof *asset);
+
+  if (!asset)
+    {
+      (*(manager->env))->CallVoidMethod (manager->env,
+                                        desc,
+                                        manager->close);
+      goto fail;
+    }
+
+  /* Pop the local frame and return desc.  */
+  desc = (*(manager->env))->NewGlobalRef (manager->env, desc);
+
+  if (!desc)
+    goto fail;
+
+  (*(manager->env))->PopLocalFrame (manager->env, NULL);
+
+  asset->manager = manager;
+  asset->length = -1;
+  asset->fd = desc;
+  asset->mode = mode;
+
+  return asset;
+
+ fail:
+  (*(manager->env))->ExceptionClear (manager->env);
+  (*(manager->env))->PopLocalFrame (manager->env, NULL);
+  free (asset);
+
+  return NULL;
+}
+
+static AAsset *
+AAsset_close (AAsset *asset)
+{
+  JNIEnv *env;
+
+  env = asset->manager->env;
+
+  (*env)->CallVoidMethod (asset->manager->env,
+                         asset->fd,
+                         asset->manager->close);
+  (*env)->DeleteGlobalRef (asset->manager->env,
+                          asset->fd);
+
+  if (asset->stream)
+    {
+      (*env)->CallVoidMethod (asset->manager->env,
+                             asset->stream,
+                             asset->manager->stream_close);
+      (*env)->DeleteGlobalRef (asset->manager->env,
+                              asset->stream);
+    }
+
+  free (asset);
+}
+
+/* Create an input stream associated with the given ASSET.  Set
+   ASSET->stream to its global reference.
+
+   Value is 1 upon failure, else 0.  ASSET must not already have an
+   input stream.  */
+
+static int
+android_asset_create_stream (AAsset *asset)
+{
+  jobject stream;
+  JNIEnv *env;
+
+  env = asset->manager->env;
+  stream
+    = (*env)->CallObjectMethod (env, asset->fd,
+                               asset->manager->create_input_stream);
+
+  if (!stream)
+    {
+      (*env)->ExceptionClear (env);
+      return 1;
+    }
+
+  asset->stream
+    = (*env)->NewGlobalRef (env, stream);
+
+  if (!asset->stream)
+    {
+      (*env)->ExceptionClear (env);
+      (*env)->DeleteLocalRef (env, stream);
+      return 1;
+    }
+
+  (*env)->DeleteLocalRef (env, stream);
+  return 0;
+}
+
+/* Read NBYTES from the specified asset into the given BUFFER;
+
+   Internally, allocate a Java byte array containing 4096 elements and
+   copy the data to and from that array.
+
+   Value is the number of bytes actually read, 0 at EOF, or -1 upon
+   failure, in which case errno is set accordingly.  If NBYTES is
+   zero, behavior is undefined.  */
+
+static int
+android_asset_read_internal (AAsset *asset, int nbytes, char *buffer)
+{
+  jbyteArray stash;
+  JNIEnv *env;
+  jint bytes_read, total;
+
+  /* Allocate a suitable amount of storage.  Either nbytes or 4096,
+     whichever is larger.  */
+  env = asset->manager->env;
+  stash = (*env)->NewByteArray (env, MIN (nbytes, 4096));
+
+  if (!stash)
+    {
+      (*env)->ExceptionClear (env);
+      errno = ENOMEM;
+      return -1;
+    }
+
+  /* Try to create an input stream.  */
+
+  if (!asset->stream
+      && android_asset_create_stream (asset))
+    {
+      (*env)->DeleteLocalRef (env, stash);
+      errno = ENOMEM;
+      return -1;
+    }
+
+  /* Start reading.  */
+
+  total = 0;
+
+  while (nbytes)
+    {
+      bytes_read = (*env)->CallIntMethod (env, asset->stream,
+                                         asset->manager->read,
+                                         stash);
+
+      /* Detect error conditions.  */
+
+      if ((*env)->ExceptionCheck (env))
+       goto out;
+
+      /* Detect EOF.  */
+
+      if (bytes_read == -1)
+       goto out;
+
+      /* Finally write out the amount that was read.  */
+      bytes_read = MIN (bytes_read, nbytes);
+      (*env)->GetByteArrayRegion (env, stash, 0, bytes_read, buffer);
+
+      buffer += bytes_read;
+      total += bytes_read;
+      nbytes -= bytes_read;
+    }
+
+  /* Make sure the value of nbytes still makes sense.  */
+  assert (nbytes >= 0);
+
+ out:
+  (*env)->ExceptionClear (env);
+  (*env)->DeleteLocalRef (env, stash);
+  return total;
+}
+
+static long
+AAsset_getLength (AAsset *asset)
+{
+  JNIEnv *env;
+
+  if (asset->length != -1)
+    return asset->length;
+
+  env = asset->manager->env;
+  asset->length
+    = (*env)->CallLongMethod (env, asset->fd,
+                             asset->manager->get_length);
+  return asset->length;
+}
+
+static char *
+AAsset_getBuffer (AAsset *asset)
+{
+  long length;
+  char *buffer;
+
+  length = AAsset_getLength (asset);
+
+  if (!length)
+    return NULL;
+
+  buffer = malloc (length);
+
+  if (!buffer)
+    return NULL;
+
+  if (android_asset_read_internal (asset, length, buffer)
+      != length)
+    {
+      xfree (buffer);
+      return NULL;
+    }
+
+  return buffer;
+}
+
+static size_t
+AAsset_read (AAsset *asset, void *buffer, size_t size)
+{
+  return android_asset_read_internal (asset, MIN (size, INT_MAX),
+                                     buffer);
+}
+
+static off_t
+AAsset_seek (AAsset *asset, off_t offset, int whence)
+{
+  /* Java InputStreams don't support seeking at all.  */
+  errno = ESPIPE;
+  return -1;
+}
diff --git a/src/android-emacs.c b/src/android-emacs.c
new file mode 100644
index 00000000000..e64caf9a9d4
--- /dev/null
+++ b/src/android-emacs.c
@@ -0,0 +1,169 @@
+/* Android initialization for GNU Emacs.
+
+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 <stdio.h>
+#include <alloca.h>
+#include <string.h>
+#include <unistd.h>
+
+/* android-emacs is a wrapper around /system/bin/app_process(64).
+   It invokes app_process(64) with the right class path and then
+   starts org.gnu.emacs.EmacsNoninteractive.
+
+   The main function in that class tries to load an activity thread
+   and obtain a context and asset manager before calling
+   android_emacs_init, which is required for Emacs to find required
+   preloaded Lisp.  */
+
+int
+main (int argc, char **argv)
+{
+  char **args;
+  int i;
+  char *bootclasspath, *emacs_class_path;
+
+  /* Allocate enough to hold the arguments to app_process.  */
+  args = alloca ((10 + argc) * sizeof *args);
+
+  /* Clear args.  */
+  memset (args, 0, (10 + argc) * sizeof *args);
+
+  /* First, figure out what program to start.  */
+#if defined __x86_64__ || defined __aarch64__
+  args[0] = (char *) "/system/bin/app_process64";
+#else
+  args[0] = (char *) "/system/bin/app_process";
+#endif
+
+  /* Machines with ART require the boot classpath to be manually
+     specified.  Machines with Dalvik however refuse to do so, as they
+     open the jars inside the BOOTCLASSPATH environment variable at
+     startup, resulting in the following crash:
+
+     W/dalvikvm( 1608): Refusing to reopen boot DEX
+     '/system/framework/core.jar'
+     W/dalvikvm( 1608): Refusing to reopen boot DEX
+     '/system/framework/bouncycastle.jar'
+     E/dalvikvm( 1608): Too many exceptions during init (failed on
+     'Ljava/io/IOException;' 'Re-opening BOOTCLASSPATH DEX files is
+     not allowed')
+     E/dalvikvm( 1608): VM aborting  */
+
+#if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL
+  if (android_get_device_api_level () < 21)
+    {
+      bootclasspath = NULL;
+      goto skip_setup;
+    }
+#else
+  if (__ANDROID_API__ < 21)
+    {
+      bootclasspath = NULL;
+      goto skip_setup;
+    }
+#endif
+
+  /* Next, obtain the boot class path.  */
+  bootclasspath = getenv ("BOOTCLASSPATH");
+
+  if (!bootclasspath)
+    {
+      fprintf (stderr, "The BOOTCLASSPATH environment variable"
+              " is not set.  As a result, Emacs does not know"
+              " how to start app_process.\n"
+              "This is likely a change in the Android platform."
+              "  Please report this to bug-gnu-emacs@gnu.org.\n");
+      return 1;
+    }
+
+ skip_setup:
+
+  /* And the Emacs class path.  */
+  emacs_class_path = getenv ("EMACS_CLASS_PATH");
+
+  if (!emacs_class_path)
+    {
+      fprintf (stderr, "EMACS_CLASS_PATH not set."
+              "  Please make sure Emacs is being started"
+              " from within a running copy of Emacs.\n");
+      return 1;
+    }
+
+  if (bootclasspath)
+    {
+      if (asprintf (&bootclasspath, "-Djava.class.path=%s:%s",
+                   bootclasspath, emacs_class_path) < 0)
+       {
+         perror ("asprintf");
+         return 1;
+       }
+    }
+  else
+    {
+      if (asprintf (&bootclasspath, "-Djava.class.path=%s",
+                   emacs_class_path) < 0)
+       {
+         perror ("asprintf");
+         return 1;
+       }
+    }
+
+  args[1] = bootclasspath;
+  args[2] = (char *) "/system/bin";
+
+#if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL
+  /* I don't know exactly when --nice-name was introduced; this is
+     just a guess.  */
+  if (android_get_device_api_level () >= 26)
+    {
+      args[3] = (char *) "--nice-name=emacs";
+      args[4] = (char *) "org.gnu.emacs.EmacsNoninteractive";
+
+      /* Arguments from here on are passed to main in
+        EmacsNoninteractive.java.  */
+      args[5] = argv[0];
+
+      /* Now copy the rest of the arguments over.  */
+      for (i = 1; i < argc; ++i)
+       args[5 + i] = argv[i];
+    }
+  else
+    {
+#endif
+      args[3] = (char *) "org.gnu.emacs.EmacsNoninteractive";
+
+      /* Arguments from here on are passed to main in
+        EmacsNoninteractive.java.  */
+      args[4] = argv[0];
+
+      /* Now copy the rest of the arguments over.  */
+      for (i = 1; i < argc; ++i)
+       args[4 + i] = argv[i];
+#if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL
+    }
+#endif
+
+  /* Finally, try to start the app_process.  */
+  execvp (args[0], args);
+
+  /* If exit fails, return an error indication.  */
+  perror ("exec");
+  return 1;
+}
diff --git a/src/android.c b/src/android.c
new file mode 100644
index 00000000000..bd19107f53a
--- /dev/null
+++ b/src/android.c
@@ -0,0 +1,6933 @@
+/* Android initialization for GNU Emacs.
+
+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 <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <limits.h>
+#include <signal.h>
+#include <semaphore.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <math.h>
+#include <string.h>
+#include <stdckdint.h>
+#include <intprops.h>
+#include <timespec.h>
+#include <libgen.h>
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+
+/* Old NDK versions lack MIN and MAX.  */
+#include <minmax.h>
+
+#include <assert.h>
+#include <fingerprint.h>
+
+#include "android.h"
+#include "androidgui.h"
+
+#include "lisp.h"
+#include "blockinput.h"
+#include "coding.h"
+#include "epaths.h"
+
+/* Whether or not Emacs is running inside the application process and
+   Android windowing should be enabled.  */
+bool android_init_gui;
+
+#ifndef ANDROID_STUBIFY
+
+#include <android/bitmap.h>
+#include <android/log.h>
+
+#include <linux/unistd.h>
+
+#include <sys/syscall.h>
+
+#ifdef __aarch64__
+#include <arm_neon.h>
+#endif /* __aarch64__ */
+
+struct android_emacs_pixmap
+{
+  jclass class;
+  jmethodID constructor;
+  jmethodID constructor_mutable;
+};
+
+struct android_graphics_point
+{
+  jclass class;
+  jmethodID constructor;
+};
+
+struct android_emacs_drawable
+{
+  jclass class;
+  jmethodID get_bitmap;
+  jmethodID damage_rect;
+};
+
+struct android_emacs_window
+{
+  jclass class;
+  jmethodID swap_buffers;
+  jmethodID toggle_on_screen_keyboard;
+  jmethodID lookup_string;
+  jmethodID set_fullscreen;
+  jmethodID change_window_background;
+  jmethodID reparent_to;
+  jmethodID map_window;
+  jmethodID unmap_window;
+  jmethodID resize_window;
+  jmethodID move_window;
+  jmethodID make_input_focus;
+  jmethodID raise;
+  jmethodID lower;
+  jmethodID get_window_geometry;
+  jmethodID translate_coordinates;
+  jmethodID set_dont_accept_focus;
+  jmethodID set_dont_focus_on_map;
+  jmethodID define_cursor;
+};
+
+struct android_emacs_cursor
+{
+  jclass class;
+  jmethodID constructor;
+};
+
+/* The API level of the current device.  */
+static int android_api_level;
+
+/* The directory used to store site-lisp.  */
+char *android_site_load_path;
+
+/* The directory used to store native libraries.  */
+char *android_lib_dir;
+
+/* The directory used to store game files.  */
+char *android_game_path;
+
+/* The directory used to store temporary files.  */
+char *android_cache_dir;
+
+/* The list of archive files within which the Java virtual macine
+   looks for class files.  */
+char *android_class_path;
+
+/* The display's pixel densities.  */
+double android_pixel_density_x, android_pixel_density_y;
+
+/* The display pixel density used to convert between point and pixel
+   font sizes.  */
+double android_scaled_pixel_density;
+
+/* The Android application data directory.  */
+static char *android_files_dir;
+
+/* The Java environment being used for the main thread.  */
+JNIEnv *android_java_env;
+
+/* The EmacsGC class.  */
+static jclass emacs_gc_class;
+
+/* Various fields.  */
+static jfieldID emacs_gc_foreground, emacs_gc_background;
+static jfieldID emacs_gc_function, emacs_gc_clip_rects;
+static jfieldID emacs_gc_clip_x_origin, emacs_gc_clip_y_origin;
+static jfieldID emacs_gc_stipple, emacs_gc_clip_mask;
+static jfieldID emacs_gc_fill_style, emacs_gc_ts_origin_x;
+static jfieldID emacs_gc_ts_origin_y;
+
+/* The constructor and one function.  */
+static jmethodID emacs_gc_constructor, emacs_gc_mark_dirty;
+
+/* The Rect class.  */
+static jclass android_rect_class;
+
+/* Its constructor.  */
+static jmethodID android_rect_constructor;
+
+/* The EmacsService object.  */
+jobject emacs_service;
+
+/* Various methods associated with the EmacsService.  */
+struct android_emacs_service service_class;
+
+/* Various methods associated with the EmacsPixmap class.  */
+static struct android_emacs_pixmap pixmap_class;
+
+/* Various methods associated with the Point class.  */
+static struct android_graphics_point point_class;
+
+/* Various methods associated with the EmacsDrawable class.  */
+static struct android_emacs_drawable drawable_class;
+
+/* Various methods associated with the EmacsWindow class.  */
+static struct android_emacs_window window_class;
+
+/* Various methods associated with the EmacsCursor class.  */
+static struct android_emacs_cursor cursor_class;
+
+/* The last event serial used.  This is a 32 bit value, but it is
+   stored in unsigned long to be consistent with X.  */
+unsigned int event_serial;
+
+#ifdef __i386__
+
+/* Unused pointer used to control compiler optimizations.  */
+void *unused_pointer;
+
+#endif /* __i386__ */
+
+/* Whether or not the default signal mask has been changed.  If so,
+   the signal mask must be restored before calling
+   android_emacs_init.  */
+static bool signal_mask_changed_p;
+
+/* The signal mask at the time Emacs was started.  */
+static sigset_t startup_signal_mask;
+
+
+
+/* Event handling functions.  Events are stored on a (circular) queue
+   that is read synchronously.  The Android port replaces pselect with
+   a function android_select, which runs pselect in a separate thread,
+   but more importantly also waits for events to be available on the
+   android event queue.  */
+
+struct android_event_container
+{
+  /* The next and last events in this queue.  */
+  struct android_event_container *next, *last;
+
+  /* The event itself.  */
+  union android_event event;
+};
+
+struct android_event_queue
+{
+  /* Mutex protecting the event queue.  */
+  pthread_mutex_t mutex;
+
+  /* Mutex protecting the select data.  */
+  pthread_mutex_t select_mutex;
+
+  /* The thread used to run select.  */
+  pthread_t select_thread;
+
+  /* Condition variables for the reading side.  */
+  pthread_cond_t read_var;
+
+  /* The number of events in the queue.  If this is greater than 1024,
+     writing will block.  */
+  int num_events;
+
+  /* Circular queue of events.  */
+  struct android_event_container events;
+};
+
+/* Arguments to pselect used by the select thread.  */
+static int android_pselect_nfds;
+static fd_set *android_pselect_readfds;
+static fd_set *android_pselect_writefds;
+static fd_set *android_pselect_exceptfds;
+static struct timespec *android_pselect_timeout;
+
+/* Value of pselect.  */
+static int android_pselect_rc;
+
+/* The global event queue.  */
+static struct android_event_queue event_queue;
+
+/* Semaphores used to signal select completion and start.  */
+static sem_t android_pselect_sem, android_pselect_start_sem;
+
+#if __ANDROID_API__ < 16
+
+/* Select self-pipe.  */
+static int select_pipe[2];
+
+#else
+
+/* Whether or not pselect has been interrupted.  */
+static volatile sig_atomic_t android_pselect_interrupted;
+
+#endif
+
+static void *
+android_run_select_thread (void *data)
+{
+  /* Apparently this is required too.  */
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  int rc;
+#if __ANDROID_API__ < 16
+  int nfds;
+  fd_set readfds;
+  char byte;
+#else
+  sigset_t signals, waitset;
+  int sig;
+#endif
+
+#if __ANDROID_API__ < 16
+  /* A completely different implementation is used when building for
+     Android versions earlier than 16, because pselect with a signal
+     mask does not work there.  Instead of blocking SIGUSR1 and
+     unblocking it inside pselect, a file descriptor is used instead.
+     Something is written to the file descriptor every time select is
+     supposed to return.  */
+
+  while (true)
+    {
+      /* Wait for the thread to be released.  */
+      while (sem_wait (&android_pselect_start_sem) < 0)
+       ;;
+
+      /* Get the select lock and call pselect.  API 8 does not have
+        working pselect in any sense.  Instead, pselect wakes up on
+        select_pipe[0].  */
+
+      pthread_mutex_lock (&event_queue.select_mutex);
+      nfds = android_pselect_nfds;
+
+      if (android_pselect_readfds)
+       readfds = *android_pselect_readfds;
+      else
+       FD_ZERO (&readfds);
+
+      if (nfds < select_pipe[0] + 1)
+       nfds = select_pipe[0] + 1;
+      FD_SET (select_pipe[0], &readfds);
+
+      rc = pselect (nfds, &readfds,
+                   android_pselect_writefds,
+                   android_pselect_exceptfds,
+                   android_pselect_timeout,
+                   NULL);
+
+      /* Subtract 1 from rc if readfds contains the select pipe, and
+        also remove it from that set.  */
+
+      if (rc != -1 && FD_ISSET (select_pipe[0], &readfds))
+       {
+         rc -= 1;
+         FD_CLR (select_pipe[0], &readfds);
+
+         /* If no file descriptors aside from the select pipe are
+            ready, then pretend that an error has occurred.  */
+         if (!rc)
+           rc = -1;
+       }
+
+      /* Save the read file descriptor set back again.  */
+
+      if (android_pselect_readfds)
+       *android_pselect_readfds = readfds;
+
+      android_pselect_rc = rc;
+      pthread_mutex_unlock (&event_queue.select_mutex);
+
+      /* Signal the main thread that there is now data to read.  Hold
+         the event queue lock during this process to make sure this
+         does not happen before the main thread begins to wait for the
+         condition variable.  */
+
+      pthread_mutex_lock (&event_queue.mutex);
+      pthread_cond_broadcast (&event_queue.read_var);
+      pthread_mutex_unlock (&event_queue.mutex);
+
+      /* Read a single byte from the select pipe.  */
+      read (select_pipe[0], &byte, 1);
+
+      /* Signal the Emacs thread that pselect is done.  If read_var
+        was signaled by android_write_event, event_queue.mutex could
+        still be locked, so this must come before.  */
+      sem_post (&android_pselect_sem);
+    }
+#else
+  if (pthread_sigmask (SIG_BLOCK, &signals, NULL))
+    __android_log_print (ANDROID_LOG_FATAL, __func__,
+                        "pthread_sigmask: %s",
+                        strerror (errno));
+
+  sigfillset (&signals);
+  sigdelset (&signals, SIGUSR1);
+  sigemptyset (&waitset);
+  sigaddset (&waitset, SIGUSR1);
+
+  while (true)
+    {
+      /* Wait for the thread to be released.  */
+      while (sem_wait (&android_pselect_start_sem) < 0)
+       ;;
+
+      /* Clear the ``pselect interrupted'' flag.  This is safe because
+        right now, SIGUSR1 is blocked.  */
+      android_pselect_interrupted = 0;
+
+      /* Get the select lock and call pselect.  */
+      pthread_mutex_lock (&event_queue.select_mutex);
+      rc = pselect (android_pselect_nfds,
+                   android_pselect_readfds,
+                   android_pselect_writefds,
+                   android_pselect_exceptfds,
+                   android_pselect_timeout,
+                   &signals);
+      android_pselect_rc = rc;
+      pthread_mutex_unlock (&event_queue.select_mutex);
+
+      /* Signal the main thread that there is now data to read.  Hold
+         the event queue lock during this process to make sure this
+         does not happen before the main thread begins to wait for the
+         condition variable.  */
+
+      pthread_mutex_lock (&event_queue.mutex);
+      pthread_cond_broadcast (&event_queue.read_var);
+      pthread_mutex_unlock (&event_queue.mutex);
+
+      /* Check `android_pselect_interrupted' instead of rc and errno.
+
+         This is because `pselect' does not return an rc of -1 upon
+         being interrupted in some versions of Android, but does set
+         signal masks correctly.  */
+
+      if (!android_pselect_interrupted)
+       /* Now, wait for SIGUSR1, unless pselect was interrupted and
+          the signal was already delivered.  The Emacs thread will
+          always send this signal after read_var is triggered or the
+          UI thread has sent an event.  */
+       sigwait (&waitset, &sig);
+
+      /* Signal the Emacs thread that pselect is done.  If read_var
+        was signaled by android_write_event, event_queue.mutex could
+        still be locked, so this must come before.  */
+      sem_post (&android_pselect_sem);
+    }
+#endif
+
+  return NULL;
+}
+
+#if __ANDROID_API__ >= 16
+
+static void
+android_handle_sigusr1 (int sig, siginfo_t *siginfo, void *arg)
+{
+  /* Notice that pselect has been interrupted.  */
+  android_pselect_interrupted = 1;
+}
+
+#endif
+
+/* Semaphore used to indicate completion of a query.
+   This should ideally be defined further down.  */
+static sem_t android_query_sem;
+
+/* Set up the global event queue by initializing the mutex and two
+   condition variables, and the linked list of events.  This must be
+   called before starting the Emacs thread.  Also, initialize the
+   thread used to run pselect.
+
+   These functions must also use the C library malloc and free,
+   because xmalloc is not thread safe.  */
+
+static void
+android_init_events (void)
+{
+  struct sigaction sa;
+
+  if (pthread_mutex_init (&event_queue.mutex, NULL))
+    __android_log_print (ANDROID_LOG_FATAL, __func__,
+                        "pthread_mutex_init: %s",
+                        strerror (errno));
+
+  if (pthread_mutex_init (&event_queue.select_mutex, NULL))
+    __android_log_print (ANDROID_LOG_FATAL, __func__,
+                        "pthread_mutex_init: %s",
+                        strerror (errno));
+
+  if (pthread_cond_init (&event_queue.read_var, NULL))
+    __android_log_print (ANDROID_LOG_FATAL, __func__,
+                        "pthread_cond_init: %s",
+                        strerror (errno));
+
+  sem_init (&android_pselect_sem, 0, 0);
+  sem_init (&android_pselect_start_sem, 0, 0);
+  sem_init (&android_query_sem, 0, 0);
+
+  event_queue.events.next = &event_queue.events;
+  event_queue.events.last = &event_queue.events;
+
+#if __ANDROID_API__ >= 16
+
+  /* Before starting the select thread, make sure the disposition for
+     SIGUSR1 is correct.  */
+  sigfillset (&sa.sa_mask);
+  sa.sa_sigaction = android_handle_sigusr1;
+  sa.sa_flags = SA_SIGINFO;
+
+#else
+
+  /* Set up the file descriptor used to wake up pselect.  */
+  if (pipe2 (select_pipe, O_CLOEXEC) < 0)
+    __android_log_print (ANDROID_LOG_FATAL, __func__,
+                        "pipe2: %s", strerror (errno));
+
+  /* Make sure the read end will fit in fd_set.  */
+  if (select_pipe[0] >= FD_SETSIZE)
+    __android_log_print (ANDROID_LOG_FATAL, __func__,
+                        "read end of select pipe"
+                        " lies outside FD_SETSIZE!");
+
+#endif
+
+  if (sigaction (SIGUSR1, &sa, NULL))
+    __android_log_print (ANDROID_LOG_FATAL, __func__,
+                        "sigaction: %s",
+                        strerror (errno));
+
+  /* Start the select thread.  */
+  if (pthread_create (&event_queue.select_thread, NULL,
+                     android_run_select_thread, NULL))
+    __android_log_print (ANDROID_LOG_FATAL, __func__,
+                        "pthread_create: %s",
+                        strerror (errno));
+}
+
+int
+android_pending (void)
+{
+  int i;
+
+  pthread_mutex_lock (&event_queue.mutex);
+  i = event_queue.num_events;
+  pthread_mutex_unlock (&event_queue.mutex);
+
+  return i;
+}
+
+/* Forward declaration.  */
+
+static void android_check_query (void);
+
+/* Wait for events to become available synchronously.  Return once an
+   event arrives.  Also, reply to the UI thread whenever it requires a
+   response.  */
+
+void
+android_wait_event (void)
+{
+  /* Run queries from the UI thread to the Emacs thread.  */
+  android_check_query ();
+
+  pthread_mutex_lock (&event_queue.mutex);
+
+  /* Wait for events to appear if there are none available to
+     read.  */
+  if (!event_queue.num_events)
+    pthread_cond_wait (&event_queue.read_var,
+                      &event_queue.mutex);
+
+  pthread_mutex_unlock (&event_queue.mutex);
+
+  /* Check for queries again.  If a query is sent after the call to
+     `android_check_query' above, `read_var' will be signaled.  */
+  android_check_query ();
+}
+
+void
+android_next_event (union android_event *event_return)
+{
+  struct android_event_container *container;
+
+  pthread_mutex_lock (&event_queue.mutex);
+
+  /* Wait for events to appear if there are none available to
+     read.  */
+  if (!event_queue.num_events)
+    pthread_cond_wait (&event_queue.read_var,
+                      &event_queue.mutex);
+
+  /* Obtain the event from the end of the queue.  */
+  container = event_queue.events.last;
+  eassert (container != &event_queue.events);
+
+  /* Remove the event from the queue and copy it to the caller
+     supplied buffer.  */
+  container->last->next = container->next;
+  container->next->last = container->last;
+  *event_return = container->event;
+  event_queue.num_events--;
+
+  /* Free the container.  */
+  free (container);
+
+  /* Unlock the queue.  */
+  pthread_mutex_unlock (&event_queue.mutex);
+}
+
+bool
+android_check_if_event (union android_event *event_return,
+                       bool (*predicate) (union android_event *,
+                                          void *),
+                       void *arg)
+{
+  struct android_event_container *container;
+
+  pthread_mutex_lock (&event_queue.mutex);
+
+  /* Loop over each event.  */
+  container = event_queue.events.last;
+  for (; container != &event_queue.events; container = container->last)
+    {
+      /* See if the predicate matches.  */
+      if ((*predicate) (&container->event, arg))
+       {
+         /* Copy out the event and return true.  */
+         *event_return = container->event;
+         --event_queue.num_events;
+
+         /* Unlink container.  */
+         container->last->next = container->next;
+         container->next->last = container->last;
+         free (container);
+         pthread_mutex_unlock (&event_queue.mutex);
+         return true;
+       }
+    }
+
+  pthread_mutex_unlock (&event_queue.mutex);
+  return false;
+}
+
+void
+android_write_event (union android_event *event)
+{
+  struct android_event_container *container;
+
+  container = malloc (sizeof *container);
+
+  if (!container)
+    return;
+
+  /* If the event queue hasn't been initialized yet, return false.  */
+  if (!event_queue.events.next)
+    return;
+
+  pthread_mutex_lock (&event_queue.mutex);
+  container->next = event_queue.events.next;
+  container->last = &event_queue.events;
+  container->next->last = container;
+  container->last->next = container;
+  container->event = *event;
+  event_queue.num_events++;
+  pthread_cond_broadcast (&event_queue.read_var);
+  pthread_mutex_unlock (&event_queue.mutex);
+
+  /* Now set pending_signals to true, and raise SIGIO to interrupt any
+     ongoing reads if the event is important.  */
+  pending_signals = true;
+
+  switch (event->type)
+    {
+      /* Key press and window action events are considered important,
+        as they either end up quitting or asking for responses to the
+        IME.  */
+    case ANDROID_KEY_PRESS:
+    case ANDROID_WINDOW_ACTION:
+      kill (getpid (), SIGIO);
+      break;
+
+    default:
+      break;
+    }
+}
+
+
+
+/* Whether or not the UI thread has been waiting for a significant
+   amount of time for a function to run in the main thread, and Emacs
+   should answer the query ASAP.  */
+static bool android_urgent_query;
+
+int
+android_select (int nfds, fd_set *readfds, fd_set *writefds,
+               fd_set *exceptfds, struct timespec *timeout)
+{
+  int nfds_return;
+#if __ANDROID_API__ < 16
+  static char byte;
+#endif
+
+  /* Since Emacs is reading keyboard input again, signify that queries
+     from input methods are no longer ``urgent''.  */
+
+  __atomic_clear (&android_urgent_query, __ATOMIC_RELEASE);
+
+  /* Check for and run anything the UI thread wants to run on the main
+     thread.  */
+  android_check_query ();
+
+  pthread_mutex_lock (&event_queue.mutex);
+
+  if (event_queue.num_events)
+    {
+      pthread_mutex_unlock (&event_queue.mutex);
+      return 1;
+    }
+
+  nfds_return = 0;
+
+  pthread_mutex_lock (&event_queue.select_mutex);
+  android_pselect_nfds = nfds;
+  android_pselect_readfds = readfds;
+  android_pselect_writefds = writefds;
+  android_pselect_exceptfds = exceptfds;
+  android_pselect_timeout = timeout;
+  pthread_mutex_unlock (&event_queue.select_mutex);
+
+  /* Release the select thread.  */
+  sem_post (&android_pselect_start_sem);
+
+  /* Start waiting for the event queue condition to be set.  */
+  pthread_cond_wait (&event_queue.read_var, &event_queue.mutex);
+
+#if __ANDROID_API__ >= 16
+  /* Interrupt the select thread now, in case it's still in
+     pselect.  */
+  pthread_kill (event_queue.select_thread, SIGUSR1);
+#else
+  /* Interrupt the select thread by writing to the select pipe.  */
+  if (write (select_pipe[1], &byte, 1) != 1)
+    __android_log_print (ANDROID_LOG_FATAL, __func__,
+                        "write: %s", strerror (errno));
+#endif
+
+  /* Unlock the event queue mutex.  */
+  pthread_mutex_unlock (&event_queue.mutex);
+
+  /* Wait for pselect to return in any case.  This must be done with
+     the event queue mutex unlocked.  Otherwise, the pselect thread
+     can hang if it tries to lock the event queue mutex to signal
+     read_var after the UI thread has already done so.  */
+  while (sem_wait (&android_pselect_sem) < 0)
+    ;;
+
+  /* If there are now events in the queue, return 1.  */
+
+  pthread_mutex_lock (&event_queue.mutex);
+  if (event_queue.num_events)
+    nfds_return = 1;
+  pthread_mutex_unlock (&event_queue.mutex);
+
+  /* Add the return value of pselect if it has also found ready file
+     descriptors.  */
+
+  if (android_pselect_rc >= 0)
+    nfds_return += android_pselect_rc;
+  else if (!nfds_return)
+    /* If pselect was interrupted and nfds_return is 0 (meaning that
+       no events have been read), indicate that an error has taken
+       place.  */
+    nfds_return = android_pselect_rc;
+
+  if ((android_pselect_rc < 0) && nfds_return >= 0)
+    {
+      /* Clear the file descriptor sets if events will be delivered
+        but no file descriptors have become ready to prevent the
+        caller from misinterpreting a non-zero return value.  */
+
+      if (readfds)
+       FD_ZERO (readfds);
+
+      if (writefds)
+       FD_ZERO (writefds);
+
+      if (exceptfds)
+       FD_ZERO (exceptfds);
+    }
+
+  /* This is to shut up process.c when pselect gets EINTR.  */
+  if (nfds_return < 0)
+    errno = EINTR;
+
+  /* Now check for and run anything the UI thread wants to run in the
+     main thread.  */
+  android_check_query ();
+
+  return nfds_return;
+}
+
+
+
+static void *
+android_run_debug_thread (void *data)
+{
+  FILE *file;
+  int fd;
+  char *line;
+  size_t n;
+
+  fd = (int) (intptr_t) data;
+  file = fdopen (fd, "r");
+
+  if (!file)
+    return NULL;
+
+  line = NULL;
+
+  while (true)
+    {
+      if (getline (&line, &n, file) < 0)
+       {
+         free (line);
+         break;
+       }
+
+      __android_log_print (ANDROID_LOG_INFO, __func__, "%s", line);
+    }
+
+  fclose (file);
+  return NULL;
+}
+
+
+
+/* Intercept USER_FULL_NAME and return something that makes sense if
+   pw->pw_gecos is NULL.  */
+
+char *
+android_user_full_name (struct passwd *pw)
+{
+#ifdef HAVE_STRUCT_PASSWD_PW_GECOS
+  if (!pw->pw_gecos)
+    return (char *) "Android user";
+
+  return pw->pw_gecos;
+#else
+  return "Android user";
+#endif
+}
+
+
+
+/* Determine whether or not the specified file NAME describes a file
+   in the directory DIR, which should be an absolute file name.  NAME
+   must be in canonical form.
+
+   Value is NULL if not.  Otherwise, it is a pointer to the first
+   character in NAME after the part containing DIR and its trailing
+   directory separator.  */
+
+const char *
+android_is_special_directory (const char *name, const char *dir)
+{
+  size_t len;
+
+  /* Compare up to strlen (DIR) bytes of NAME with DIR.  */
+
+  len = strlen (dir);
+  if (strncmp (name, dir, len))
+    return NULL;
+
+  /* Now see if the character of NAME after len is either a directory
+     separator or a terminating NULL.  */
+
+  name += len;
+  switch (*name)
+    {
+    case '\0':
+      /* Return the empty string if this is the end of the file
+        name.  */
+      return name;
+
+    case '/':
+      /* Return NAME (with the separator removed) if it describes a
+        file.  */
+      return name + 1;
+
+    default:
+      /* The file name doesn't match.  */
+      return NULL;
+    }
+}
+
+#if 0
+
+/* URL-encode N bytes of the specified STRING into at most N bytes of
+   BUFFER; STRING is assumed to be encoded in a `utf-8-emacs'
+   compatible coding system.  Value is the number of bytes encoded
+   (excluding the trailing null byte placed at the end of the encoded
+   text) or -1 upon failure.  */
+
+static ssize_t
+android_url_encode (const char *restrict string, size_t length,
+                   char *restrict buffer, size_t n)
+{
+  int len, character;
+  size_t num_encoded;
+  char *end;
+  char format[1 + 25];
+
+  /* For each multibyte character... */
+
+  end = string + length;
+  num_encoded = 0;
+
+  while (string < end)
+    {
+      /* XXX: Android documentation claims that URIs is encoded
+        according to the ``Unicode'' scheme, but what this means in
+        reality is that the URI is encoded in UTF-8, and then
+        each of its bytes are encoded.  */
+      /* Find the length of the multibyte character at STRING.  */
+      len = /* multibyte_length (string, end, true, true) */ 1;
+
+      /* 0 means that STRING is not a valid multibyte string.  */
+      if (!len || string + len > end)
+       goto failure;
+
+      /* Now fetch the character and increment string.  */
+      /* character = /\* STRING_CHAR ((unsigned char *) string) *\/; */
+      character = *(unsigned char *) string;
+      string += len;
+
+      /* If CHARACTER is not a letter or an unreserved character,
+        escape it.  */
+
+      if (!((character >= 'A'
+            && character <= 'Z')
+           || (character >= 'a'
+               && character <= 'z')
+           || (character >= '0'
+               && character <= '9')
+           || character == '_'
+           || character == '-'
+           || character == '!'
+           || character == '.'
+           || character == '~'
+           || character == '\''
+           || character == '('
+           || character == ')'
+           || character == '*'))
+       {
+         len = sprintf (format, "%%%X", (unsigned int) character);
+         if (len < 0)
+           goto failure;
+
+         /* See if there is enough space left to hold the encoded
+            string.  */
+
+         if (n < len)
+           goto failure;
+
+         n -= len;
+         num_encoded += len;
+
+         /* Copy the encoded string to STRING.  */
+         memcpy (buffer, format, n);
+         buffer += len;
+       }
+      else
+       {
+         /* No more space within BUFFER.  */
+         if (!n)
+           goto failure;
+
+         /* Don't encode this ASCII character; just store it.  */
+         n--, num_encoded++;
+         *(buffer++) = character;
+       }
+    }
+
+  /* If there's no space for a trailing null byte or more bytes have
+     been encoded than representable in ssize_t, fail.  */
+
+  if (!n || num_encoded > SSIZE_MAX)
+    goto failure;
+
+  /* Store the terminating NULL byte.  */
+  *buffer = '\0';
+  return num_encoded;
+
+ failure:
+  return -1;
+}
+
+/* Return the content URI corresponding to a `/content' file name,
+   or NULL if it is not a content URI.
+
+   This function is not reentrant.  */
+
+static const char *
+android_get_content_name (const char *filename)
+{
+  static char buffer[PATH_MAX + 1];
+  char *head, *token, *next, *saveptr, *copy, *mark, *mark1;
+  ssize_t rc;
+  size_t n, length;
+
+  /* Find the file name described if it starts with `/content'.  If
+     just the directory is described, return content://.  */
+
+  filename = android_is_special_directory (filename, "/content");
+
+  if (!filename)
+    return NULL;
+
+  if (!*filename)
+    return "content://";
+
+  /* Now copy FILENAME into a buffer and convert it into a content
+     URI.  */
+
+  copy = xstrdup (filename);
+  mark = saveptr = NULL;
+  head = stpcpy (buffer, "content:/");
+
+  /* Split FILENAME by slashes.  */
+
+  token = strtok_r (copy, "/", &saveptr);
+
+  while (token)
+    {
+      /* Compute the number of bytes remaining in buffer excluding a
+        trailing null byte.  */
+      n = PATH_MAX - (head - buffer);
+
+      /* Write / to the buffer.  Return failure if there is no space
+        for it.  */
+
+      if (!n)
+       goto failure;
+
+      *head++ = '/';
+      n--;
+
+      /* Find the next token now.  */
+      next = strtok_r (NULL, "/", &saveptr);
+
+      /* Detect and avoid encoding an encoded URL query affixed to the
+        end of the last component within the content file name.
+
+         Content URIs can include a query describing parameters that
+         must be provided to the content provider.  They are separated
+         from the rest of the URI by a single question mark character,
+         which should not be encoded.
+
+         However, the distinction between the separator and question
+         marks that appear inside file name components is lost when a
+         content URI is decoded into a content path.  To compensate
+         for this loss of information, Emacs assumes that the last
+         question mark is always a URI separator, and suffixes content
+         file names which contain question marks with a trailing
+         question mark.  */
+
+      if (!next)
+       {
+         /* Find the last question mark character.  */
+
+         mark1 = strchr (token, '?');
+
+         while (mark1)
+           {
+             mark = mark1;
+             mark1 = strchr (mark + 1, '?');
+           }
+       }
+
+      if (mark)
+       {
+         /* First, encode the part leading to the question mark
+            character.  */
+
+         rc = 0;
+         if (mark > token)
+           rc = android_url_encode (token, mark - token,
+                                    head, n + 1);
+
+         /* If this fails, bail out.  */
+
+         if (rc < 0)
+           goto failure;
+
+         /* Copy mark to the file name.  */
+
+         n -= rc, head += rc;
+         length = strlen (mark);
+
+         if (n < length)
+           goto failure;
+
+         strcpy (head, mark);
+
+         /* Now break out of the loop, since this is the last
+            component anyway.  */
+         break;
+       }
+      else
+       /* Now encode this file name component into the buffer.  */
+       rc = android_url_encode (token, strlen (token),
+                                head, n + 1);
+
+      if (rc < 0)
+       goto failure;
+
+      head += rc;
+      token = next;
+    }
+
+  /* buffer must have been null terminated by
+     `android_url_encode'.  */
+  xfree (copy);
+  return buffer;
+
+ failure:
+  xfree (copy);
+  return NULL;
+}
+
+/* Return whether or not the specified FILENAME is an accessible
+   content URI.  MODE specifies what to check.  */
+
+static bool
+android_check_content_access (const char *filename, int mode)
+{
+  const char *name;
+  jobject string;
+  size_t length;
+  jboolean rc;
+
+  name = android_get_content_name (filename);
+  length = strlen (name);
+
+  string = (*android_java_env)->NewByteArray (android_java_env,
+                                             length);
+  android_exception_check ();
+
+  (*android_java_env)->SetByteArrayRegion (android_java_env,
+                                          string, 0, length,
+                                          (jbyte *) name);
+  rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+                                              emacs_service,
+                                              service_class.check_content_uri,
+                                              string,
+                                              (jboolean) ((mode & R_OK)
+                                                          != 0),
+                                              (jboolean) ((mode & W_OK)
+                                                          != 0));
+  android_exception_check_1 (string);
+  ANDROID_DELETE_LOCAL_REF (string);
+
+  return rc;
+}
+
+#endif /* 0 */
+
+/* Return the current user's ``home'' directory, which is actually the
+   app data directory on Android.  */
+
+const char *
+android_get_home_directory (void)
+{
+  return android_files_dir;
+}
+
+/* Return the name of the file behind a file descriptor FD by reading
+   /proc/self/fd/.  Place the name in BUFFER, which should be able to
+   hold size bytes.  Value is 0 upon success, and 1 upon failure.  */
+
+static int
+android_proc_name (int fd, char *buffer, size_t size)
+{
+  char format[sizeof "/proc/self/fd/"
+             + INT_STRLEN_BOUND (int)];
+  ssize_t read;
+
+  sprintf (format, "/proc/self/fd/%d", fd);
+  read = readlink (format, buffer, size - 1);
+
+  if (read == -1)
+    return 1;
+
+  buffer[read] = '\0';
+  return 0;
+}
+
+/* Try to guarantee the existence of the `lib' directory within the
+   parent directory of the application files directory.
+
+   If `/data/data/org.gnu.emacs/lib' (or
+   `/data/user/N/org.gnu.emacs/lib') does not exist or is a dangling
+   symbolic link, create a symlink from it to the library
+   directory.
+
+   Newer versions of Android don't create this link by default, making
+   it difficult to locate the directory containing Emacs library
+   files, particularly from scripts in other programs sharing the same
+   user ID as Emacs that don't have access to `exec-path'.  */
+
+static void
+android_create_lib_link (void)
+{
+  char *filename;
+  char lib_directory[PATH_MAX];
+  int fd;
+
+  /* Find the directory containing the files directory.  */
+  filename = dirname (android_files_dir);
+  if (!filename)
+    goto failure;
+
+  /* Now make `lib_directory' the name of the library directory
+     within.  */
+  snprintf (lib_directory, PATH_MAX, "%s/lib", filename);
+
+  /* Try to open this directory.  */
+  fd = open (lib_directory, O_DIRECTORY);
+
+  /* If the directory can be opened normally, close it and return
+     now.  */
+  if (fd >= 0)
+    goto success;
+
+  /* Try to unlink the directory in case it's a dangling symbolic
+     link.  */
+  unlink (lib_directory);
+
+  /* Otherwise, try to symlink lib_directory to the actual library
+     directory.  */
+
+  if (symlink (android_lib_dir, lib_directory))
+    /* Print a warning message if creating the link fails.  */
+    __android_log_print (ANDROID_LOG_WARN, __func__,
+                        "Failed to create symbolic link from"
+                        " application library directory `%s'"
+                        " to its actual location at `%s'",
+                        lib_directory, android_files_dir);
+
+ success:
+  close (fd);
+ failure:
+  return;
+}
+
+
+
+/* JNI functions called by Java.  */
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#else
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+#endif
+
+JNIEXPORT jint JNICALL
+NATIVE_NAME (dup) (JNIEnv *env, jobject object, jint fd)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  return dup (fd);
+}
+
+JNIEXPORT jstring JNICALL
+NATIVE_NAME (getFingerprint) (JNIEnv *env, jobject object)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  char buffer[sizeof fingerprint * 2 + 1];
+
+  memset (buffer, 0, sizeof buffer);
+  hexbuf_digest (buffer, (char *) fingerprint,
+                sizeof fingerprint);
+
+  return (*env)->NewStringUTF (env, buffer);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
+                             jobject local_asset_manager,
+                             jobject files_dir, jobject libs_dir,
+                             jobject cache_dir,
+                             jfloat pixel_density_x,
+                             jfloat pixel_density_y,
+                             jfloat scaled_density,
+                             jobject class_path,
+                             jobject emacs_service_object,
+                             jint api_level)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  int pipefd[2];
+  pthread_t thread;
+  const char *java_string;
+
+  /* Set the Android API level early, as it is used by
+     `android_vfs_init'.  */
+  android_api_level = api_level;
+
+  /* This function should only be called from the main thread.  */
+
+  android_pixel_density_x = pixel_density_x;
+  android_pixel_density_y = pixel_density_y;
+  android_scaled_pixel_density = scaled_density;
+
+  __android_log_print (ANDROID_LOG_INFO, __func__,
+                      "Initializing "PACKAGE_STRING"...\nPlease report bugs to 
"
+                      PACKAGE_BUGREPORT".  Thanks.\n");
+
+  if (emacs_service_object)
+    {
+      /* Create a pipe and duplicate it to stdout and stderr.  Next,
+        make a thread that prints stderr to the system log.
+
+         Notice that this function is called in one of two ways.  The
+         first is when Emacs is being started as a GUI application by
+         the system, and the second is when Emacs is being started by
+         libandroid-emacs.so as an ordinary noninteractive Emacs.
+
+         In the second case, stderr is usually connected to a PTY, so
+         this is unnecessary.  */
+
+      if (pipe2 (pipefd, O_CLOEXEC) < 0)
+       emacs_abort ();
+
+      if (dup2 (pipefd[1], 2) < 0)
+       emacs_abort ();
+      close (pipefd[1]);
+
+      if (pthread_create (&thread, NULL, android_run_debug_thread,
+                         (void *) (intptr_t) pipefd[0]))
+       emacs_abort ();
+    }
+
+  /* Now set the path to the site load directory.  */
+
+  java_string = (*env)->GetStringUTFChars (env, (jstring) files_dir,
+                                          NULL);
+
+  if (!java_string)
+    emacs_abort ();
+
+  android_files_dir = strdup ((const char *) java_string);
+
+  if (!android_files_dir)
+    emacs_abort ();
+
+  (*env)->ReleaseStringUTFChars (env, (jstring) files_dir,
+                                java_string);
+
+  java_string = (*env)->GetStringUTFChars (env, (jstring) libs_dir,
+                                          NULL);
+
+  if (!java_string)
+    emacs_abort ();
+
+  android_lib_dir = strdup ((const char *) java_string);
+
+  if (!android_files_dir)
+    emacs_abort ();
+
+  (*env)->ReleaseStringUTFChars (env, (jstring) libs_dir,
+                                java_string);
+
+  java_string = (*env)->GetStringUTFChars (env, (jstring) cache_dir,
+                                          NULL);
+
+  if (!java_string)
+    emacs_abort ();
+
+  android_cache_dir = strdup ((const char *) java_string);
+
+  if (!android_files_dir)
+    emacs_abort ();
+
+  (*env)->ReleaseStringUTFChars (env, (jstring) cache_dir,
+                                java_string);
+
+  if (class_path)
+    {
+      java_string = (*env)->GetStringUTFChars (env, (jstring) class_path,
+                                              NULL);
+
+      if (!java_string)
+       emacs_abort ();
+
+      android_class_path = strdup ((const char *) java_string);
+
+      if (!android_files_dir)
+       emacs_abort ();
+
+      (*env)->ReleaseStringUTFChars (env, (jstring) class_path,
+                                    java_string);
+    }
+
+  /* Calculate the site-lisp path.  */
+
+  android_site_load_path = malloc (PATH_MAX + 1);
+
+  if (!android_site_load_path)
+    emacs_abort ();
+
+  android_game_path = malloc (PATH_MAX + 1);
+
+  if (!android_game_path)
+    emacs_abort ();
+
+  snprintf (android_site_load_path, PATH_MAX, "%s/site-lisp",
+           android_files_dir);
+  snprintf (android_game_path, PATH_MAX, "%s/scores", android_files_dir);
+
+  __android_log_print (ANDROID_LOG_INFO, __func__,
+                      "Site-lisp directory: %s\n"
+                      "Files directory: %s\n"
+                      "Native code directory: %s\n"
+                      "Game score path: %s\n"
+                      "Class path: %s\n",
+                      android_site_load_path,
+                      android_files_dir,
+                      android_lib_dir, android_game_path,
+                      (android_class_path
+                       ? android_class_path
+                       : "None"));
+
+  if (android_class_path)
+    /* Set EMACS_CLASS_PATH to the class path where
+       EmacsNoninteractive can be found.  */
+    setenv ("EMACS_CLASS_PATH", android_class_path, 1);
+
+  /* Set LD_LIBRARY_PATH to an appropriate value.  */
+  setenv ("LD_LIBRARY_PATH", android_lib_dir, 1);
+
+  /* Make a reference to the Emacs service.  */
+
+  if (emacs_service_object)
+    {
+      emacs_service = (*env)->NewGlobalRef (env, emacs_service_object);
+
+      if (!emacs_service)
+       emacs_abort ();
+
+      /* If the service is set this Emacs is being initialized as part
+        of the Emacs application itself.
+
+         Try to create a symlink from where scripts expect Emacs to
+         place its library files to the directory that actually holds
+         them; earlier versions of Android used to do this
+         automatically, but that feature has been removed.  */
+
+      android_create_lib_link ();
+    }
+
+  /* Set up events.  */
+  android_init_events ();
+
+  /* Set up the Android virtual filesystem layer.  */
+  android_vfs_init (env, local_asset_manager);
+
+  /* OK, setup is now complete.  The caller may call initEmacs
+     now.  */
+}
+
+JNIEXPORT jobject JNICALL
+NATIVE_NAME (getProcName) (JNIEnv *env, jobject object, jint fd)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  char buffer[PATH_MAX + 1];
+  size_t length;
+  jbyteArray array;
+
+  if (android_proc_name (fd, buffer, PATH_MAX + 1))
+    return NULL;
+
+  /* Return a byte array, as Java strings cannot always encode file
+     names.  */
+  length = strlen (buffer);
+  array = (*env)->NewByteArray (env, length);
+  if (!array)
+    return NULL;
+
+  (*env)->SetByteArrayRegion (env, array, 0, length,
+                             (jbyte *) buffer);
+
+  return array;
+}
+
+/* Initialize service_class, aborting if something goes wrong.  */
+
+static void
+android_init_emacs_service (void)
+{
+  jclass old;
+
+  service_class.class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "org/gnu/emacs/EmacsService");
+  eassert (service_class.class);
+
+  old = service_class.class;
+  service_class.class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 (jobject) old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!service_class.class)
+    emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature)                   \
+  service_class.c_name                                         \
+    = (*android_java_env)->GetMethodID (android_java_env,      \
+                                       service_class.class,    \
+                                       name, signature);       \
+  assert (service_class.c_name);
+
+  FIND_METHOD (fill_rectangle, "fillRectangle",
+              "(Lorg/gnu/emacs/EmacsDrawable;"
+              "Lorg/gnu/emacs/EmacsGC;IIII)V");
+  FIND_METHOD (fill_polygon, "fillPolygon",
+              "(Lorg/gnu/emacs/EmacsDrawable;"
+              "Lorg/gnu/emacs/EmacsGC;"
+              "[Landroid/graphics/Point;)V");
+  FIND_METHOD (draw_rectangle, "drawRectangle",
+              "(Lorg/gnu/emacs/EmacsDrawable;"
+              "Lorg/gnu/emacs/EmacsGC;IIII)V");
+  FIND_METHOD (draw_line, "drawLine",
+              "(Lorg/gnu/emacs/EmacsDrawable;"
+              "Lorg/gnu/emacs/EmacsGC;IIII)V");
+  FIND_METHOD (draw_point, "drawPoint",
+              "(Lorg/gnu/emacs/EmacsDrawable;"
+              "Lorg/gnu/emacs/EmacsGC;II)V");
+  FIND_METHOD (clear_window, "clearWindow",
+              "(Lorg/gnu/emacs/EmacsWindow;)V");
+  FIND_METHOD (clear_area, "clearArea",
+              "(Lorg/gnu/emacs/EmacsWindow;IIII)V");
+  FIND_METHOD (ring_bell, "ringBell", "()V");
+  FIND_METHOD (query_tree, "queryTree",
+              "(Lorg/gnu/emacs/EmacsWindow;)[S");
+  FIND_METHOD (get_screen_width, "getScreenWidth", "(Z)I");
+  FIND_METHOD (get_screen_height, "getScreenHeight", "(Z)I");
+  FIND_METHOD (detect_mouse, "detectMouse", "()Z");
+  FIND_METHOD (name_keysym, "nameKeysym", "(I)Ljava/lang/String;");
+  FIND_METHOD (browse_url, "browseUrl", "(Ljava/lang/String;Z)"
+              "Ljava/lang/String;");
+  FIND_METHOD (restart_emacs, "restartEmacs", "()V");
+  FIND_METHOD (update_ic, "updateIC",
+              "(Lorg/gnu/emacs/EmacsWindow;IIII)V");
+  FIND_METHOD (reset_ic, "resetIC",
+              "(Lorg/gnu/emacs/EmacsWindow;I)V");
+  FIND_METHOD (open_content_uri, "openContentUri",
+              "([BZZZ)I");
+  FIND_METHOD (check_content_uri, "checkContentUri",
+              "([BZZ)Z");
+  FIND_METHOD (query_battery, "queryBattery", "()[J");
+  FIND_METHOD (update_extracted_text, "updateExtractedText",
+              "(Lorg/gnu/emacs/EmacsWindow;"
+              "Landroid/view/inputmethod/ExtractedText;I)V");
+  FIND_METHOD (update_cursor_anchor_info, "updateCursorAnchorInfo",
+              "(Lorg/gnu/emacs/EmacsWindow;FFFF)V");
+  FIND_METHOD (get_document_authorities, "getDocumentAuthorities",
+              "()[Ljava/lang/String;");
+  FIND_METHOD (request_directory_access, "requestDirectoryAccess",
+              "()I");
+  FIND_METHOD (get_document_trees, "getDocumentTrees",
+              "([B)[Ljava/lang/String;");
+  FIND_METHOD (document_id_from_name, "documentIdFromName",
+              "(Ljava/lang/String;Ljava/lang/String;"
+              "[Ljava/lang/String;)I");
+  FIND_METHOD (get_tree_uri, "getTreeUri",
+              "(Ljava/lang/String;Ljava/lang/String;)"
+              "Ljava/lang/String;");
+  FIND_METHOD (stat_document, "statDocument",
+              "(Ljava/lang/String;Ljava/lang/String;)[J");
+  FIND_METHOD (access_document, "accessDocument",
+              "(Ljava/lang/String;Ljava/lang/String;Z)I");
+  FIND_METHOD (open_document_directory, "openDocumentDirectory",
+              "(Ljava/lang/String;Ljava/lang/String;)"
+              "Landroid/database/Cursor;");
+  FIND_METHOD (read_directory_entry, "readDirectoryEntry",
+              "(Landroid/database/Cursor;)Lorg/gnu/emacs/"
+              "EmacsDirectoryEntry;");
+  FIND_METHOD (open_document, "openDocument",
+              "(Ljava/lang/String;Ljava/lang/String;ZZ)"
+              "Landroid/os/ParcelFileDescriptor;");
+  FIND_METHOD (create_document, "createDocument",
+              "(Ljava/lang/String;Ljava/lang/String;"
+              "Ljava/lang/String;)Ljava/lang/String;");
+  FIND_METHOD (create_directory, "createDirectory",
+              "(Ljava/lang/String;Ljava/lang/String;"
+              "Ljava/lang/String;)Ljava/lang/String;");
+  FIND_METHOD (delete_document, "deleteDocument",
+              "(Ljava/lang/String;Ljava/lang/String;"
+              "Ljava/lang/String;)I");
+  FIND_METHOD (rename_document, "renameDocument",
+              "(Ljava/lang/String;Ljava/lang/String;"
+              "Ljava/lang/String;Ljava/lang/String;)I");
+  FIND_METHOD (move_document, "moveDocument",
+              "(Ljava/lang/String;Ljava/lang/String;"
+              "Ljava/lang/String;Ljava/lang/String;"
+              "Ljava/lang/String;)Ljava/lang/String;");
+  FIND_METHOD (valid_authority, "validAuthority",
+              "(Ljava/lang/String;)Z");
+#undef FIND_METHOD
+}
+
+static void
+android_init_emacs_pixmap (void)
+{
+  jclass old;
+
+  pixmap_class.class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "org/gnu/emacs/EmacsPixmap");
+  eassert (pixmap_class.class);
+
+  old = pixmap_class.class;
+  pixmap_class.class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 (jobject) old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!pixmap_class.class)
+    emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature)                   \
+  pixmap_class.c_name                                          \
+    = (*android_java_env)->GetMethodID (android_java_env,      \
+                                       pixmap_class.class,     \
+                                       name, signature);       \
+  assert (pixmap_class.c_name);
+
+  FIND_METHOD (constructor, "<init>", "(S[IIII)V");
+  FIND_METHOD (constructor_mutable, "<init>", "(SIII)V");
+
+#undef FIND_METHOD
+}
+
+static void
+android_init_graphics_point (void)
+{
+  jclass old;
+
+  point_class.class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "android/graphics/Point");
+  eassert (point_class.class);
+
+  old = point_class.class;
+  point_class.class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 (jobject) old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!point_class.class)
+    emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature)                   \
+  point_class.c_name                                           \
+    = (*android_java_env)->GetMethodID (android_java_env,      \
+                                       point_class.class,      \
+                                       name, signature);       \
+  assert (point_class.c_name);
+
+  FIND_METHOD (constructor, "<init>", "(II)V");
+#undef FIND_METHOD
+}
+
+static void
+android_init_emacs_drawable (void)
+{
+  jclass old;
+
+  drawable_class.class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "org/gnu/emacs/EmacsDrawable");
+  eassert (drawable_class.class);
+
+  old = drawable_class.class;
+  drawable_class.class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 (jobject) old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!drawable_class.class)
+    emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature)                   \
+  drawable_class.c_name                                                \
+    = (*android_java_env)->GetMethodID (android_java_env,      \
+                                       drawable_class.class,   \
+                                       name, signature);       \
+  assert (drawable_class.c_name);
+
+  FIND_METHOD (get_bitmap, "getBitmap", "()Landroid/graphics/Bitmap;");
+  FIND_METHOD (damage_rect, "damageRect", "(Landroid/graphics/Rect;)V");
+#undef FIND_METHOD
+}
+
+static void
+android_init_emacs_window (void)
+{
+  jclass old;
+
+  window_class.class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "org/gnu/emacs/EmacsWindow");
+  eassert (window_class.class);
+
+  old = window_class.class;
+  window_class.class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 (jobject) old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!window_class.class)
+    emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature)                   \
+  window_class.c_name                                          \
+    = (*android_java_env)->GetMethodID (android_java_env,      \
+                                       window_class.class,     \
+                                       name, signature);       \
+  assert (window_class.c_name);
+
+  FIND_METHOD (swap_buffers, "swapBuffers", "()V");
+  FIND_METHOD (toggle_on_screen_keyboard,
+              "toggleOnScreenKeyboard", "(Z)V");
+  FIND_METHOD (lookup_string, "lookupString", "(I)Ljava/lang/String;");
+  FIND_METHOD (set_fullscreen, "setFullscreen", "(Z)V");
+  FIND_METHOD (change_window_background, "changeWindowBackground",
+              "(I)V");
+  FIND_METHOD (reparent_to, "reparentTo",
+              "(Lorg/gnu/emacs/EmacsWindow;II)V");
+  FIND_METHOD (map_window, "mapWindow", "()V");
+  FIND_METHOD (unmap_window, "unmapWindow", "()V");
+  FIND_METHOD (resize_window, "resizeWindow", "(II)V");
+  FIND_METHOD (move_window, "moveWindow", "(II)V");
+  FIND_METHOD (make_input_focus, "makeInputFocus", "(J)V");
+  FIND_METHOD (raise, "raise", "()V");
+  FIND_METHOD (lower, "lower", "()V");
+  FIND_METHOD (get_window_geometry, "getWindowGeometry",
+              "()[I");
+  FIND_METHOD (translate_coordinates, "translateCoordinates",
+              "(II)[I");
+  FIND_METHOD (set_dont_focus_on_map, "setDontFocusOnMap", "(Z)V");
+  FIND_METHOD (set_dont_accept_focus, "setDontAcceptFocus", "(Z)V");
+  FIND_METHOD (define_cursor, "defineCursor",
+              "(Lorg/gnu/emacs/EmacsCursor;)V");
+#undef FIND_METHOD
+}
+
+static void
+android_init_emacs_cursor (void)
+{
+  jclass old;
+
+  cursor_class.class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "org/gnu/emacs/EmacsCursor");
+  eassert (cursor_class.class);
+
+  old = cursor_class.class;
+  cursor_class.class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 (jobject) old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!cursor_class.class)
+    emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature)                   \
+  cursor_class.c_name                                          \
+    = (*android_java_env)->GetMethodID (android_java_env,      \
+                                       cursor_class.class,     \
+                                       name, signature);       \
+  assert (cursor_class.c_name);
+
+  FIND_METHOD (constructor, "<init>", "(SI)V");
+#undef FIND_METHOD
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv,
+                        jobject dump_file_object)
+{
+  /* android_emacs_init is not main, so GCC is not nice enough to add
+     the stack alignment prologue.
+
+     Unfortunately for us, dalvik on Android 4.0.x calls native code
+     with a 4 byte aligned stack, so this prologue must be inserted
+     before each function exported via JNI.  */
+
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  char **c_argv;
+  jsize nelements, i;
+  jobject argument;
+  const char *c_argument;
+  char *dump_file;
+
+  android_java_env = env;
+
+  nelements = (*env)->GetArrayLength (env, argv);
+  c_argv = alloca (sizeof *c_argv * nelements);
+
+  for (i = 0; i < nelements; ++i)
+    {
+      argument = (*env)->GetObjectArrayElement (env, argv, i);
+      c_argument = (*env)->GetStringUTFChars (env, (jstring) argument,
+                                             NULL);
+
+      if (!c_argument)
+       emacs_abort ();
+
+      /* Note that c_argument is in ``modified UTF-8 encoding'', but
+        we don't care as NUL bytes are not being specified inside.  */
+      c_argv[i] = alloca (strlen (c_argument) + 1);
+      strcpy (c_argv[i], c_argument);
+      (*env)->ReleaseStringUTFChars (env, (jstring) argument, c_argument);
+    }
+
+  android_init_emacs_service ();
+  android_init_emacs_pixmap ();
+  android_init_graphics_point ();
+  android_init_emacs_drawable ();
+  android_init_emacs_window ();
+  android_init_emacs_cursor ();
+
+  /* Set HOME to the app data directory.  */
+  setenv ("HOME", android_files_dir, 1);
+
+  /* Set TMPDIR to the temporary files directory.  */
+  setenv ("TMPDIR", android_cache_dir, 1);
+
+  /* And finally set "SHELL" to /system/bin/sh.  Otherwise, some
+     programs will look for /bin/sh, which is problematic.  */
+  setenv ("SHELL", "/system/bin/sh", 1);
+
+  /* Set the cwd to that directory as well.  */
+  if (chdir (android_files_dir))
+    __android_log_print (ANDROID_LOG_WARN, __func__,
+                        "chdir: %s", strerror (errno));
+
+  /* Initialize the Android GUI as long as the service object was
+     set.  */
+
+  if (emacs_service)
+    android_init_gui = true;
+
+  /* Now see if a dump file has been specified and should be used.  */
+  dump_file = NULL;
+
+  if (dump_file_object)
+    {
+      c_argument
+       = (*env)->GetStringUTFChars (env, (jstring) dump_file_object,
+                                    NULL);
+
+      /* Copy the Java string data once.  */
+      dump_file = strdup (c_argument);
+
+      /* Release the Java string data.  */
+      (*env)->ReleaseStringUTFChars (env, (jstring) dump_file_object,
+                                    c_argument);
+    }
+
+  /* Delete local references to objects that are no longer needed.  */
+  ANDROID_DELETE_LOCAL_REF (argv);
+  ANDROID_DELETE_LOCAL_REF (dump_file_object);
+
+  /* Restore the signal mask at the time of startup if it was changed
+     to block unwanted signals from reaching system threads.  */
+
+  if (signal_mask_changed_p)
+    pthread_sigmask (SIG_SETMASK, &startup_signal_mask, NULL);
+
+  /* Now start Emacs proper.  */
+  android_emacs_init (nelements, c_argv, dump_file);
+
+  /* android_emacs_init should never return.  */
+  emacs_abort ();
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (emacsAbort) (JNIEnv *env, jobject object)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  emacs_abort ();
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (quit) (JNIEnv *env, jobject object)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  __android_log_print (ANDROID_LOG_VERBOSE, __func__,
+                      "Sending SIGIO and setting Vquit_flag");
+
+  /* Raise sigio to interrupt anything that could be reading
+     input.  */
+  Vquit_flag = Qt;
+  kill (getpid (), SIGIO);
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject object,
+                                  jshort window, jlong time,
+                                  jint x, jint y, jint width,
+                                  jint height)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.xconfigure.type = ANDROID_CONFIGURE_NOTIFY;
+  event.xconfigure.serial = ++event_serial;
+  event.xconfigure.window = window;
+  event.xconfigure.time = time;
+  event.xconfigure.x = x;
+  event.xconfigure.y = y;
+  event.xconfigure.width = width;
+  event.xconfigure.height = height;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object,
+                           jshort window, jlong time,
+                           jint state, jint keycode,
+                           jint unicode_char)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.xkey.type = ANDROID_KEY_PRESS;
+  event.xkey.serial = ++event_serial;
+  event.xkey.window = window;
+  event.xkey.time = time;
+  event.xkey.state = state;
+  event.xkey.keycode = keycode;
+  event.xkey.unicode_char = unicode_char;
+  event.xkey.counter = 0;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object,
+                             jshort window, jlong time,
+                             jint state, jint keycode,
+                             jint unicode_char)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.xkey.type = ANDROID_KEY_RELEASE;
+  event.xkey.serial = ++event_serial;
+  event.xkey.window = window;
+  event.xkey.time = time;
+  event.xkey.state = state;
+  event.xkey.keycode = keycode;
+  event.xkey.unicode_char = unicode_char;
+  event.xkey.counter = 0;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendFocusIn) (JNIEnv *env, jobject object,
+                          jshort window, jlong time)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.xfocus.type = ANDROID_FOCUS_IN;
+  event.xfocus.serial = ++event_serial;
+  event.xfocus.window = window;
+  event.xfocus.time = time;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendFocusOut) (JNIEnv *env, jobject object,
+                           jshort window, jlong time)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.xfocus.type = ANDROID_FOCUS_OUT;
+  event.xfocus.serial = ++event_serial;
+  event.xfocus.window = window;
+  event.xfocus.time = time;
+
+  android_write_event (&event);
+  return ++event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendWindowAction) (JNIEnv *env, jobject object,
+                               jshort window, jint action)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.xaction.type = ANDROID_WINDOW_ACTION;
+  event.xaction.serial = ++event_serial;
+  event.xaction.window = window;
+  event.xaction.action = action;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendEnterNotify) (JNIEnv *env, jobject object,
+                              jshort window, jint x, jint y,
+                              jlong time)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.xcrossing.type = ANDROID_ENTER_NOTIFY;
+  event.xcrossing.serial = ++event_serial;
+  event.xcrossing.window = window;
+  event.xcrossing.x = x;
+  event.xcrossing.y = y;
+  event.xcrossing.time = time;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendLeaveNotify) (JNIEnv *env, jobject object,
+                              jshort window, jint x, jint y,
+                              jlong time)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.xcrossing.type = ANDROID_LEAVE_NOTIFY;
+  event.xcrossing.serial = ++event_serial;
+  event.xcrossing.window = window;
+  event.xcrossing.x = x;
+  event.xcrossing.y = y;
+  event.xcrossing.time = time;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendMotionNotify) (JNIEnv *env, jobject object,
+                               jshort window, jint x, jint y,
+                               jlong time)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.xmotion.type = ANDROID_MOTION_NOTIFY;
+  event.xmotion.serial = ++event_serial;
+  event.xmotion.window = window;
+  event.xmotion.x = x;
+  event.xmotion.y = y;
+  event.xmotion.time = time;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendButtonPress) (JNIEnv *env, jobject object,
+                              jshort window, jint x, jint y,
+                              jlong time, jint state,
+                              jint button)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.xbutton.type = ANDROID_BUTTON_PRESS;
+  event.xbutton.serial = ++event_serial;
+  event.xbutton.window = window;
+  event.xbutton.x = x;
+  event.xbutton.y = y;
+  event.xbutton.time = time;
+  event.xbutton.state = state;
+  event.xbutton.button = button;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject object,
+                                jshort window, jint x, jint y,
+                                jlong time, jint state,
+                                jint button)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.xbutton.type = ANDROID_BUTTON_RELEASE;
+  event.xbutton.serial = ++event_serial;
+  event.xbutton.window = window;
+  event.xbutton.x = x;
+  event.xbutton.y = y;
+  event.xbutton.time = time;
+  event.xbutton.state = state;
+  event.xbutton.button = button;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object,
+                            jshort window, jint x, jint y,
+                            jlong time, jint pointer_id,
+                            jint flags)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.touch.type = ANDROID_TOUCH_DOWN;
+  event.touch.serial = ++event_serial;
+  event.touch.window = window;
+  event.touch.x = x;
+  event.touch.y = y;
+  event.touch.time = time;
+  event.touch.pointer_id = pointer_id;
+  event.touch.flags = flags;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object,
+                          jshort window, jint x, jint y,
+                          jlong time, jint pointer_id,
+                          jint flags)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.touch.type = ANDROID_TOUCH_UP;
+  event.touch.serial = ++event_serial;
+  event.touch.window = window;
+  event.touch.x = x;
+  event.touch.y = y;
+  event.touch.time = time;
+  event.touch.pointer_id = pointer_id;
+  event.touch.flags = flags;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object,
+                            jshort window, jint x, jint y,
+                            jlong time, jint pointer_id,
+                            jint flags)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.touch.type = ANDROID_TOUCH_MOVE;
+  event.touch.serial = ++event_serial;
+  event.touch.window = window;
+  event.touch.x = x;
+  event.touch.y = y;
+  event.touch.time = time;
+  event.touch.pointer_id = pointer_id;
+  event.touch.flags = flags;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object,
+                        jshort window, jint x, jint y,
+                        jlong time, jint state,
+                        jfloat x_delta, jfloat y_delta)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.wheel.type = ANDROID_WHEEL;
+  event.wheel.serial = ++event_serial;
+  event.wheel.window = window;
+  event.wheel.x = x;
+  event.wheel.y = y;
+  event.wheel.time = time;
+  event.wheel.state = state;
+  event.wheel.x_delta = x_delta;
+  event.wheel.y_delta = y_delta;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendIconified) (JNIEnv *env, jobject object,
+                            jshort window)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.iconified.type = ANDROID_ICONIFIED;
+  event.iconified.serial = ++event_serial;
+  event.iconified.window = window;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendDeiconified) (JNIEnv *env, jobject object,
+                              jshort window)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.iconified.type = ANDROID_DEICONIFIED;
+  event.iconified.serial = ++event_serial;
+  event.iconified.window = window;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject object,
+                              jshort window, jint menu_event_id,
+                              jint menu_event_serial)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.menu.type = ANDROID_CONTEXT_MENU;
+  event.menu.serial = ++event_serial;
+  event.menu.window = window;
+  event.menu.menu_event_id = menu_event_id;
+  event.menu.menu_event_serial = menu_event_serial;
+
+  android_write_event (&event);
+  return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendExpose) (JNIEnv *env, jobject object,
+                         jshort window, jint x, jint y,
+                         jint width, jint height)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  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;
+}
+
+JNIEXPORT jboolean JNICALL
+NATIVE_NAME (shouldForwardMultimediaButtons) (JNIEnv *env,
+                                             jobject object)
+{
+  /* Yes, android_pass_multimedia_buttons_to_system is being
+     read from the UI thread.  */
+  return !android_pass_multimedia_buttons_to_system;
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (blitRect) (JNIEnv *env, jobject object,
+                       jobject src, jobject dest,
+                       jint x1, jint y1, jint x2, jint y2)
+{
+  AndroidBitmapInfo src_info, dest_info;
+  unsigned char *src_data_1, *dest_data_1;
+  void *src_data, *dest_data;
+
+  /* N.B. that X2 and Y2 represent the pixel past the edge of the
+     rectangle; thus, the width is x2 - x1 and the height is y2 -
+     y1.  */
+
+  memset (&src_info, 0, sizeof src_info);
+  memset (&dest_info, 0, sizeof dest_info);
+  AndroidBitmap_getInfo (env, src, &src_info);
+  AndroidBitmap_getInfo (env, dest, &dest_info);
+
+  /* If the stride is 0 after a call to `getInfo', assume it
+     failed.  */
+
+  if (!src_info.stride || !dest_info.stride)
+    return;
+
+  /* If formats differ, abort.  */
+  eassert (src_info.format == dest_info.format
+          && src_info.format == ANDROID_BITMAP_FORMAT_RGBA_8888);
+
+  /* Lock the image data.  */
+  src_data = NULL;
+  AndroidBitmap_lockPixels (env, src, &src_data);
+
+  if (!src_data)
+    return;
+
+  dest_data = NULL;
+  AndroidBitmap_lockPixels (env, dest, &dest_data);
+
+  if (!dest_data)
+    goto fail1;
+
+  /* Now clip the rectangle to the bounds of the source and
+     destination bitmap.  */
+
+  x1 = MAX (x1, 0);
+  y1 = MAX (y1, 0);
+  x2 = MAX (x2, 0);
+  y2 = MAX (y2, 0);
+
+
+  if (x1 >= src_info.width
+      || x1 >= dest_info.width)
+    x1 = MIN (dest_info.width - 1, src_info.width - 1);
+
+  if (x2 > src_info.width
+      || x2 > dest_info.width)
+    x2 = MIN (src_info.width, dest_info.width);
+
+  if (y1 >= src_info.height
+      || y1 >= dest_info.height)
+    y1 = MIN (dest_info.height - 1, src_info.height - 1);
+
+  if (y2 > src_info.height
+      || y2 > dest_info.height)
+    y2 = MIN (src_info.height, dest_info.height);
+
+  if (x1 >= x2 || y1 >= y2)
+    goto fail2;
+
+  /* Determine the address of the first line to copy.  */
+
+  src_data_1 = src_data;
+  dest_data_1 = dest_data;
+  src_data_1 += x1 * 4;
+  src_data_1 += y1 * src_info.stride;
+  dest_data_1 += x1 * 4;
+  dest_data_1 += y1 * dest_info.stride;
+
+  /* Start copying each line.  */
+
+  while (y1 != y2)
+    {
+      memcpy (dest_data_1, src_data_1, (x2 - x1) * 4);
+      src_data_1 += src_info.stride;
+      dest_data_1 += dest_info.stride;
+      y1++;
+    }
+
+  /* Complete the copy and unlock the bitmap.  */
+
+ fail2:
+  AndroidBitmap_unlockPixels (env, dest);
+ fail1:
+  AndroidBitmap_unlockPixels (env, src);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (notifyPixelsChanged) (JNIEnv *env, jobject object,
+                                  jobject bitmap)
+{
+  void *data;
+
+  /* Lock and unlock the bitmap.  This calls
+     SkBitmap->notifyPixelsChanged.  */
+
+  if (AndroidBitmap_lockPixels (env, bitmap, &data) < 0)
+    /* The return value is less than 0 if an error occurs.
+       Good luck finding this in the documentation.  */
+    return;
+
+  AndroidBitmap_unlockPixels (env, bitmap);
+}
+
+/* Forward declarations of deadlock prevention functions.  */
+
+static void android_begin_query (void);
+static void android_end_query (void);
+static void android_answer_query_spin (void);
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (beginSynchronous) (JNIEnv *env, jobject object)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  android_begin_query ();
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (endSynchronous) (JNIEnv *env, jobject object)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  android_end_query ();
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (answerQuerySpin) (JNIEnv *env, jobject object)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  android_answer_query_spin ();
+}
+
+
+
+/* System thread setup.  Android doesn't always block signals Emacs is
+   interested in from being received by the UI or render threads,
+   which can lead to problems when those signals then interrupt one of
+   those threads.  */
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (setupSystemThread) (void)
+{
+  sigset_t sigset;
+
+  /* Block everything except for SIGSEGV and SIGBUS; those two are
+     used by the runtime.  */
+
+  sigfillset (&sigset);
+  sigdelset (&sigset, SIGSEGV);
+  sigdelset (&sigset, SIGBUS);
+
+  /* Save the signal mask that was previously used.  It will be
+     restored in `initEmacs'.  */
+
+  if (pthread_sigmask (SIG_BLOCK, &sigset, &startup_signal_mask))
+    __android_log_print (ANDROID_LOG_WARN, __func__,
+                        "pthread_sigmask: %s", strerror (errno));
+  else
+    signal_mask_changed_p = true;
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#else
+#pragma GCC diagnostic pop
+#endif
+
+
+
+/* Java functions called by C.
+
+   Because all C code runs in the native function initEmacs, ALL LOCAL
+   REFERENCES WILL PERSIST!
+
+   This means that every local reference must be explicitly destroyed
+   with DeleteLocalRef.  A helper macro is provided to do this.  */
+
+struct android_handle_entry
+{
+  /* The type.  */
+  enum android_handle_type type;
+
+  /* The handle.  */
+  jobject handle;
+};
+
+/* Table of handles MAX_HANDLE long.  */
+struct android_handle_entry android_handles[USHRT_MAX];
+
+/* The largest handle ID currently known, but subject to
+   wraparound.  */
+static android_handle max_handle;
+
+/* Allocate a new, unused, handle identifier.  If Emacs is out of
+   identifiers, return 0.  */
+
+static android_handle
+android_alloc_id (void)
+{
+  android_handle handle;
+
+  /* 0 is never a valid handle ID.  */
+
+  if (!max_handle)
+    max_handle++;
+
+  /* See if the handle is already occupied.  */
+
+  if (android_handles[max_handle].handle)
+    {
+      /* Look for a fresh unoccupied handle.  */
+
+      handle = max_handle;
+      max_handle++;
+
+      while (handle != max_handle)
+       {
+         ++max_handle;
+
+         /* Make sure the handle is valid.  */
+         if (!max_handle)
+           ++max_handle;
+
+         if (!android_handles[max_handle].handle)
+           return max_handle++;
+       }
+
+      return ANDROID_NONE;
+    }
+
+  return max_handle++;
+}
+
+/* Destroy the specified handle and mark it as free on the Java side
+   as well.  */
+
+static void
+android_destroy_handle (android_handle handle)
+{
+  static jclass old, class;
+  static jmethodID method;
+
+  if (!android_handles[handle].handle)
+    {
+      __android_log_print (ANDROID_LOG_ERROR, __func__,
+                          "Trying to destroy free handle!");
+      emacs_abort ();
+    }
+
+  if (!class)
+    {
+      class
+       = (*android_java_env)->FindClass (android_java_env,
+                                         "org/gnu/emacs/EmacsHandleObject");
+      assert (class != NULL);
+
+      method
+       = (*android_java_env)->GetMethodID (android_java_env, class,
+                                           "destroyHandle", "()V");
+      assert (method != NULL);
+
+      old = class;
+      class
+       = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                     (jobject) class);
+      android_exception_check_1 (old);
+      ANDROID_DELETE_LOCAL_REF (old);
+    }
+
+  (*android_java_env)->CallVoidMethod (android_java_env,
+                                      android_handles[handle].handle,
+                                      method);
+
+  /* Just clear any exception thrown.  If destroying the handle
+     fails from an out-of-memory error, then Emacs loses some
+     resources, but that is not as big deal as signalling.  */
+  (*android_java_env)->ExceptionClear (android_java_env);
+
+  /* Delete the global reference regardless of any error.  */
+  (*android_java_env)->DeleteGlobalRef (android_java_env,
+                                       android_handles[handle].handle);
+  android_handles[handle].handle = NULL;
+}
+
+jobject
+android_resolve_handle (android_handle handle,
+                       enum android_handle_type type)
+{
+  if (!handle)
+    /* ANDROID_NONE.  */
+    return NULL;
+
+  /* CheckJNI will normally ensure that the handle exists and is
+     the right type, but with a less informative error message.
+     Don't waste cycles doing our own checking here.  */
+
+#ifdef ENABLE_CHECKING
+
+  if (!android_handles[handle].handle)
+    {
+      __android_log_print (ANDROID_LOG_ERROR, __func__,
+                          "Trying to resolve free handle!");
+      emacs_abort ();
+    }
+
+  if (android_handles[handle].type != type)
+    {
+      __android_log_print (ANDROID_LOG_ERROR, __func__,
+                          "Handle has wrong type!");
+      emacs_abort ();
+    }
+
+#endif /* ENABLE_CHECKING */
+
+  return android_handles[handle].handle;
+}
+
+static jobject
+android_resolve_handle2 (android_handle handle,
+                        enum android_handle_type type,
+                        enum android_handle_type type2)
+{
+  if (!handle)
+    return NULL;
+
+  /* CheckJNI will normally ensure that the handle exists and is
+     the right type, but with a less informative error message.
+     Don't waste cycles doing our own checking here.  */
+
+#ifdef ENABLE_CHECKING
+
+  if (!android_handles[handle].handle)
+    {
+      __android_log_print (ANDROID_LOG_ERROR, __func__,
+                          "Trying to resolve free handle!");
+      emacs_abort ();
+    }
+
+  if (android_handles[handle].type != type
+      && android_handles[handle].type != type2)
+    {
+      __android_log_print (ANDROID_LOG_ERROR, __func__,
+                          "Handle has wrong type!");
+      emacs_abort ();
+    }
+
+#endif /* ENABLE_CHECKING */
+
+  return android_handles[handle].handle;
+}
+
+void
+android_change_window_attributes (android_window handle,
+                                 enum android_window_value_mask value_mask,
+                                 struct android_set_window_attributes *attrs)
+{
+  jmethodID method;
+  jobject window;
+  jint pixel;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+  if (value_mask & ANDROID_CW_BACK_PIXEL)
+    {
+      method = window_class.change_window_background;
+      pixel = (jint) attrs->background_pixel;
+      (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                    window,
+                                                    window_class.class,
+                                                    method, pixel);
+      android_exception_check ();
+    }
+}
+
+/* Create a new window with the given width, height and
+   attributes.  */
+
+android_window
+android_create_window (android_window parent, int x, int y,
+                      int width, int height,
+                      enum android_window_value_mask value_mask,
+                      struct android_set_window_attributes *attrs)
+{
+  static jclass class;
+  static jmethodID constructor;
+  jobject object, parent_object, old;
+  android_window window;
+  android_handle prev_max_handle;
+  bool override_redirect;
+
+  parent_object = android_resolve_handle (parent, ANDROID_HANDLE_WINDOW);
+
+  prev_max_handle = max_handle;
+  window = android_alloc_id ();
+
+  if (!window)
+    error ("Out of window handles!");
+
+  if (!class)
+    {
+      class = (*android_java_env)->FindClass (android_java_env,
+                                             "org/gnu/emacs/EmacsWindow");
+      assert (class != NULL);
+
+      constructor
+       = (*android_java_env)->GetMethodID (android_java_env, class, "<init>",
+                                           "(SLorg/gnu/emacs/EmacsWindow;"
+                                           "IIIIZ)V");
+      assert (constructor != NULL);
+
+      old = class;
+      class = (*android_java_env)->NewGlobalRef (android_java_env, class);
+      android_exception_check_1 (old);
+      ANDROID_DELETE_LOCAL_REF (old);
+    }
+
+  /* N.B. that ANDROID_CW_OVERRIDE_REDIRECT can only be set at window
+     creation time.  */
+  override_redirect = ((value_mask
+                       & ANDROID_CW_OVERRIDE_REDIRECT)
+                      && attrs->override_redirect);
+
+  object = (*android_java_env)->NewObject (android_java_env, class,
+                                          constructor, (jshort) window,
+                                          parent_object, (jint) x, (jint) y,
+                                          (jint) width, (jint) height,
+                                          (jboolean) override_redirect);
+  if (!object)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+
+      max_handle = prev_max_handle;
+      memory_full (0);
+    }
+
+  android_handles[window].type = ANDROID_HANDLE_WINDOW;
+  android_handles[window].handle
+    = (*android_java_env)->NewGlobalRef (android_java_env,
+                                        object);
+  (*android_java_env)->ExceptionClear (android_java_env);
+  ANDROID_DELETE_LOCAL_REF (object);
+
+  if (!android_handles[window].handle)
+    memory_full (0);
+
+  android_change_window_attributes (window, value_mask, attrs);
+  return window;
+}
+
+void
+android_set_window_background (android_window window, unsigned long pixel)
+{
+  struct android_set_window_attributes attrs;
+
+  attrs.background_pixel = pixel;
+  android_change_window_attributes (window, ANDROID_CW_BACK_PIXEL,
+                                   &attrs);
+}
+
+void
+android_destroy_window (android_window window)
+{
+  if (android_handles[window].type != ANDROID_HANDLE_WINDOW)
+    {
+      __android_log_print (ANDROID_LOG_ERROR, __func__,
+                          "Trying to destroy something not a window!");
+      emacs_abort ();
+    }
+
+  android_destroy_handle (window);
+}
+
+static void
+android_init_android_rect_class (void)
+{
+  jclass old;
+
+  if (android_rect_class)
+    /* Already initialized.  */
+    return;
+
+  android_rect_class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "android/graphics/Rect");
+  assert (android_rect_class);
+
+  android_rect_constructor
+    = (*android_java_env)->GetMethodID (android_java_env, android_rect_class,
+                                       "<init>", "(IIII)V");
+  assert (emacs_gc_constructor);
+
+  old = android_rect_class;
+  android_rect_class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 (jobject) android_rect_class);
+  android_exception_check_1 (old);
+  ANDROID_DELETE_LOCAL_REF (old);
+}
+
+static void
+android_init_emacs_gc_class (void)
+{
+  jclass old;
+
+  if (emacs_gc_class)
+    /* Already initialized.  */
+    return;
+
+  emacs_gc_class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "org/gnu/emacs/EmacsGC");
+  assert (emacs_gc_class);
+
+  emacs_gc_constructor
+    = (*android_java_env)->GetMethodID (android_java_env,
+                                       emacs_gc_class,
+                                       "<init>", "(S)V");
+  assert (emacs_gc_constructor);
+
+  emacs_gc_mark_dirty
+    = (*android_java_env)->GetMethodID (android_java_env,
+                                       emacs_gc_class,
+                                       "markDirty", "(Z)V");
+  assert (emacs_gc_mark_dirty);
+
+  old = emacs_gc_class;
+  emacs_gc_class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 (jobject) emacs_gc_class);
+  android_exception_check_1 (old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  emacs_gc_foreground
+    = (*android_java_env)->GetFieldID (android_java_env,
+                                      emacs_gc_class,
+                                      "foreground", "I");
+  emacs_gc_background
+    = (*android_java_env)->GetFieldID (android_java_env,
+                                      emacs_gc_class,
+                                      "background", "I");
+  emacs_gc_function
+    = (*android_java_env)->GetFieldID (android_java_env,
+                                      emacs_gc_class,
+                                      "function", "I");
+  emacs_gc_clip_rects
+    = (*android_java_env)->GetFieldID (android_java_env,
+                                      emacs_gc_class,
+                                      "clip_rects",
+                                      "[Landroid/graphics/Rect;");
+  emacs_gc_clip_x_origin
+    = (*android_java_env)->GetFieldID (android_java_env,
+                                      emacs_gc_class,
+                                      "clip_x_origin", "I");
+  emacs_gc_clip_y_origin
+    = (*android_java_env)->GetFieldID (android_java_env,
+                                      emacs_gc_class,
+                                      "clip_y_origin", "I");
+  emacs_gc_stipple
+    = (*android_java_env)->GetFieldID (android_java_env,
+                                      emacs_gc_class,
+                                      "stipple",
+                                      "Lorg/gnu/emacs/EmacsPixmap;");
+  emacs_gc_clip_mask
+    = (*android_java_env)->GetFieldID (android_java_env,
+                                      emacs_gc_class,
+                                      "clip_mask",
+                                      "Lorg/gnu/emacs/EmacsPixmap;");
+  emacs_gc_fill_style
+    = (*android_java_env)->GetFieldID (android_java_env,
+                                      emacs_gc_class,
+                                      "fill_style", "I");
+  emacs_gc_ts_origin_x
+    = (*android_java_env)->GetFieldID (android_java_env,
+                                      emacs_gc_class,
+                                      "ts_origin_x", "I");
+  emacs_gc_ts_origin_y
+    = (*android_java_env)->GetFieldID (android_java_env,
+                                      emacs_gc_class,
+                                      "ts_origin_y", "I");
+}
+
+struct android_gc *
+android_create_gc (enum android_gc_value_mask mask,
+                  struct android_gc_values *values)
+{
+  struct android_gc *gc;
+  android_handle prev_max_handle;
+  jobject object;
+
+  android_init_emacs_gc_class ();
+
+  gc = xmalloc (sizeof *gc);
+  prev_max_handle = max_handle;
+  gc->gcontext = android_alloc_id ();
+  gc->foreground = 0;
+  gc->background = 0xffffff;
+  gc->clip_rects = NULL;
+
+  /* This means to not apply any clipping.  */
+  gc->num_clip_rects = -1;
+
+  /* Apply the other default values.  */
+  gc->function = ANDROID_GC_COPY;
+  gc->fill_style = ANDROID_FILL_SOLID;
+  gc->clip_x_origin = 0;
+  gc->clip_y_origin = 0;
+  gc->clip_mask = ANDROID_NONE;
+  gc->stipple = ANDROID_NONE;
+  gc->ts_x_origin = 0;
+  gc->ts_y_origin = 0;
+
+  if (!gc->gcontext)
+    {
+      xfree (gc);
+      error ("Out of GContext handles!");
+    }
+
+  object = (*android_java_env)->NewObject (android_java_env,
+                                          emacs_gc_class,
+                                          emacs_gc_constructor,
+                                          (jshort) gc->gcontext);
+
+  if (!object)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+
+      max_handle = prev_max_handle;
+      memory_full (0);
+    }
+
+  android_handles[gc->gcontext].type = ANDROID_HANDLE_GCONTEXT;
+  android_handles[gc->gcontext].handle
+    = (*android_java_env)->NewGlobalRef (android_java_env, object);
+  (*android_java_env)->ExceptionClear (android_java_env);
+  ANDROID_DELETE_LOCAL_REF (object);
+
+  if (!android_handles[gc->gcontext].handle)
+    memory_full (0);
+
+  android_change_gc (gc, mask, values);
+  return gc;
+}
+
+void
+android_free_gc (struct android_gc *gc)
+{
+  android_destroy_handle (gc->gcontext);
+
+  xfree (gc->clip_rects);
+  xfree (gc);
+}
+
+void
+android_change_gc (struct android_gc *gc,
+                  enum android_gc_value_mask mask,
+                  struct android_gc_values *values)
+{
+  jobject what, gcontext;
+  jboolean clip_changed;
+
+  clip_changed = false;
+
+  android_init_emacs_gc_class ();
+  gcontext = android_resolve_handle (gc->gcontext,
+                                    ANDROID_HANDLE_GCONTEXT);
+
+  if (mask & ANDROID_GC_FOREGROUND)
+    {
+      (*android_java_env)->SetIntField (android_java_env,
+                                       gcontext,
+                                       emacs_gc_foreground,
+                                       values->foreground);
+      gc->foreground = values->foreground;
+    }
+
+  if (mask & ANDROID_GC_BACKGROUND)
+    {
+      (*android_java_env)->SetIntField (android_java_env,
+                                       gcontext,
+                                       emacs_gc_background,
+                                       values->background);
+      gc->background = values->background;
+    }
+
+  if (mask & ANDROID_GC_FUNCTION)
+    {
+      (*android_java_env)->SetIntField (android_java_env,
+                                       gcontext,
+                                       emacs_gc_function,
+                                       values->function);
+      gc->function = values->function;
+    }
+
+  if (mask & ANDROID_GC_CLIP_X_ORIGIN)
+    {
+      (*android_java_env)->SetIntField (android_java_env,
+                                       gcontext,
+                                       emacs_gc_clip_x_origin,
+                                       values->clip_x_origin);
+      gc->clip_x_origin = values->clip_x_origin;
+      clip_changed = true;
+    }
+
+  if (mask & ANDROID_GC_CLIP_Y_ORIGIN)
+    {
+      (*android_java_env)->SetIntField (android_java_env,
+                                       gcontext,
+                                       emacs_gc_clip_y_origin,
+                                       values->clip_y_origin);
+      gc->clip_y_origin = values->clip_y_origin;
+      clip_changed = true;
+    }
+
+  if (mask & ANDROID_GC_CLIP_MASK)
+    {
+      what = android_resolve_handle (values->clip_mask,
+                                    ANDROID_HANDLE_PIXMAP);
+      (*android_java_env)->SetObjectField (android_java_env,
+                                          gcontext,
+                                          emacs_gc_clip_mask,
+                                          what);
+      gc->clip_mask = values->clip_mask;
+
+      /* Changing GCClipMask also clears the clip rectangles.  */
+      (*android_java_env)->SetObjectField (android_java_env,
+                                          gcontext,
+                                          emacs_gc_clip_rects,
+                                          NULL);
+
+      xfree (gc->clip_rects);
+      gc->clip_rects = NULL;
+      gc->num_clip_rects = -1;
+      clip_changed = true;
+    }
+
+  if (mask & ANDROID_GC_STIPPLE)
+    {
+      what = android_resolve_handle (values->stipple,
+                                    ANDROID_HANDLE_PIXMAP);
+      (*android_java_env)->SetObjectField (android_java_env,
+                                          gcontext,
+                                          emacs_gc_stipple,
+                                          what);
+      gc->stipple = values->stipple;
+    }
+
+  if (mask & ANDROID_GC_FILL_STYLE)
+    {
+      (*android_java_env)->SetIntField (android_java_env,
+                                       gcontext,
+                                       emacs_gc_fill_style,
+                                       values->fill_style);
+      gc->fill_style = values->fill_style;
+    }
+
+  if (mask & ANDROID_GC_TILE_STIP_X_ORIGIN)
+    {
+      (*android_java_env)->SetIntField (android_java_env,
+                                       gcontext,
+                                       emacs_gc_ts_origin_x,
+                                       values->ts_x_origin);
+      gc->ts_x_origin = values->ts_x_origin;
+    }
+
+  if (mask & ANDROID_GC_TILE_STIP_Y_ORIGIN)
+    {
+      (*android_java_env)->SetIntField (android_java_env,
+                                       gcontext,
+                                       emacs_gc_ts_origin_y,
+                                       values->ts_y_origin);
+      gc->ts_y_origin = values->ts_y_origin;
+    }
+
+  if (mask)
+    {
+      (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                    gcontext,
+                                                    emacs_gc_class,
+                                                    emacs_gc_mark_dirty,
+                                                    (jboolean) clip_changed);
+      android_exception_check ();
+    }
+}
+
+void
+android_set_clip_rectangles (struct android_gc *gc, int clip_x_origin,
+                            int clip_y_origin,
+                            struct android_rectangle *clip_rects,
+                            int n_clip_rects)
+{
+  jobjectArray array;
+  jobject rect, gcontext;
+  int i;
+
+  android_init_android_rect_class ();
+  android_init_emacs_gc_class ();
+
+  gcontext = android_resolve_handle (gc->gcontext,
+                                    ANDROID_HANDLE_GCONTEXT);
+
+  array = (*android_java_env)->NewObjectArray (android_java_env,
+                                              n_clip_rects,
+                                              android_rect_class,
+                                              NULL);
+  android_exception_check ();
+
+  for (i = 0; i < n_clip_rects; ++i)
+    {
+      rect = (*android_java_env)->NewObject (android_java_env,
+                                            android_rect_class,
+                                            android_rect_constructor,
+                                            (jint) clip_rects[i].x,
+                                            (jint) clip_rects[i].y,
+                                            (jint) (clip_rects[i].x
+                                                    + clip_rects[i].width),
+                                            (jint) (clip_rects[i].y
+                                                    + clip_rects[i].height));
+
+      /* The meaning of this call is to check whether or not an
+        allocation error happened, and to delete ARRAY and signal an
+        out-of-memory error if that is the case.  */
+      android_exception_check_1 (array);
+
+      (*android_java_env)->SetObjectArrayElement (android_java_env,
+                                                 array, i, rect);
+      ANDROID_DELETE_LOCAL_REF (rect);
+    }
+
+  (*android_java_env)->SetObjectField (android_java_env,
+                                      gcontext,
+                                      emacs_gc_clip_rects,
+                                      (jobject) array);
+  ANDROID_DELETE_LOCAL_REF (array);
+
+  (*android_java_env)->SetIntField (android_java_env,
+                                   gcontext,
+                                   emacs_gc_clip_x_origin,
+                                   clip_x_origin);
+  (*android_java_env)->SetIntField (android_java_env,
+                                   gcontext,
+                                   emacs_gc_clip_y_origin,
+                                   clip_y_origin);
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                gcontext,
+                                                emacs_gc_class,
+                                                emacs_gc_mark_dirty,
+                                                (jboolean) true);
+  android_exception_check ();
+
+  /* Cache the clip rectangles on the C side for
+     sfntfont-android.c.  */
+  if (gc->clip_rects)
+    xfree (gc->clip_rects);
+
+  /* If gc->num_clip_rects is 0, then no drawing will be performed at
+     all.  */
+  gc->clip_rects = xmalloc (sizeof *gc->clip_rects
+                           * n_clip_rects);
+  gc->num_clip_rects = n_clip_rects;
+  memcpy (gc->clip_rects, clip_rects,
+         n_clip_rects * sizeof *gc->clip_rects);
+}
+
+void
+android_reparent_window (android_window w, android_window parent_handle,
+                        int x, int y)
+{
+  jobject window, parent;
+  jmethodID method;
+
+  window = android_resolve_handle (w, ANDROID_HANDLE_WINDOW);
+  parent = android_resolve_handle (parent_handle,
+                                  ANDROID_HANDLE_WINDOW);
+
+  method = window_class.reparent_to;
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, window,
+                                                window_class.class, method,
+                                                parent, (jint) x, (jint) y);
+  android_exception_check ();
+}
+
+void
+android_clear_window (android_window handle)
+{
+  jobject window;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                emacs_service,
+                                                service_class.class,
+                                                service_class.clear_window,
+                                                window);
+  android_exception_check ();
+}
+
+void
+android_map_window (android_window handle)
+{
+  jobject window;
+  jmethodID map_window;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  map_window = window_class.map_window;
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                window,
+                                                window_class.class,
+                                                map_window);
+  android_exception_check ();
+}
+
+void
+android_unmap_window (android_window handle)
+{
+  jobject window;
+  jmethodID unmap_window;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  unmap_window = window_class.unmap_window;
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                window,
+                                                window_class.class,
+                                                unmap_window);
+  android_exception_check ();
+}
+
+void
+android_resize_window (android_window handle, unsigned int width,
+                      unsigned int height)
+{
+  jobject window;
+  jmethodID resize_window;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  resize_window = window_class.resize_window;
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                window,
+                                                window_class.class,
+                                                resize_window,
+                                                (jint) width,
+                                                (jint) height);
+  android_exception_check ();
+}
+
+void
+android_move_window (android_window handle, int x, int y)
+{
+  jobject window;
+  jmethodID move_window;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  move_window = window_class.move_window;
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                window,
+                                                window_class.class,
+                                                move_window,
+                                                (jint) x, (jint) y);
+  android_exception_check ();
+}
+
+void
+android_swap_buffers (struct android_swap_info *swap_info,
+                     int num_windows)
+{
+  jobject window;
+  int i;
+
+  for (i = 0; i < num_windows; ++i)
+    {
+      window = android_resolve_handle (swap_info[i].swap_window,
+                                      ANDROID_HANDLE_WINDOW);
+      (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                    window,
+                                                    window_class.class,
+                                                    window_class.swap_buffers);
+      android_exception_check ();
+    }
+}
+
+void
+android_get_gc_values (struct android_gc *gc,
+                      enum android_gc_value_mask mask,
+                      struct android_gc_values *values)
+{
+  if (mask & ANDROID_GC_FOREGROUND)
+    /* GCs never have 32 bit colors, so we don't have to worry about
+       sign extension here.  */
+    values->foreground = gc->foreground;
+
+  if (mask & ANDROID_GC_BACKGROUND)
+    values->background = gc->background;
+
+  if (mask & ANDROID_GC_FUNCTION)
+    values->function = gc->function;
+
+  if (mask & ANDROID_GC_CLIP_X_ORIGIN)
+    values->clip_x_origin = gc->clip_x_origin;
+
+  if (mask & ANDROID_GC_CLIP_Y_ORIGIN)
+    values->clip_y_origin = gc->clip_y_origin;
+
+  if (mask & ANDROID_GC_FILL_STYLE)
+    values->fill_style = gc->fill_style;
+
+  if (mask & ANDROID_GC_TILE_STIP_X_ORIGIN)
+    values->ts_x_origin = gc->ts_x_origin;
+
+  if (mask & ANDROID_GC_TILE_STIP_Y_ORIGIN)
+    values->ts_y_origin = gc->ts_y_origin;
+
+  /* Fields involving handles are not used by Emacs, and thus not
+     implemented */
+}
+
+void
+android_set_foreground (struct android_gc *gc, unsigned long foreground)
+{
+  struct android_gc_values gcv;
+
+  gcv.foreground = foreground;
+  android_change_gc (gc, ANDROID_GC_FOREGROUND, &gcv);
+}
+
+void
+android_fill_rectangle (android_drawable handle, struct android_gc *gc,
+                       int x, int y, unsigned int width,
+                       unsigned int height)
+{
+  jobject drawable, gcontext;
+
+  drawable = android_resolve_handle2 (handle,
+                                     ANDROID_HANDLE_WINDOW,
+                                     ANDROID_HANDLE_PIXMAP);
+  gcontext = android_resolve_handle (gc->gcontext,
+                                    ANDROID_HANDLE_GCONTEXT);
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                emacs_service,
+                                                service_class.class,
+                                                service_class.fill_rectangle,
+                                                drawable,
+                                                gcontext,
+                                                (jint) x, (jint) y,
+                                                (jint) width,
+                                                (jint) height);
+}
+
+android_pixmap
+android_create_pixmap_from_bitmap_data (char *data, unsigned int width,
+                                       unsigned int height,
+                                       unsigned long foreground,
+                                       unsigned long background,
+                                       unsigned int depth)
+{
+  android_handle prev_max_handle;
+  jobject object;
+  jintArray colors;
+  android_pixmap pixmap;
+  unsigned int x, y;
+  jint *region;
+
+  USE_SAFE_ALLOCA;
+
+  /* Create the color array holding the data.  */
+  colors = (*android_java_env)->NewIntArray (android_java_env,
+                                            width * height);
+  android_exception_check ();
+
+  SAFE_NALLOCA (region, sizeof *region, width);
+
+  for (y = 0; y < height; ++y)
+    {
+      for (x = 0; x < width; ++x)
+       {
+         if (depth == 24)
+           {
+             /* The alpha channels must be set, or otherwise, the
+                pixmap will be created entirely transparent.  */
+
+             if (data[x / 8] & (1 << (x % 8)))
+               region[x] = foreground | 0xff000000;
+             else
+               region[x] = background | 0xff000000;
+           }
+         else
+           {
+             if (data[x / 8] & (1 << (x % 8)))
+               region[x] = foreground;
+             else
+               region[x] = background;
+           }
+       }
+
+      (*android_java_env)->SetIntArrayRegion (android_java_env,
+                                             colors,
+                                             width * y, width,
+                                             region);
+      data += width / 8;
+    }
+
+  /* First, allocate the pixmap handle.  */
+  prev_max_handle = max_handle;
+  pixmap = android_alloc_id ();
+
+  if (!pixmap)
+    {
+      ANDROID_DELETE_LOCAL_REF ((jobject) colors);
+      error ("Out of pixmap handles!");
+    }
+
+  object = (*android_java_env)->NewObject (android_java_env,
+                                          pixmap_class.class,
+                                          pixmap_class.constructor,
+                                          (jshort) pixmap, colors,
+                                          (jint) width, (jint) height,
+                                          (jint) depth);
+  (*android_java_env)->ExceptionClear (android_java_env);
+  ANDROID_DELETE_LOCAL_REF ((jobject) colors);
+
+  if (!object)
+    {
+      max_handle = prev_max_handle;
+      memory_full (0);
+    }
+
+  android_handles[pixmap].type = ANDROID_HANDLE_PIXMAP;
+  android_handles[pixmap].handle
+    = (*android_java_env)->NewGlobalRef (android_java_env, object);
+  ANDROID_DELETE_LOCAL_REF (object);
+
+  if (!android_handles[pixmap].handle)
+    memory_full (0);
+
+  SAFE_FREE ();
+  return pixmap;
+}
+
+void
+android_set_clip_mask (struct android_gc *gc, android_pixmap pixmap)
+{
+  struct android_gc_values gcv;
+
+  gcv.clip_mask = pixmap;
+  android_change_gc (gc, ANDROID_GC_CLIP_MASK, &gcv);
+}
+
+void
+android_set_fill_style (struct android_gc *gc,
+                       enum android_fill_style fill_style)
+{
+  struct android_gc_values gcv;
+
+  gcv.fill_style = fill_style;
+  android_change_gc (gc, ANDROID_GC_FILL_STYLE, &gcv);
+}
+
+
+
+/* Pixmap bit blit implementation.  This exists as `Canvas.drawBitmap'
+   seems to have trouble with copying bitmap data from one bitmap back
+   to itself on Android 8.0.  */
+
+/* Function called to actually perform the copy.  */
+
+typedef void (*android_blit_func) (int, int, int, int, int, int,
+                                  struct android_gc *,
+                                  unsigned char *, AndroidBitmapInfo *,
+                                  unsigned char *, AndroidBitmapInfo *,
+                                  unsigned char *, AndroidBitmapInfo *);
+
+
+
+#ifdef __aarch64__
+
+/* Copy N pixels from SRC to DST, using MASK as a depth 1 clip
+   mask.  */
+
+static void
+android_neon_mask_line (unsigned int *src, unsigned int *dst,
+                       unsigned char *mask, int n)
+{
+  uint32x4_t src_low, src_high, dst_low, dst_high;
+  int16x8_t vmask;
+  int32x4_t ext_mask_low, ext_mask_high, low, high;
+  int rem, i;
+
+  /* Calculate the remainder.  */
+  rem = n & 7, n &= ~7;
+
+  /* Process eight pixels at a time.  */
+
+  if (n)
+    {
+    again:
+      /* Load the low and high four pixels from the source.  */
+      src_low = vld1q_u32 (src);
+      src_high = vld1q_u32 (src + 4);
+
+      /* Do the same with the destination.  */
+      dst_low = vld1q_u32 (dst);
+      dst_high = vld1q_u32 (dst + 4);
+
+      /* Load and sign extend the mask.  */
+      vmask = vmovl_s8 (vld1_u8 (mask));
+      ext_mask_low = vmovl_s16 (vget_low_s16 (vmask));
+      ext_mask_high = vmovl_s16 (vget_high_s16 (vmask));
+
+      /* Reinterpret the mask.  */
+      low = vreinterpretq_u32_s32 (ext_mask_low);
+      high = vreinterpretq_u32_s32 (ext_mask_high);
+
+      /* Apply the mask.  */
+      dst_low = vbicq_u32 (dst_low, low);
+      src_low = vandq_u32 (src_low, low);
+      dst_high = vbicq_u32 (dst_high, high);
+      src_high = vandq_u32 (src_high, high);
+
+      /* Write the result after combining both masked vectors.  */
+      vst1q_u32 (dst, vorrq_u32 (dst_low, src_low));
+      vst1q_u32 (dst + 4, vorrq_u32 (dst_high, src_high));
+
+      /* Adjust src, dst and mask.  */
+      dst += 8;
+      src += 8;
+      mask += 8;
+
+      /* See if this loop should continue.  */
+      n -= 8;
+      if (n > 0)
+       goto again;
+    }
+
+  /* Process the remaining pixels.  */
+
+  for (i = 0; i < rem; ++i)
+    {
+      /* Sign extend the mask.  */
+      n = ((signed char *) mask)[i];
+
+      /* Combine src and dst.  */
+      dst[i] = ((src[i] & n) | (dst[i] & ~n));
+    }
+}
+
+#endif /* __aarch64__ */
+
+
+
+/* Copy a rectangle SRC_X, SRC_Y, WIDTH and HEIGHT from SRC, described
+   by SRC_INFO, to DST_X and DST_Y in DST, as described by DST_INFO.
+
+   If MASK is set, mask the source data using MASK_INFO, translating
+   it by GC->clip_x_origin and GC->clip_y_origin.  MASK must be a
+   pixmap of depth 1.
+
+   N.B. that currently only copies between bitmaps of depth 24 are
+   implemented.  */
+
+static void
+android_blit_copy (int src_x, int src_y, int width, int height,
+                  int dst_x, int dst_y, struct android_gc *gc,
+                  unsigned char *src, AndroidBitmapInfo *src_info,
+                  unsigned char *dst, AndroidBitmapInfo *dst_info,
+                  unsigned char *mask, AndroidBitmapInfo *mask_info)
+{
+  uintptr_t start, end;
+  int mask_offset;
+  size_t pixel, offset, offset1;
+  unsigned char *src_current, *dst_current;
+  unsigned char *mask_current;
+  int overflow, temp, i;
+#ifndef __aarch64__
+  int j;
+#endif /* __aarch64__ */
+  bool backwards;
+  unsigned int *long_src, *long_dst;
+
+  /* Assert that the specified coordinates are within bounds.  */
+  eassert (src_x >= 0 && src_y >= 0
+          && dst_x >= 0 && dst_y >= 0);
+  eassert (src_x + width <= src_info->width);
+  eassert (src_y + height <= src_info->height);
+  eassert (dst_x + width <= dst_info->width);
+  eassert (dst_y + height <= dst_info->height);
+
+  /* Now check that each bitmap has the correct format.  */
+  eassert (src_info->format == dst_info->format
+          && src_info->format == ANDROID_BITMAP_FORMAT_RGBA_8888);
+  pixel = sizeof (unsigned int);
+
+  /* Android doesn't have A1 bitmaps, so A8 is used to represent
+     packed bitmaps of depth 1.  */
+  eassert (!mask || mask_info->format == ANDROID_BITMAP_FORMAT_A_8);
+
+  /* Calculate the address of the first pixel of the first row to be
+     copied in both src and dst.  Compare them to determine the
+     direction in which the copy is to take place.  */
+
+  overflow  = ckd_mul (&start, src_y, src_info->stride);
+  overflow |= ckd_mul (&end, src_x, pixel);
+  overflow |= ckd_add (&start, end, start);
+  overflow |= ckd_add (&start, (uintptr_t) src, start);
+
+  if (overflow)
+    return;
+
+  src_current = (unsigned char *) start;
+
+  overflow  = ckd_mul (&start, dst_y, dst_info->stride);
+  overflow |= ckd_mul (&end, dst_x, pixel);
+  overflow |= ckd_add (&start, end, start);
+  overflow |= ckd_add (&start, (uintptr_t) dst, start);
+
+  if (overflow)
+    return;
+
+  dst_current = (unsigned char *) start;
+  backwards = false;
+
+  /* Now see if copying should proceed from the bottom up.  */
+
+  if (src == dst && dst_current >= src_current)
+    {
+      backwards = true;
+
+      /* Walk src and dst from bottom to top, in order to avoid
+        overlap.  Calculate the coordinate of the last pixel of the
+        last row in both src and dst.  */
+
+      overflow = ckd_mul (&start, src_y + height - 1,
+                         src_info->stride);
+
+      if (mask)
+       /* If a mask is set, put the pointers before the end of the
+          row.  */
+       overflow |= ckd_mul (&end, src_x + width - 1, pixel);
+      else
+       end = src_x * pixel;
+
+      overflow |= ckd_add (&start, start, end);
+      overflow |= ckd_add (&start, (uintptr_t) src, start);
+
+      if (overflow)
+       return;
+
+      src_current = (unsigned char *) start;
+
+      overflow = ckd_mul (&start, dst_y + height - 1,
+                         dst_info->stride);
+
+      if (mask)
+       /* If a mask is set, put the pointers before the end of the
+          row.  */
+       overflow |= ckd_mul (&end, dst_x + width - 1, pixel);
+      else
+       end = dst_x * pixel;
+
+      overflow |= ckd_add (&start, start, end);
+      overflow |= ckd_add (&start, (uintptr_t) dst, start);
+
+      if (overflow)
+       return;
+
+      dst_current = (unsigned char *) start;
+    }
+
+  if (!mask)
+    {
+      /* Change the direction of the copy depending on how SRC and DST
+        overlap.  */
+
+      for (i = 0; i < height; ++i)
+       {
+         memmove (dst_current, src_current,
+                  width * pixel);
+
+         if (backwards)
+           {
+             /* Proceed to the last row.  */
+             src_current -= src_info->stride;
+             dst_current -= dst_info->stride;
+           }
+         else
+           {
+             /* Proceed to the next row.  */
+             src_current += src_info->stride;
+             dst_current += dst_info->stride;
+           }
+       }
+    }
+  else
+    {
+      /* Adjust the source and destination Y.  The start is MAX
+         (dst_y, gc->clip_y_origin); the difference between that value
+         and dst_y is the offset to apply to src_y. */
+
+      temp    = dst_y;
+      dst_y   = MAX (dst_y, gc->clip_y_origin);
+      src_y  += dst_y - temp;
+      height -= dst_y - temp;
+
+      /* Verify that the bounds are correct.  */
+      eassert (dst_y + height
+              <= gc->clip_y_origin + mask_info->height);
+      eassert (dst_y >= gc->clip_y_origin);
+
+      /* There is a mask.  For each scan line... */
+
+      if (backwards)
+       {
+         /* Calculate the number of pixels at the end of the
+            mask.  */
+
+         mask_offset  = dst_x + width;
+         mask_offset -= mask_info->width + gc->clip_x_origin;
+
+         if (mask_offset < 0)
+           mask_offset = 0;
+
+         /* Calculate the last column of the mask that will be
+            consulted.  */
+
+         temp = dst_x - gc->clip_x_origin;
+         temp += MIN (mask_info->width - temp,
+                      width - mask_offset);
+
+         if (temp < 0)
+           return;
+
+         /* Now calculate the last row of the mask that will be
+            consulted.  */
+         i = dst_y - gc->clip_y_origin + height;
+
+         /* Turn both into offsets.  */
+
+         if (INT_MULTIPLY_WRAPV (temp, pixel, &offset)
+             || INT_MULTIPLY_WRAPV (i, mask_info->stride, &offset1)
+             || INT_ADD_WRAPV (offset, offset1, &offset)
+             || INT_ADD_WRAPV ((uintptr_t) mask, offset, &start))
+           return;
+
+         if (height <= 0)
+           return;
+
+         mask = mask_current = (unsigned char *) start;
+
+         while (height--)
+           {
+             /* Skip backwards past the end of the mask.  */
+
+             long_src = (unsigned int *) (src_current - mask_offset * pixel);
+             long_dst = (unsigned int *) (dst_current - mask_offset * pixel);
+             mask = mask_current;
+
+             /* For each pixel covered by the mask... */
+             temp = MIN (mask_info->width - temp, width - mask_offset);
+             while (temp--)
+               {
+                 /* Copy the destination it to the source, masked by
+                    the mask.  */
+
+                 /* Sign extend the mask.  */
+                 i = *(signed char *) mask--;
+
+                 /* Apply the mask.  */
+                 *long_dst = ((*long_src & i) | (*long_dst & ~i));
+
+                 long_dst--;
+                 long_src--;
+               }
+
+             /* Return to the last row.  */
+             src_current -= src_info->stride;
+             dst_current -= dst_info->stride;
+             mask_current -= mask_info->stride;
+           }
+       }
+      else
+       {
+         /* Calculate the first column of the mask that will be
+            consulted.  */
+
+         mask_offset = dst_x - gc->clip_x_origin;
+
+         /* Adjust the mask by that much.  */
+
+         if (mask_offset > 0)
+           mask += mask_offset;
+         else
+           {
+             /* Offset src and dst by the mask offset.  */
+             src_current += -mask_offset * pixel;
+             dst_current += -mask_offset * pixel;
+             width += mask_offset;
+           }
+
+         /* Make sure it's not out of bounds.  */
+
+         eassert (dst_y - gc->clip_y_origin >= 0);
+         if ((dst_y - gc->clip_y_origin) + height > mask_info->height
+             || width <= 0)
+           return;
+
+         /* Now move mask to the position of the first row.  */
+
+         mask += ((dst_y - gc->clip_y_origin)
+                  * mask_info->stride);
+
+         /* Determine how many bytes need to be copied.  */
+
+         if (mask_offset > 0)
+           temp = MIN (mask_info->width - mask_offset, width);
+         else
+           temp = MIN (mask_info->width, width);
+
+         if (temp <= 0 || height <= 0)
+           return;
+
+         /* Copy bytes according to the mask.  */
+
+         while (height--)
+           {
+             long_src = (unsigned int *) src_current;
+             long_dst = (unsigned int *) dst_current;
+             mask_current = mask;
+
+#ifndef __aarch64__
+             for (j = 0; j < temp; ++j)
+               {
+                 /* Sign extend the mask.  */
+                 i = *(signed char *) mask_current++;
+
+                 /* Apply the mask.  */
+                 *long_dst = ((*long_src & i) | (*long_dst & ~i));
+                 long_dst++;
+                 long_src++;
+               }
+#else /* __aarch64__ */
+             android_neon_mask_line (long_src, long_dst, mask, temp);
+#endif /* __aarch64__ */
+
+             src_current += src_info->stride;
+             dst_current += dst_info->stride;
+             mask        += mask_info->stride;
+           }
+       }
+    }
+}
+
+
+/* Xor a rectangle SRC_X, SRC_Y, WIDTH and HEIGHT from SRC, described
+   by SRC_INFO, to DST_X and DST_Y in DST, as described by DST_INFO.
+
+   Ignore the alpha channel when computing the exclusive-or of the
+   destination pixel.
+
+   If MASK is set, mask the source data using MASK_INFO, translating
+   it by GC->clip_x_origin and GC->clip_y_origin.  MASK must be a
+   pixmap of depth 1.
+
+   N.B. that currently only copies between bitmaps of depth 24 are
+   implemented.  */
+
+static void
+android_blit_xor (int src_x, int src_y, int width, int height,
+                 int dst_x, int dst_y, struct android_gc *gc,
+                 unsigned char *src, AndroidBitmapInfo *src_info,
+                 unsigned char *dst, AndroidBitmapInfo *dst_info,
+                 unsigned char *mask, AndroidBitmapInfo *mask_info)
+{
+#if 0
+  uintptr_t start, end;
+  int mask_offset;
+  size_t pixel, offset, offset1;
+  unsigned char *src_current, *dst_current;
+  unsigned char *mask_current;
+  int overflow, temp, i;
+  bool backwards;
+  unsigned int *long_src, *long_dst;
+#endif /* 0 */
+
+  /* Note that this alu hasn't been tested -- it probably does not
+     work! */
+  emacs_abort ();
+
+#if 0
+  /* Assert that the specified coordinates are within bounds.  */
+  eassert (src_x >= 0 && src_y >= 0
+          && dst_x >= 0 && dst_y >= 0);
+  eassert (src_x + width <= src_info->width);
+  eassert (src_y + height <= src_info->height);
+  eassert (dst_x + width <= dst_info->width);
+  eassert (dst_y + height <= dst_info->height);
+
+  /* Now check that each bitmap has the correct format.  */
+  eassert (src_info->format == dst_info->format
+          && src_info->format == ANDROID_BITMAP_FORMAT_RGBA_8888);
+  pixel = sizeof (unsigned int);
+
+  /* Android doesn't have A1 bitmaps, so A8 is used to represent
+     packed bitmaps of depth 1.  */
+  eassert (!mask || mask_info->format == ANDROID_BITMAP_FORMAT_A_8);
+
+  /* Calculate the address of the first pixel of the first row to be
+     copied in both src and dst.  Compare them to determine the
+     direction in which the copy is to take place.  */
+
+  overflow  = ckd_mul (&start, src_y, src_info->stride);
+  overflow |= ckd_mul (&end, src_x, pixel);
+  overflow |= ckd_add (&start, (uintptr_t) src, start);
+
+  if (overflow)
+    return;
+
+  src_current = (unsigned char *) start;
+
+  overflow  = ckd_mul (&start, dst_y, src_info->stride);
+  overflow |= ckd_mul (&end, dst_x, pixel);
+  overflow |= ckd_add (&start, (uintptr_t) dst, start);
+
+  if (overflow)
+    return;
+
+  dst_current = (unsigned char *) start;
+  backwards = false;
+
+  /* Now see if copying should proceed from the bottom up.  */
+
+  if (src == dst && dst_current >= src_current)
+    {
+      backwards = true;
+
+      /* Walk src and dst from bottom to top, in order to avoid
+        overlap.  Calculate the coordinate of the last pixel of the
+        last row in both src and dst.  */
+
+      overflow  = ckd_mul (&start, src_y + height - 1,
+                          src_info->stride);
+      if (mask) /* If a mask is set, put the pointers before the end
+                  of the row.  */
+       overflow |= ckd_mul (&end, src_x + width - 1, pixel);
+      else
+       overflow |= ckd_mul (&end, src_x, pixel);
+      overflow |= ckd_add (&start, start, end);
+      overflow |= ckd_add (&start, (uintptr_t) src, start);
+
+      if (overflow)
+       return;
+
+      src_current = (unsigned char *) start;
+
+      overflow  = ckd_mul (&start, dst_y + height - 1,
+                          dst_info->stride);
+      if (mask) /* If a mask is set, put the pointers before the end
+                  of the row.  */
+       overflow |= ckd_mul (&end, dst_x + width - 1, pixel);
+      else
+       overflow |= ckd_mul (&end, dst_x, pixel);
+      overflow |= ckd_add (&start, start, end);
+      overflow |= ckd_add (&start, (uintptr_t) dst, start);
+
+      if (overflow)
+       return;
+
+      dst_current = (unsigned char *) start;
+    }
+
+  if (!mask)
+    {
+      /* Change the direction of the copy depending on how SRC and DST
+        overlap.  */
+
+      for (i = 0; i < height; ++i)
+       {
+         if (backwards)
+           {
+             for (i = width - 1; i <= 0; --i)
+               (((unsigned int *) dst_current)[i])
+                 /* Keep the alpha channel intact.  */
+                 ^= (((unsigned int *) src_current)[i]) & 0xffffff;
+
+             /* Proceed to the last row.  */
+             src_current -= src_info->stride;
+             dst_current -= dst_info->stride;
+           }
+         else
+           {
+             for (i = 0; i < width; ++i)
+               (((unsigned int *) dst_current)[i])
+                 /* Keep the alpha channel intact.  */
+                 ^= (((unsigned int *) src_current)[i]) & 0xffffff;
+
+             /* Proceed to the next row.  */
+             src_current += src_info->stride;
+             dst_current += dst_info->stride;
+           }
+       }
+    }
+  else
+    {
+      /* Adjust the source and destination Y.  The start is MAX
+         (dst_y, gc->clip_y_origin); the difference between that value
+         and dst_y is the offset to apply to src_y. */
+
+      temp    = dst_y;
+      dst_y   = MAX (dst_y, gc->clip_y_origin);
+      src_y  += dst_y - temp;
+      height -= dst_y - temp;
+
+      /* Verify that the bounds are correct.  */
+      eassert (dst_y + height
+              <= gc->clip_y_origin + mask_info->height);
+      eassert (dst_y >= gc->clip_y_origin);
+
+      /* There is a mask.  For each scan line... */
+
+      if (backwards)
+       {
+         /* Calculate the number of pixels at the end of the
+            mask.  */
+
+         mask_offset  = dst_x + width;
+         mask_offset -= mask_info->width + gc->clip_x_origin;
+
+         if (mask_info < 0)
+           mask_info = 0;
+
+         /* Calculate the last column of the mask that will be
+            consulted.  */
+
+         temp = dst_x - gc->clip_x_origin;
+         temp += MIN (mask_info->width - temp,
+                      width - mask_offset);
+
+         if (temp < 0)
+           return;
+
+         /* Now calculate the last row of the mask that will be
+            consulted.  */
+         i = dst_y - gc->clip_y_origin + height;
+
+         /* Turn both into offsets.  */
+
+         if (INT_MULTIPLY_WRAPV (temp, pixel, &offset)
+             || INT_MULTIPLY_WRAPV (i, mask_info->stride, &offset1)
+             || INT_ADD_WRAPV (offset, offset1, &offset)
+             || INT_ADD_WRAPV ((uintptr_t) mask, offset, &start))
+           return;
+
+         mask = mask_current = (unsigned char *) start;
+
+         for (i = 0; i < height; ++i)
+           {
+             /* Skip backwards past the end of the mask.  */
+
+             long_src = (unsigned int *) (src_current - mask_offset * pixel);
+             long_dst = (unsigned int *) (dst_current - mask_offset * pixel);
+             mask = mask_current;
+
+             /* For each pixel covered by the mask... */
+             temp = MIN (mask_info->width - temp, width - mask_offset);
+             while (temp--)
+               /* XOR the source to the destination, masked by the
+                  mask.  */
+               *long_dst-- ^= ((*(long_src--) & (0u - (*(mask--) & 1)))
+                               & 0xffffff);
+
+             /* Return to the last row.  */
+             src_current -= src_info->stride;
+             dst_current -= dst_info->stride;
+             mask_current -= mask_info->stride;
+           }
+       }
+      else
+       {
+         /* Calculate the first column of the mask that will be
+            consulted.  */
+
+         mask_offset = dst_x - gc->clip_x_origin;
+
+         /* Adjust the mask by that much.  */
+
+         if (mask_offset > 0)
+           mask += mask_offset;
+         else
+           {
+             /* Offset src and dst by the mask offset.  */
+             src_current += -mask_offset * pixel;
+             dst_current += -mask_offset * pixel;
+             width -= mask_offset;
+           }
+
+         /* Now move mask to the position of the first row.  */
+
+         mask += gc->clip_y_origin * mask_info->stride;
+
+         for (i = 0; i < height; ++i)
+           {
+             long_src = (unsigned int *) src_current;
+             long_dst = (unsigned int *) dst_current;
+             mask_current = mask;
+
+             if (mask_offset > 0)
+               {
+                 /* Copy bytes according to the mask.  */
+                 temp = MIN (mask_info->width - mask_offset, width);
+                 while (temp--)
+                   *long_dst++ ^= ((*(long_src++)
+                                    & (0u - (*(mask_current++) & 1)))
+                                   & 0xffffff);
+               }
+             else
+               {
+                 /* Copy bytes according to the mask.  */
+                 temp = MIN (mask_info->width, width);
+                 while (temp--)
+                   *long_dst++ = ((*(long_src++)
+                                   & (0u - (*(mask_current++) & 1)))
+                                  & 0xffffff);
+               }
+
+             src_current += src_info->stride;
+             dst_current += dst_info->stride;
+             mask        += mask_info->stride;
+           }
+       }
+    }
+#endif /* 0 */
+}
+
+void
+android_copy_area (android_drawable src, android_drawable dest,
+                  struct android_gc *gc, int src_x, int src_y,
+                  unsigned int width, unsigned int height,
+                  int dest_x, int dest_y)
+{
+  jobject src_object, dest_object, mask;
+  android_blit_func do_blit;
+  AndroidBitmapInfo src_info, dest_info, mask_info;
+  void *src_data, *dest_data, *mask_data;
+  int n_clip_rects, i;
+  bool flag;
+  struct android_rectangle bounds, rect, temp, *clip_rectangles;
+
+  /* Perform the copy.  Loop over each clip rectangle, unless none are
+     set.  Also, obtain bitmaps for src and dst, and possibly the mask
+     as well if it is present.  */
+
+  src_data = android_lock_bitmap (src, &src_info, &src_object);
+  if (!src_data)
+    return;
+
+  mask_data = mask = NULL;
+
+  if (src != dest)
+    {
+      dest_data = android_lock_bitmap (dest, &dest_info, &dest_object);
+      if (!dest_data)
+       goto fail;
+    }
+  else
+    {
+      dest_data = src_data;
+      dest_info = src_info;
+    }
+
+  /* Obtain the bitmap for the mask if necessary.  */
+
+  if (gc->clip_mask)
+    {
+      mask_data = android_lock_bitmap (gc->clip_mask,
+                                      &mask_info, &mask);
+      if (!mask_data)
+       goto fail1;
+    }
+
+  /* Calculate the number of clip rectangles.  */
+  n_clip_rects = gc->num_clip_rects;
+
+  /* If n_clip_rects is -1, then no clipping is in effect.  Set rect
+     to the bounds of the destination.  */
+
+  flag = n_clip_rects == -1;
+  if (flag)
+    {
+      n_clip_rects = 1;
+      clip_rectangles = &rect;
+    }
+  else if (!n_clip_rects)
+    goto fail2;
+  else
+    clip_rectangles = gc->clip_rects;
+
+  /* Set rect to the bounds of the destination.  */
+
+  rect.x = 0;
+  rect.y = 0;
+  rect.width = dest_info.width;
+  rect.height = dest_info.height;
+
+  if (mask_data)
+    {
+      /* Clip width and height to that of the mask.  */
+
+      if (src_x + width > mask_info.width)
+       width = mask_info.width - src_x;
+
+      if (src_y + height > mask_info.height)
+       height = mask_info.height - src_y;
+    }
+
+  /* Clip width and height to that of the source.  */
+
+  if (src_x + width > src_info.width)
+    width = src_info.width - src_x;
+
+  if (src_y + height > src_info.height)
+    height = src_info.height - src_y;
+
+  /* Return if the copy is outside the source.  */
+
+  if (width <= 0 || height <= 0)
+    goto fail2;
+
+  /* Look up the right function for the alu.  */
+
+  switch (gc->function)
+    {
+    case ANDROID_GC_COPY:
+      do_blit = android_blit_copy;
+      break;
+
+    case ANDROID_GC_XOR:
+      do_blit = android_blit_xor;
+      break;
+
+    default:
+      emacs_abort ();
+    }
+
+  /* Load the bounds of the destination rectangle.  */
+  bounds.x = dest_x;
+  bounds.y = dest_y;
+  bounds.width = width;
+  bounds.height = height;
+
+  /* For each clip rectangle... */
+  for (i = 0; i < n_clip_rects; ++i)
+    {
+      /* Calculate its intersection with the destination
+        rectangle.  */
+
+      if (!gui_intersect_rectangles (&clip_rectangles[i], &bounds,
+                                    &temp))
+       continue;
+
+      /* And that of the destination itself.  */
+
+      if (!flag && !gui_intersect_rectangles (&temp, &rect, &temp))
+       continue;
+
+      /* Now perform the copy.  */
+      (*do_blit) (src_x + temp.x - dest_x,     /* temp.x relative to src_x */
+                 src_y + temp.y - dest_y,      /* temp.y relative to src_y */
+                 temp.width,                   /* Width of area to copy.  */
+                 temp.height,                  /* Height of area to copy.  */
+                 temp.x, temp.y,               /* Coordinates to copy to.  */
+                 gc,                           /* GC.  */
+                 src_data, &src_info,          /* Source drawable.  */
+                 dest_data, &dest_info,        /* Destination drawable.  */
+                 mask_data, &mask_info);       /* Mask drawable.  */
+    }
+
+  /* Now damage the destination drawable accordingly, should it be a
+     window.  */
+
+  if (android_handles[dest].type == ANDROID_HANDLE_WINDOW)
+    android_damage_window (dest, &bounds);
+
+ fail2:
+  if (mask)
+    {
+      AndroidBitmap_unlockPixels (android_java_env, mask);
+      ANDROID_DELETE_LOCAL_REF (mask);
+    }
+ fail1:
+  if (src != dest)
+    {
+      AndroidBitmap_unlockPixels (android_java_env, dest_object);
+      ANDROID_DELETE_LOCAL_REF (dest_object);
+    }
+ fail:
+  AndroidBitmap_unlockPixels (android_java_env, src_object);
+  ANDROID_DELETE_LOCAL_REF (src_object);
+}
+
+
+
+void
+android_free_pixmap (android_pixmap pixmap)
+{
+  android_destroy_handle (pixmap);
+}
+
+void
+android_set_background (struct android_gc *gc, unsigned long background)
+{
+  struct android_gc_values gcv;
+
+  gcv.background = background;
+  android_change_gc (gc, ANDROID_GC_BACKGROUND, &gcv);
+}
+
+void
+android_fill_polygon (android_drawable drawable, struct android_gc *gc,
+                     struct android_point *points, int npoints,
+                     enum android_shape shape, enum android_coord_mode mode)
+{
+  jobjectArray array;
+  jobject point, drawable_object, gcontext;
+  int i;
+
+  drawable_object = android_resolve_handle2 (drawable,
+                                            ANDROID_HANDLE_WINDOW,
+                                            ANDROID_HANDLE_PIXMAP);
+  gcontext = android_resolve_handle (gc->gcontext,
+                                    ANDROID_HANDLE_GCONTEXT);
+
+  array = (*android_java_env)->NewObjectArray (android_java_env,
+                                              npoints,
+                                              point_class.class,
+                                              NULL);
+  android_exception_check ();
+
+  for (i = 0; i < npoints; ++i)
+    {
+      point = (*android_java_env)->NewObject (android_java_env,
+                                             point_class.class,
+                                             point_class.constructor,
+                                             (jint) points[i].x,
+                                             (jint) points[i].y);
+      android_exception_check_1 (array);
+
+      (*android_java_env)->SetObjectArrayElement (android_java_env,
+                                                 array, i, point);
+      ANDROID_DELETE_LOCAL_REF (point);
+    }
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                emacs_service,
+                                                service_class.class,
+                                                service_class.fill_polygon,
+                                                drawable_object,
+                                                gcontext, array);
+  ANDROID_DELETE_LOCAL_REF (array);
+}
+
+void
+android_draw_rectangle (android_drawable handle, struct android_gc *gc,
+                       int x, int y, unsigned int width, unsigned int height)
+{
+  jobject drawable, gcontext;
+
+  drawable = android_resolve_handle2 (handle,
+                                     ANDROID_HANDLE_WINDOW,
+                                     ANDROID_HANDLE_PIXMAP);
+  gcontext = android_resolve_handle (gc->gcontext,
+                                    ANDROID_HANDLE_GCONTEXT);
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                emacs_service,
+                                                service_class.class,
+                                                service_class.draw_rectangle,
+                                                drawable, gcontext,
+                                                (jint) x, (jint) y,
+                                                (jint) width, (jint) height);
+}
+
+void
+android_draw_point (android_drawable handle, struct android_gc *gc,
+                   int x, int y)
+{
+  jobject drawable, gcontext;
+
+  drawable = android_resolve_handle2 (handle,
+                                     ANDROID_HANDLE_WINDOW,
+                                     ANDROID_HANDLE_PIXMAP);
+  gcontext = android_resolve_handle (gc->gcontext,
+                                    ANDROID_HANDLE_GCONTEXT);
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                emacs_service,
+                                                service_class.class,
+                                                service_class.draw_point,
+                                                drawable, gcontext,
+                                                (jint) x, (jint) y);
+}
+
+void
+android_draw_line (android_drawable handle, struct android_gc *gc,
+                  int x, int y, int x2, int y2)
+{
+  jobject drawable, gcontext;
+
+  drawable = android_resolve_handle2 (handle,
+                                     ANDROID_HANDLE_WINDOW,
+                                     ANDROID_HANDLE_PIXMAP);
+  gcontext = android_resolve_handle (gc->gcontext,
+                                    ANDROID_HANDLE_GCONTEXT);
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                emacs_service,
+                                                service_class.class,
+                                                service_class.draw_line,
+                                                drawable, gcontext,
+                                                (jint) x, (jint) y,
+                                                (jint) x2, (jint) y2);
+}
+
+android_pixmap
+android_create_pixmap (unsigned int width, unsigned int height,
+                      int depth)
+{
+  android_handle prev_max_handle;
+  jobject object;
+  android_pixmap pixmap;
+
+  /* First, allocate the pixmap handle.  */
+  prev_max_handle = max_handle;
+  pixmap = android_alloc_id ();
+
+  if (!pixmap)
+    error ("Out of pixmap handles!");
+
+  object = (*android_java_env)->NewObject (android_java_env,
+                                          pixmap_class.class,
+                                          pixmap_class.constructor_mutable,
+                                          (jshort) pixmap,
+                                          (jint) width, (jint) height,
+                                          (jint) depth);
+
+  if (!object)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      max_handle = prev_max_handle;
+      memory_full (0);
+    }
+
+  android_handles[pixmap].type = ANDROID_HANDLE_PIXMAP;
+  android_handles[pixmap].handle
+    = (*android_java_env)->NewGlobalRef (android_java_env, object);
+  (*android_java_env)->ExceptionClear (android_java_env);
+  ANDROID_DELETE_LOCAL_REF (object);
+
+  if (!android_handles[pixmap].handle)
+    memory_full (0);
+
+  return pixmap;
+}
+
+void
+android_set_ts_origin (struct android_gc *gc, int x, int y)
+{
+  struct android_gc_values gcv;
+
+  gcv.ts_x_origin = x;
+  gcv.ts_y_origin = y;
+  android_change_gc (gc, (ANDROID_GC_TILE_STIP_X_ORIGIN
+                         | ANDROID_GC_TILE_STIP_Y_ORIGIN),
+                    &gcv);
+}
+
+void
+android_clear_area (android_window handle, int x, int y,
+                   unsigned int width, unsigned int height)
+{
+  jobject window;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                emacs_service,
+                                                service_class.class,
+                                                service_class.clear_area,
+                                                window, (jint) x, (jint) y,
+                                                (jint) width, (jint) height);
+}
+
+android_pixmap
+android_create_bitmap_from_data (char *bits, unsigned int width,
+                                unsigned int height)
+{
+  return android_create_pixmap_from_bitmap_data (bits, 1, 0,
+                                                width, height, 1);
+}
+
+struct android_image *
+android_create_image (unsigned int depth, enum android_image_format format,
+                     char *data, unsigned int width, unsigned int height)
+{
+  struct android_image *image;
+
+  image = xmalloc (sizeof *image);
+
+  /* Fill in the fields required by image.c.  N.B. that
+     android_destroy_image ostensibly will free data, but image.c
+     mostly sets and frees data itself.  */
+  image->width = width;
+  image->height = height;
+  image->data = data;
+  image->depth = depth;
+  image->format = format;
+
+  /* Now fill in the image dimensions.  There are only two depths
+     supported by this function.  */
+
+  if (depth == 1)
+    {
+      image->bytes_per_line = (width + 7) / 8;
+      image->bits_per_pixel = 1;
+    }
+  else if (depth == 24)
+    {
+      image->bytes_per_line = width * 4;
+      image->bits_per_pixel = 32;
+    }
+  else
+    emacs_abort ();
+
+  return image;
+}
+
+void
+android_destroy_image (struct android_image *ximg)
+{
+  /* If XIMG->data is NULL, then it has already been freed by
+     image.c.  */
+
+  if (ximg->data)
+    xfree (ximg->data);
+  xfree (ximg);
+}
+
+void
+android_put_pixel (struct android_image *ximg, int x, int y,
+                  unsigned long pixel)
+{
+  char *byte, *word;
+  unsigned int r, g, b;
+  unsigned int pixel_int;
+
+  /* Ignore out-of-bounds accesses.  */
+
+  if (x >= ximg->width || y >= ximg->height || x < 0 || y < 0)
+    return;
+
+  switch (ximg->depth)
+    {
+    case 1:
+      byte = ximg->data + y * ximg->bytes_per_line + x / 8;
+
+      if (pixel)
+       *byte |= (1 << x % 8);
+      else
+       *byte &= ~(1 << x % 8);
+      break;
+
+    case 24:
+      /* Unaligned accesses are problematic on Android devices.  */
+      word = ximg->data + y * ximg->bytes_per_line + x * 4;
+
+      /* Swizzle the pixel into ABGR format.  Android uses Skia's
+        ``native color type'', which is ABGR.  This is despite the
+        format being named ``ARGB'', and more confusingly
+        `ANDROID_BITMAP_FORMAT_RGBA_8888' in bitmap.h.  */
+      r = pixel & 0x00ff0000;
+      g = pixel & 0x0000ff00;
+      b = pixel & 0x000000ff;
+      pixel = (r >> 16) | g | (b << 16) | 0xff000000;
+
+      pixel_int = pixel;
+      memcpy (word, &pixel_int, sizeof pixel_int);
+      break;
+    }
+}
+
+unsigned long
+android_get_pixel (struct android_image *ximg, int x, int y)
+{
+  char *byte, *word;
+  unsigned int pixel, r, g, b;
+
+  if (x >= ximg->width || y >= ximg->height
+      || x < 0 || y < 0)
+    return 0;
+
+  switch (ximg->depth)
+    {
+    case 1:
+      byte = ximg->data + y * ximg->bytes_per_line + x / 8;
+      return (*byte & (1 << x % 8)) ? 1 : 0;
+
+    case 24:
+      word = ximg->data + y * ximg->bytes_per_line + x * 4;
+      memcpy (&pixel, word, sizeof pixel);
+
+      /* Convert the pixel back to RGB.  */
+      b = pixel & 0x00ff0000;
+      g = pixel & 0x0000ff00;
+      r = pixel & 0x000000ff;
+      pixel = ((r << 16) | g | (b >> 16)) & ~0xff000000;
+
+      return pixel;
+    }
+
+  emacs_abort ();
+}
+
+struct android_image *
+android_get_image (android_drawable handle,
+                  enum android_image_format format)
+{
+  jobject drawable, bitmap;
+  AndroidBitmapInfo bitmap_info;
+  size_t byte_size;
+  void *data;
+  struct android_image *image;
+  unsigned char *data1, *data2;
+  int i, x;
+
+  drawable = android_resolve_handle2 (handle, ANDROID_HANDLE_WINDOW,
+                                     ANDROID_HANDLE_PIXMAP);
+
+  /* Look up the drawable and get the bitmap corresponding to it.
+     Then, lock the bitmap's bits.  */
+  bitmap = (*android_java_env)->CallObjectMethod (android_java_env,
+                                                 drawable,
+                                                 drawable_class.get_bitmap);
+  android_exception_check ();
+
+  /* Clear the bitmap info structure.  */
+  memset (&bitmap_info, 0, sizeof bitmap_info);
+
+  /* The NDK doc seems to imply this function can fail but doesn't say
+     what value it gives when it does! */
+  AndroidBitmap_getInfo (android_java_env, bitmap, &bitmap_info);
+
+  if (!bitmap_info.stride)
+    {
+      ANDROID_DELETE_LOCAL_REF (bitmap);
+      memory_full (0);
+    }
+
+  /* Compute how big the image data will be.  Fail if it would be too
+     big.  */
+
+  if (bitmap_info.format != ANDROID_BITMAP_FORMAT_A_8)
+    {
+      if (INT_MULTIPLY_WRAPV ((size_t) bitmap_info.stride,
+                             (size_t) bitmap_info.height,
+                             &byte_size))
+       {
+         ANDROID_DELETE_LOCAL_REF (bitmap);
+         memory_full (0);
+       }
+    }
+  else
+    /* This A8 image will be packed into A1 later on.  */
+    byte_size = (bitmap_info.width + 7) / 8;
+
+  /* Lock the image data.  Once again, the NDK documentation says the
+     call can fail, but does not say how to determine whether or not
+     it has failed, nor how the address is aligned.  */
+  data = NULL;
+  AndroidBitmap_lockPixels (android_java_env, bitmap, &data);
+
+  if (!data)
+    {
+      /* Take a NULL pointer to mean that AndroidBitmap_lockPixels
+        failed.  */
+      ANDROID_DELETE_LOCAL_REF (bitmap);
+      memory_full (0);
+    }
+
+  /* Copy the data into a new struct android_image.  */
+  image = xmalloc (sizeof *image);
+  image->width = bitmap_info.width;
+  image->height = bitmap_info.height;
+  image->data = malloc (byte_size);
+
+  if (!image->data)
+    {
+      ANDROID_DELETE_LOCAL_REF (bitmap);
+      xfree (image);
+      memory_full (byte_size);
+    }
+
+  /* Use the format of the bitmap to determine the image depth.  */
+  switch (bitmap_info.format)
+    {
+    case ANDROID_BITMAP_FORMAT_RGBA_8888:
+      image->depth = 24;
+      image->bits_per_pixel = 32;
+      break;
+
+      /* A8 images are used by Emacs to represent bitmaps.  They have
+        to be packed manually.  */
+    case ANDROID_BITMAP_FORMAT_A_8:
+      image->depth = 1;
+      image->bits_per_pixel = 1;
+      break;
+
+      /* Other formats are currently not supported.  */
+    default:
+      emacs_abort ();
+    }
+
+  image->format = format;
+
+  if (image->depth == 24)
+    {
+      image->bytes_per_line = bitmap_info.stride;
+
+      /* Copy the bitmap data over.  */
+      memcpy (image->data, data, byte_size);
+    }
+  else
+    {
+      /* Pack the A8 image data into bits manually.  */
+      image->bytes_per_line = (image->width + 7) / 8;
+
+      data1 = (unsigned char *) image->data;
+      data2 = data;
+
+      for (i = 0; i < image->height; ++i)
+       {
+         for (x = 0; x < image->width; ++x)
+           /* Some bits in data1 might be initialized at this point,
+              but they will all be set properly later.  */
+           data1[x / 8] = (data2[x]
+                           ? (data1[x / 8] | (1 << (x % 8)))
+                           : (data1[x / 8] & ~(1 << (x % 8))));
+
+         data1 += image->bytes_per_line;
+         data2 += bitmap_info.stride;
+       }
+    }
+
+  /* Unlock the bitmap pixels.  */
+  AndroidBitmap_unlockPixels (android_java_env, bitmap);
+
+  /* Delete the bitmap reference.  */
+  ANDROID_DELETE_LOCAL_REF (bitmap);
+  return image;
+}
+
+void
+android_put_image (android_pixmap handle, struct android_image *image)
+{
+  jobject drawable, bitmap;
+  AndroidBitmapInfo bitmap_info;
+  void *data;
+  unsigned char *data_1, *data_2;
+  int i, x;
+
+  drawable = android_resolve_handle (handle, ANDROID_HANDLE_PIXMAP);
+
+  /* Look up the drawable and get the bitmap corresponding to it.
+     Then, lock the bitmap's bits.  */
+  bitmap = (*android_java_env)->CallObjectMethod (android_java_env,
+                                                 drawable,
+                                                 drawable_class.get_bitmap);
+  android_exception_check ();
+
+  /* Clear the bitmap info structure.  */
+  memset (&bitmap_info, 0, sizeof bitmap_info);
+
+  /* The NDK doc seems to imply this function can fail but doesn't say
+     what value it gives when it does! */
+  AndroidBitmap_getInfo (android_java_env, bitmap, &bitmap_info);
+
+  if (!bitmap_info.stride)
+    {
+      ANDROID_DELETE_LOCAL_REF (bitmap);
+      memory_full (0);
+    }
+
+  if (bitmap_info.width != image->width
+      || bitmap_info.height != image->height)
+    /* This is not yet supported.  */
+    emacs_abort ();
+
+  /* Make sure the bitmap formats are compatible with each other.  */
+
+  if ((image->depth == 24
+       && bitmap_info.format != ANDROID_BITMAP_FORMAT_RGBA_8888)
+      || (image->depth == 1
+         && bitmap_info.format != ANDROID_BITMAP_FORMAT_A_8))
+    emacs_abort ();
+
+  /* Lock the image data.  Once again, the NDK documentation says the
+     call can fail, but does not say how to determine whether or not
+     it has failed, nor how the address is aligned.  */
+  data = NULL;
+  AndroidBitmap_lockPixels (android_java_env, bitmap, &data);
+
+  if (!data)
+    {
+      /* Take a NULL pointer to mean that AndroidBitmap_lockPixels
+        failed.  */
+      ANDROID_DELETE_LOCAL_REF (bitmap);
+      memory_full (0);
+    }
+
+  data_1 = data;
+  data_2 = (unsigned char *) image->data;
+
+  /* Copy the bitmap data over scanline-by-scanline.  */
+  for (i = 0; i < image->height; ++i)
+    {
+      if (image->depth != 1)
+       memcpy (data_1, data_2,
+               image->width * (image->bits_per_pixel / 8));
+      else
+       {
+         /* Android internally uses a 1 byte-per-pixel format for
+            ALPHA_8 images.  Expand the image from the 1
+            bit-per-pixel X format correctly.  */
+
+         for (x = 0; x < image->width; ++x)
+           data_1[x] = (data_2[x / 8] & (1 << x % 8)) ? 0xff : 0;
+       }
+
+      data_1 += bitmap_info.stride;
+      data_2 += image->bytes_per_line;
+    }
+
+  /* Unlock the bitmap pixels.  */
+  AndroidBitmap_unlockPixels (android_java_env, bitmap);
+
+  /* Delete the bitmap reference.  */
+  ANDROID_DELETE_LOCAL_REF (bitmap);
+}
+
+void
+android_bell (void)
+{
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                emacs_service,
+                                                service_class.class,
+                                                service_class.ring_bell);
+  android_exception_check ();
+}
+
+void
+android_set_input_focus (android_window handle, unsigned long time)
+{
+  jobject window;
+  jmethodID make_input_focus;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  make_input_focus = window_class.make_input_focus;
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                window,
+                                                window_class.class,
+                                                make_input_focus,
+                                                (jlong) time);
+  android_exception_check ();
+}
+
+void
+android_raise_window (android_window handle)
+{
+  jobject window;
+  jmethodID raise;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  raise = window_class.raise;
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                window,
+                                                window_class.class,
+                                                raise);
+  android_exception_check ();
+}
+
+void
+android_lower_window (android_window handle)
+{
+  jobject window;
+  jmethodID lower;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  lower = window_class.lower;
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                window,
+                                                window_class.class,
+                                                lower);
+  android_exception_check ();
+}
+
+int
+android_query_tree (android_window handle, android_window *root_return,
+                   android_window *parent_return,
+                   android_window **children_return,
+                   unsigned int *nchildren_return)
+{
+  jobject window, array;
+  jsize nelements, i;
+  android_window *children;
+  jshort *shorts;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+  /* window can be NULL, so this is a service method.  */
+  array
+    = (*android_java_env)->CallObjectMethod (android_java_env,
+                                            emacs_service,
+                                            service_class.query_tree,
+                                            window);
+  android_exception_check ();
+
+  /* The first element of the array is the parent window.  The rest
+     are the children.  */
+  nelements = (*android_java_env)->GetArrayLength (android_java_env,
+                                                  array);
+  eassert (nelements);
+
+  /* Now fill in the children.  */
+  children = xnmalloc (nelements - 1, sizeof *children);
+
+  shorts
+    = (*android_java_env)->GetShortArrayElements (android_java_env, array,
+                                                 NULL);
+  android_exception_check_nonnull (shorts, array);
+
+  for (i = 1; i < nelements; ++i)
+    /* Subtract one from the index into children, since the parent is
+       not included.  */
+    children[i - 1] = shorts[i];
+
+  /* Finally, return the parent and other values.  */
+  *root_return = 0;
+  *parent_return = shorts[0];
+  *children_return = children;
+  *nchildren_return = nelements - 1;
+
+  /* Release the array contents.  */
+  (*android_java_env)->ReleaseShortArrayElements (android_java_env, array,
+                                                 shorts, JNI_ABORT);
+
+  ANDROID_DELETE_LOCAL_REF (array);
+  return 1;
+}
+
+void
+android_get_geometry (android_window handle,
+                     android_window *root_return,
+                     int *x_return, int *y_return,
+                     unsigned int *width_return,
+                     unsigned int *height_return,
+                     unsigned int *border_width_return)
+{
+  jobject window;
+  jarray window_geometry;
+  jmethodID get_geometry;
+  jint *ints;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  get_geometry = window_class.get_window_geometry;
+
+  window_geometry
+    = (*android_java_env)->CallObjectMethod (android_java_env,
+                                            window,
+                                            get_geometry);
+  android_exception_check ();
+
+  /* window_geometry is an array containing x, y, width and
+     height.  border_width is always 0 on Android.  */
+  eassert ((*android_java_env)->GetArrayLength (android_java_env,
+                                               window_geometry)
+          == 4);
+
+  *root_return = 0;
+  *border_width_return = 0;
+
+  ints
+    = (*android_java_env)->GetIntArrayElements (android_java_env,
+                                               window_geometry,
+                                               NULL);
+  android_exception_check_nonnull (ints, window_geometry);
+
+  *x_return = ints[0];
+  *y_return = ints[1];
+  *width_return = ints[2];
+  *height_return = ints[3];
+
+  (*android_java_env)->ReleaseIntArrayElements (android_java_env,
+                                               window_geometry,
+                                               ints, JNI_ABORT);
+
+  /* Now free the local reference.  */
+  ANDROID_DELETE_LOCAL_REF (window_geometry);
+}
+
+void
+android_move_resize_window (android_window window, int x, int y,
+                           unsigned int width, unsigned int height)
+{
+  android_move_window (window, x, y);
+  android_resize_window (window, width, height);
+}
+
+void
+android_map_raised (android_window window)
+{
+  android_raise_window (window);
+  android_map_window (window);
+}
+
+void
+android_translate_coordinates (android_window src, int x,
+                              int y, int *root_x, int *root_y)
+{
+  jobject window;
+  jarray coordinates;
+  jmethodID method;
+  jint *ints;
+
+  window = android_resolve_handle (src, ANDROID_HANDLE_WINDOW);
+  method = window_class.translate_coordinates;
+  coordinates
+    = (*android_java_env)->CallObjectMethod (android_java_env,
+                                            window, method,
+                                            (jint) x, (jint) y);
+  android_exception_check ();
+
+  /* The array must contain two elements: X, Y translated to the root
+     window.  */
+  eassert ((*android_java_env)->GetArrayLength (android_java_env,
+                                               coordinates)
+          == 2);
+
+  /* Obtain the coordinates from the array.  */
+  ints = (*android_java_env)->GetIntArrayElements (android_java_env,
+                                                  coordinates, NULL);
+  android_exception_check_nonnull (ints, coordinates);
+
+  *root_x = ints[0];
+  *root_y = ints[1];
+
+  /* Release the coordinates.  */
+  (*android_java_env)->ReleaseIntArrayElements (android_java_env,
+                                               coordinates, ints,
+                                               JNI_ABORT);
+
+  /* And free the local reference.  */
+  ANDROID_DELETE_LOCAL_REF (coordinates);
+}
+
+int
+android_wc_lookup_string (android_key_pressed_event *event,
+                         wchar_t *buffer_return, int wchars_buffer,
+                         int *keysym_return,
+                         enum android_lookup_status *status_return)
+{
+  enum android_lookup_status status;
+  int rc;
+  jobject window, string;
+  const jchar *characters;
+  jsize size;
+  size_t i;
+
+  status = ANDROID_LOOKUP_NONE;
+  rc = 0;
+
+  /* See if an actual lookup has to be made.  Note that while
+     BUFFER_RETURN is wchar_t, the returned characters are always in
+     UCS.  */
+
+  if (event->unicode_char != (uint32_t) -1)
+    {
+      if (event->unicode_char)
+       {
+         if (wchars_buffer < 1)
+           {
+             *status_return = ANDROID_BUFFER_OVERFLOW;
+             return 0;
+           }
+         else
+           {
+             buffer_return[0] = event->unicode_char;
+             status = ANDROID_LOOKUP_CHARS;
+             rc = 1;
+           }
+       }
+
+      *keysym_return = event->keycode;
+
+      if (status == ANDROID_LOOKUP_CHARS)
+       status = ANDROID_LOOKUP_BOTH;
+      else
+       {
+         status = ANDROID_LOOKUP_KEYSYM;
+         rc = 0;
+       }
+
+      *status_return = status;
+
+      return rc;
+    }
+
+  /* Now look up the window.  */
+  rc = 0;
+
+  if (!android_handles[event->window].handle
+      || (android_handles[event->window].type
+         != ANDROID_HANDLE_WINDOW))
+    status = ANDROID_LOOKUP_NONE;
+  else
+    {
+      window = android_handles[event->window].handle;
+      string
+       = (*android_java_env)->CallObjectMethod (android_java_env, window,
+                                                window_class.lookup_string,
+                                                (jint) event->serial);
+      android_exception_check ();
+
+      if (!string)
+       status = ANDROID_LOOKUP_NONE;
+      else
+       {
+         /* Now return this input method string.  */
+         characters = (*android_java_env)->GetStringChars (android_java_env,
+                                                           string, NULL);
+         android_exception_check_nonnull ((void *) characters, string);
+
+         /* Figure out how big the string is.  */
+         size = (*android_java_env)->GetStringLength (android_java_env,
+                                                      string);
+
+         /* Copy over the string data.  */
+         for (i = 0; i < MIN ((unsigned int) wchars_buffer, size); ++i)
+           buffer_return[i] = characters[i];
+
+         if (i < size)
+           status = ANDROID_BUFFER_OVERFLOW;
+         else
+           status = ANDROID_LOOKUP_CHARS;
+
+         /* Return the number of characters that should have been
+            written.  */
+
+         if (size > INT_MAX)
+           rc = INT_MAX;
+         else
+           rc = size;
+
+         (*android_java_env)->ReleaseStringChars (android_java_env, string,
+                                                  characters);
+         ANDROID_DELETE_LOCAL_REF (string);
+       }
+    }
+
+  *status_return = status;
+  return rc;
+}
+
+
+
+/* Low level drawing primitives.  */
+
+/* Lock the bitmap corresponding to the drawable DRAWABLE.  Return the
+   bitmap data upon success, and store the bitmap object in
+   BITMAP_RETURN.  Value is NULL upon failure.
+
+   The caller must take care to unlock the bitmap data afterwards.  */
+
+unsigned char *
+android_lock_bitmap (android_window drawable,
+                    AndroidBitmapInfo *bitmap_info,
+                    jobject *bitmap_return)
+{
+  jobject object, bitmap;
+  void *data;
+
+  object = android_resolve_handle2 (drawable, ANDROID_HANDLE_WINDOW,
+                                   ANDROID_HANDLE_PIXMAP);
+
+  /* Look up the drawable and get the bitmap corresponding to it.
+     Then, lock the bitmap's bits.  */
+  bitmap = (*android_java_env)->CallObjectMethod (android_java_env,
+                                                 object,
+                                                 drawable_class.get_bitmap);
+  if (!bitmap)
+    /* NULL is returned when the bitmap does not currently exist due
+       to ongoing reconfiguration on the main thread.  */
+    return NULL;
+
+  memset (bitmap_info, 0, sizeof *bitmap_info);
+
+  /* Get the bitmap info.  */
+  AndroidBitmap_getInfo (android_java_env, bitmap, bitmap_info);
+
+  if (!bitmap_info->stride)
+    {
+      ANDROID_DELETE_LOCAL_REF (bitmap);
+      return NULL;
+    }
+
+  /* Now lock the image data.  */
+  data = NULL;
+  AndroidBitmap_lockPixels (android_java_env, bitmap, &data);
+
+  if (!data)
+    {
+      ANDROID_DELETE_LOCAL_REF (bitmap);
+      return NULL;
+    }
+
+  /* Give the bitmap to the caller.  */
+  *bitmap_return = bitmap;
+
+  /* The bitmap data is now locked.  */
+  return data;
+}
+
+/* Damage the window HANDLE by the given damage rectangle.  */
+
+void
+android_damage_window (android_drawable handle,
+                      struct android_rectangle *damage)
+{
+  jobject drawable, rect;
+
+  drawable = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+  /* Now turn DAMAGE into a Java rectangle.  */
+  rect = (*android_java_env)->NewObject (android_java_env,
+                                        android_rect_class,
+                                        android_rect_constructor,
+                                        (jint) damage->x,
+                                        (jint) damage->y,
+                                        (jint) (damage->x
+                                                + damage->width),
+                                        (jint) (damage->y
+                                                + damage->height));
+  android_exception_check ();
+
+  /* Post the damage to the drawable.  */
+  (*android_java_env)->CallVoidMethod (android_java_env,
+                                      drawable,
+                                      drawable_class.damage_rect,
+                                      rect);
+  android_exception_check_1 (rect);
+  ANDROID_DELETE_LOCAL_REF (rect);
+}
+
+
+
+/* Other misc system routines.  */
+
+int
+android_get_screen_width (void)
+{
+  int rc;
+  jmethodID method;
+
+  method = service_class.get_screen_width;
+  rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
+                                                    emacs_service,
+                                                    service_class.class,
+                                                    method,
+                                                    (jboolean) false);
+  android_exception_check ();
+  return rc;
+}
+
+int
+android_get_screen_height (void)
+{
+  int rc;
+  jmethodID method;
+
+  method = service_class.get_screen_height;
+  rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
+                                                    emacs_service,
+                                                    service_class.class,
+                                                    method,
+                                                    (jboolean) false);
+  android_exception_check ();
+  return rc;
+}
+
+int
+android_get_mm_width (void)
+{
+  int rc;
+  jmethodID method;
+
+  method = service_class.get_screen_width;
+  rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
+                                                    emacs_service,
+                                                    service_class.class,
+                                                    method,
+                                                    (jboolean) true);
+  android_exception_check ();
+  return rc;
+}
+
+int
+android_get_mm_height (void)
+{
+  int rc;
+  jmethodID method;
+
+  method = service_class.get_screen_height;
+  rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
+                                                    emacs_service,
+                                                    service_class.class,
+                                                    method,
+                                                    (jboolean) true);
+  android_exception_check ();
+  return rc;
+}
+
+bool
+android_detect_mouse (void)
+{
+  bool rc;
+  jmethodID method;
+
+  method = service_class.detect_mouse;
+  rc = (*android_java_env)->CallNonvirtualBooleanMethod (android_java_env,
+                                                        emacs_service,
+                                                        service_class.class,
+                                                        method);
+  android_exception_check ();
+  return rc;
+}
+
+void
+android_set_dont_focus_on_map (android_window handle,
+                              bool no_focus_on_map)
+{
+  jmethodID method;
+  jobject window;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  method = window_class.set_dont_focus_on_map;
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, window,
+                                                window_class.class,
+                                                method,
+                                                (jboolean) no_focus_on_map);
+  android_exception_check ();
+}
+
+void
+android_set_dont_accept_focus (android_window handle,
+                              bool no_accept_focus)
+{
+  jmethodID method;
+  jobject window;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  method = window_class.set_dont_accept_focus;
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, window,
+                                                window_class.class,
+                                                method,
+                                                (jboolean) no_accept_focus);
+  android_exception_check ();
+}
+
+void
+android_get_keysym_name (int keysym, char *name_return, size_t size)
+{
+  jobject string;
+  const char *buffer;
+
+  string = (*android_java_env)->CallObjectMethod (android_java_env,
+                                                 emacs_service,
+                                                 service_class.name_keysym,
+                                                 (jint) keysym);
+  android_exception_check ();
+
+  buffer = (*android_java_env)->GetStringUTFChars (android_java_env,
+                                                  (jstring) string,
+                                                  NULL);
+  android_exception_check_nonnull ((void *) buffer, string);
+  strncpy (name_return, buffer, size - 1);
+  name_return[size] = '\0';
+
+  (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+                                             (jstring) string,
+                                             buffer);
+  ANDROID_DELETE_LOCAL_REF (string);
+}
+
+/* Display the on screen keyboard on window WINDOW, or hide it if SHOW
+   is false.  Ask the system to bring up or hide the on-screen
+   keyboard on behalf of WINDOW.  The request may be rejected by the
+   system, especially when the window does not have the input
+   focus.  */
+
+void
+android_toggle_on_screen_keyboard (android_window window, bool show)
+{
+  jobject object;
+  jmethodID method;
+
+  object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+  method = window_class.toggle_on_screen_keyboard;
+
+  /* Now display the on screen keyboard.  */
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, object,
+                                                window_class.class,
+                                                method, (jboolean) show);
+
+  /* Check for out of memory errors.  */
+  android_exception_check ();
+}
+
+
+
+/* emacs_abort implementation for Android.  This logs a stack
+   trace.  */
+
+void
+emacs_abort (void)
+{
+  volatile char *foo;
+
+  __android_log_print (ANDROID_LOG_FATAL, __func__,
+                      "emacs_abort called, please review the ensuing"
+                      " stack trace");
+
+  /* Cause a NULL pointer dereference to make debuggerd generate a
+     tombstone.  */
+  foo = NULL;
+  *foo = '\0';
+
+  abort ();
+}
+
+
+
+/* Return whether or not TEXT, a string without multibyte
+   characters, has no bytes with the 8th bit set.  */
+
+static bool
+android_check_string (Lisp_Object text)
+{
+  ptrdiff_t i;
+
+  for (i = 0; i < SBYTES (text); ++i)
+    {
+      if (SREF (text, i) & 128)
+       return false;
+    }
+
+  return true;
+}
+
+/* Verify that the specified NULL-terminated STRING is a valid JNI
+   ``UTF-8'' string.  Return 0 if so, 1 otherwise.
+
+   Do not perform GC, enabling NAME to be a direct reference to string
+   data.
+
+   The native coding system used by the JVM to store strings derives
+   from UTF-8, but deviates from it in two aspects in an attempt to
+   better represent the UCS-16 based Java String format, and to let
+   strings contain NULL characters while remaining valid C strings:
+   NULL bytes are encoded as two-byte sequences, and Unicode surrogate
+   pairs encoded as two-byte sequences are prefered to four-byte
+   sequences when encoding characters above the BMP.  */
+
+int
+android_verify_jni_string (const char *name)
+{
+  const unsigned char *chars;
+
+  chars = (unsigned char *) name;
+  while (*chars)
+    {
+      /* Switch on the high 4 bits.  */
+
+      switch (*chars++ >> 4)
+       {
+       case 0 ... 7:
+         /* The 8th bit is clean, so this is a regular C
+            character.  */
+         break;
+
+       case 8 ... 0xb:
+         /* Invalid starting byte! */
+         return 1;
+
+       case 0xf:
+         /* The start of a four byte sequence.  These aren't allowed
+            in Java.  */
+         return 1;
+
+       case 0xe:
+         /* The start of a three byte sequence.  Verify that its
+            continued.  */
+
+         if ((*chars++ & 0xc0) != 0x80)
+           return 1;
+
+         FALLTHROUGH;
+
+       case 0xc ... 0xd:
+         /* The start of a two byte sequence.  Verify that the
+            next byte exists and has its high bit set.  */
+
+         if ((*chars++ & 0xc0) != 0x80)
+           return 1;
+
+         break;
+       }
+    }
+
+  return 0;
+}
+
+/* Given a Lisp string TEXT, return a local reference to an equivalent
+   Java string.  */
+
+jstring
+android_build_string (Lisp_Object text)
+{
+  Lisp_Object encoded;
+  jstring string;
+  size_t nchars;
+  jchar *characters;
+  USE_SAFE_ALLOCA;
+
+  /* Directly encode TEXT if it contains no non-ASCII characters, or
+     is multibyte and a valid Modified UTF-8 string.  This is okay
+     because the Java extended UTF format is compatible with
+     ASCII.  */
+
+  if ((SBYTES (text) == SCHARS (text)
+       && android_check_string (text))
+      /* If TEXT is a multibyte string, then it's using Emacs's
+        internal UTF-8 coding system, a significant subset of which
+        is compatible with JNI.  */
+      || (STRING_MULTIBYTE (text)
+         && !android_verify_jni_string (SSDATA (text))))
+    {
+      string = (*android_java_env)->NewStringUTF (android_java_env,
+                                                 SSDATA (text));
+      android_exception_check ();
+      SAFE_FREE ();
+
+      return string;
+    }
+
+  encoded = code_convert_string_norecord (text, Qutf_16le,
+                                         true);
+  nchars = (SBYTES (encoded) / sizeof (jchar));
+
+  /* Encode the string as UTF-16 prior to creating the string.
+     Copy the string to a separate buffer in order to preserve
+     alignment.  */
+
+  characters = SAFE_ALLOCA (SBYTES (encoded));
+  memcpy (characters, SDATA (encoded), SBYTES (encoded));
+
+  /* Create the string.  */
+  string
+    = (*android_java_env)->NewString (android_java_env,
+                                     characters, nchars);
+  android_exception_check ();
+
+  SAFE_FREE ();
+  return string;
+}
+
+/* Do the same, except TEXT is constant string data in ASCII or
+   UTF-8 containing no characters outside the Basic Multilingual
+   Plane.  */
+
+jstring
+android_build_jstring (const char *text)
+{
+  jstring string;
+
+  /* Note that Java expects this string to be in ``modified UTF
+     encoding'', which is actually UTF-8, except with NUL
+     encoded as a two-byte sequence, and surrogate pairs encoded
+     in the three-byte extended encoding.  The only consequence
+     of passing an actual UTF-8 string is that NUL bytes and
+     characters requiring surrogate pairs cannot be represented,
+     which is not really of consequence.  */
+
+  string = (*android_java_env)->NewStringUTF (android_java_env,
+                                             text);
+  android_exception_check ();
+
+  return string;
+}
+
+
+
+/* Exception checking functions.  Most JNI functions which allocate
+   memory return NULL upon failure; they also set the JNI
+   environment's pending exception to an OutOfMemoryError.
+
+   These functions check for such errors and call memory_full wherever
+   appropriate.  Three variants are provided: one which releases no
+   local references, one which releases a single local reference
+   before calling memory_full, and one which releases two local
+   references.
+
+   Typically, you use these functions by calling them immediately
+   after a JNI function which allocates memory, passing it any local
+   references that are already valid but should be deleted after
+   leaving the current scope.  For example, to allocate foo, make
+   global_foo its global reference, and then release foo, you write:
+
+     jobject foo, global_foo;
+
+     foo = (*android_java_env)->New...;
+     android_exception_check ();
+
+     global_foo = (*android_java_env)->NewGlobalRef (..., foo);
+     android_exception_check_1 (foo);
+     ANDROID_DELETE_LOCAL_REF (foo);
+
+   where the first android_exception_check ensures that foo has been
+   allocated correctly, while the call to android_exception_check_1,
+   and the call to ANDROID_DELETE_LOCAL_REF afterwards, together
+   ensure the same of global_foo, and also that foo is released both
+   if global_foo cannot be allocated, and after the global reference
+   is created.  */
+
+#if __GNUC__ >= 3
+#define likely(cond)   __builtin_expect ((cond), 1)
+#else /* __GNUC__ < 3 */
+#define likely(cond)   (cond)
+#endif /* __GNUC__ >= 3 */
+
+/* Check for JNI exceptions and call memory_full in that
+   situation.  */
+
+void
+android_exception_check (void)
+{
+  if (likely (!(*android_java_env)->ExceptionCheck (android_java_env)))
+    return;
+
+  __android_log_print (ANDROID_LOG_WARN, __func__,
+                      "Possible out of memory error. "
+                      " The Java exception follows:  ");
+  /* Describe exactly what went wrong.  */
+  (*android_java_env)->ExceptionDescribe (android_java_env);
+  (*android_java_env)->ExceptionClear (android_java_env);
+  memory_full (0);
+}
+
+/* Check for JNI exceptions.  If there is one such exception, clear
+   it, then delete the local reference to OBJECT and call
+   memory_full.  */
+
+void
+android_exception_check_1 (jobject object)
+{
+  if (likely (!(*android_java_env)->ExceptionCheck (android_java_env)))
+    return;
+
+  __android_log_print (ANDROID_LOG_WARN, __func__,
+                      "Possible out of memory error. "
+                      " The Java exception follows:  ");
+  /* Describe exactly what went wrong.  */
+  (*android_java_env)->ExceptionDescribe (android_java_env);
+  (*android_java_env)->ExceptionClear (android_java_env);
+  ANDROID_DELETE_LOCAL_REF (object);
+  memory_full (0);
+}
+
+/* Like android_exception_check_1, except it takes more than one local
+   reference argument.  */
+
+void
+android_exception_check_2 (jobject object, jobject object1)
+{
+  if (likely (!(*android_java_env)->ExceptionCheck (android_java_env)))
+    return;
+
+  __android_log_print (ANDROID_LOG_WARN, __func__,
+                      "Possible out of memory error. "
+                      " The Java exception follows:  ");
+  /* Describe exactly what went wrong.  */
+  (*android_java_env)->ExceptionDescribe (android_java_env);
+  (*android_java_env)->ExceptionClear (android_java_env);
+  ANDROID_DELETE_LOCAL_REF (object);
+  ANDROID_DELETE_LOCAL_REF (object1);
+  memory_full (0);
+}
+
+/* Like android_exception_check_2, except it takes more than two local
+   reference arguments.  */
+
+void
+android_exception_check_3 (jobject object, jobject object1,
+                          jobject object2)
+{
+  if (likely (!(*android_java_env)->ExceptionCheck (android_java_env)))
+    return;
+
+  __android_log_print (ANDROID_LOG_WARN, __func__,
+                      "Possible out of memory error. "
+                      " The Java exception follows:  ");
+  /* Describe exactly what went wrong.  */
+  (*android_java_env)->ExceptionDescribe (android_java_env);
+  (*android_java_env)->ExceptionClear (android_java_env);
+  ANDROID_DELETE_LOCAL_REF (object);
+  ANDROID_DELETE_LOCAL_REF (object1);
+  ANDROID_DELETE_LOCAL_REF (object2);
+  memory_full (0);
+}
+
+/* Like android_exception_check_3, except it takes more than three
+   local reference arguments.  */
+
+void
+android_exception_check_4 (jobject object, jobject object1,
+                          jobject object2, jobject object3)
+{
+  if (likely (!(*android_java_env)->ExceptionCheck (android_java_env)))
+    return;
+
+  __android_log_print (ANDROID_LOG_WARN, __func__,
+                      "Possible out of memory error. "
+                      " The Java exception follows:  ");
+  /* Describe exactly what went wrong.  */
+  (*android_java_env)->ExceptionDescribe (android_java_env);
+  (*android_java_env)->ExceptionClear (android_java_env);
+  ANDROID_DELETE_LOCAL_REF (object);
+  ANDROID_DELETE_LOCAL_REF (object1);
+  ANDROID_DELETE_LOCAL_REF (object2);
+  ANDROID_DELETE_LOCAL_REF (object3);
+  memory_full (0);
+}
+
+/* Check for JNI problems based on the value of OBJECT.
+
+   Signal out of memory if OBJECT is NULL.  OBJECT1 means the
+   same as in `android_exception_check_1'.
+
+   This function is useful when checking for errors from JNI
+   functions that do not set exceptions on failure, such as
+   `GetIntArrayElements'.  */
+
+void
+android_exception_check_nonnull (void *object, jobject object1)
+{
+  if (likely (object != NULL))
+    return;
+
+  if (object1)
+    ANDROID_DELETE_LOCAL_REF (object1);
+
+  memory_full (0);
+}
+
+/* Check for JNI problems based on the value of OBJECT.
+
+   Signal out of memory if OBJECT is NULL.  OBJECT1 and OBJECT2 mean
+   the same as in `android_exception_check_2'.  */
+
+void
+android_exception_check_nonnull_1 (void *object, jobject object1,
+                                  jobject object2)
+{
+  if (likely (object != NULL))
+    return;
+
+  if (object1)
+    ANDROID_DELETE_LOCAL_REF (object1);
+
+  if (object2)
+    ANDROID_DELETE_LOCAL_REF (object2);
+
+  memory_full (0);
+}
+
+
+
+/* Native image transforms.  */
+
+/* Transform the coordinates X and Y by the specified affine
+   transformation MATRIX.  Place the result in *XOUT and *YOUT.  */
+
+static void
+android_transform_coordinates (int x, int y,
+                              struct android_transform *transform,
+                              float *xout, float *yout)
+{
+  /* Apply the specified affine transformation.
+     A transform looks like:
+
+       M1 M2 M3     X
+       M4 M5 M6   * Y
+
+       =
+
+       M1*X + M2*Y + M3*1 = X1
+       M4*X + M5*Y + M6*1 = Y1
+
+     (In most transforms, there is another row at the bottom for
+     mathematical reasons.  Since Z1 is always 1.0, the row is simply
+     implied to be 0 0 1, because 0 * x + 0 * y + 1 * 1 = 1.0.  See
+     the definition of matrix3x3 in image.c for some more explanations
+     about this.) */
+
+  *xout = transform->m1 * x + transform->m2 * y + transform->m3;
+  *yout = transform->m4 * x + transform->m5 * y + transform->m6;
+}
+
+/* Return the interpolation of the four pixels TL, TR, BL, and BR,
+   according to the weights DISTX and DISTY.  */
+
+static unsigned int
+android_four_corners_bilinear (unsigned int tl, unsigned int tr,
+                              unsigned int bl, unsigned int br,
+                              int distx, int disty)
+{
+  int distxy, distxiy, distixy, distixiy;
+  uint32_t f, r;
+
+  distxy = distx * disty;
+  distxiy = (distx << 8) - distxy;
+  distixy = (disty << 8) - distxy;
+  distixiy = (256 * 256 - (disty << 8)
+             - (distx << 8) + distxy);
+
+  /* Red */
+  r = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy
+       + (bl & 0x000000ff) * distixy  + (br & 0x000000ff) * distxy);
+
+  /* Green */
+  f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy
+       + (bl & 0x0000ff00) * distixy  + (br & 0x0000ff00) * distxy);
+  r |= f & 0xff000000;
+
+  /* Now do the upper two components.  */
+  tl >>= 16;
+  tr >>= 16;
+  bl >>= 16;
+  br >>= 16;
+  r >>= 16;
+
+  /* Blue */
+  f = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy
+       + (bl & 0x000000ff) * distixy  + (br & 0x000000ff) * distxy);
+  r |= f & 0x00ff0000;
+
+  /* Alpha */
+  f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy
+       + (bl & 0x0000ff00) * distixy  + (br & 0x0000ff00) * distxy);
+  r |= f & 0xff000000;
+
+  return r;
+}
+
+/* Return the interpolation of the four pixels closest to at X, Y in
+   IMAGE, according to weights in both axes computed from X and Y.
+   IMAGE must be depth 24, or the behavior is undefined.  */
+
+static unsigned int
+android_fetch_pixel_bilinear (struct android_image *image,
+                             float x, float y)
+{
+  int x1, y1, x2, y2;
+  float distx, disty;
+  unsigned int top_left, top_right;
+  unsigned int bottom_left, bottom_right;
+  char *word;
+
+  /* Compute the four closest corners to X and Y.  */
+  x1 = (int) x;
+  x2 = x1 + 1;
+  y1 = (int) y;
+  y2 = y1 + 1;
+
+  /* Make sure all four corners are within range.  */
+  x1 = MAX (0, MIN (image->width - 1, x1));
+  y1 = MAX (0, MIN (image->height - 1, y1));
+  x2 = MAX (0, MIN (image->width - 1, x2));
+  y2 = MAX (0, MIN (image->height - 1, y2));
+
+  /* Compute the X and Y biases.  These are numbers between 0f and
+     1f.  */
+  distx = x - x1;
+  disty = y - y1;
+
+  /* Fetch the four closest pixels.  */
+  word = image->data + y1 * image->bytes_per_line + x1 * 4;
+  memcpy (&top_left, word, sizeof top_left);
+  word = image->data + y1 * image->bytes_per_line + x2 * 4;
+  memcpy (&top_right, word, sizeof top_right);
+  word = image->data + y2 * image->bytes_per_line + x1 * 4;
+  memcpy (&bottom_left, word, sizeof bottom_left);
+  word = image->data + y2 * image->bytes_per_line + x2 * 4;
+  memcpy (&bottom_right, word, sizeof bottom_right);
+
+  /* Do the interpolation.  */
+  return android_four_corners_bilinear (top_left, top_right, bottom_left,
+                                       bottom_right, distx * 256,
+                                       disty * 256);
+}
+
+/* Transform the depth 24 image IMAGE by the 3x2 affine transformation
+   matrix MATRIX utilizing a bilinear filter.  Place the result in
+   OUT.  The matrix maps from the coordinate space of OUT to
+   IMAGE.  */
+
+void
+android_project_image_bilinear (struct android_image *image,
+                               struct android_image *out,
+                               struct android_transform *transform)
+{
+  int x, y;
+  unsigned int pixel;
+  float xout, yout;
+  char *word;
+
+  /* Loop through each pixel in OUT.  Transform it by TRANSFORM, then
+     interpolate it to IMAGE, and place the result back in OUT.  */
+
+  for (y = 0; y < out->height; ++y)
+    {
+      for (x = 0; x < out->width; ++x)
+       {
+         /* Transform the coordinates by TRANSFORM.  */
+         android_transform_coordinates (x, y, transform,
+                                        &xout, &yout);
+
+         /* Interpolate back to IMAGE.  */
+         pixel = android_fetch_pixel_bilinear (image, xout, yout);
+
+         /* Put the pixel back in OUT.  */
+         word = out->data + y * out->bytes_per_line + x * 4;
+         memcpy (word, &pixel, sizeof pixel);
+       }
+    }
+}
+
+/* Return the interpolation of X, Y to IMAGE, a depth 24 image.  */
+
+static unsigned int
+android_fetch_pixel_nearest_24 (struct android_image *image, float x,
+                               float y)
+{
+  int x1, y1;
+  char *word;
+  unsigned int pixel;
+
+  x1 = MAX (0, MIN (image->width - 1, (int) roundf (x)));
+  y1 = MAX (0, MIN (image->height - 1, (int) roundf (y)));
+
+  word = image->data + y1 * image->bytes_per_line + x1 * 4;
+  memcpy (&pixel, word, sizeof pixel);
+
+  return pixel;
+}
+
+/* Return the interpolation of X, Y to IMAGE, a depth 1 image.  */
+
+static unsigned int
+android_fetch_pixel_nearest_1 (struct android_image *image, float x,
+                              float y)
+{
+  int x1, y1;
+  char *byte;
+
+  x1 = MAX (0, MIN (image->width - 1, (int) roundf (x)));
+  y1 = MAX (0, MIN (image->height - 1, (int) roundf (y)));
+
+  byte = image->data + y1 * image->bytes_per_line;
+  return (byte[x1 / 8] & (1 << x1 % 8)) ? 1 : 0;
+}
+
+/* Transform the depth 24 or 1 image IMAGE by the 3x2 affine
+   transformation matrix MATRIX.  Place the result in OUT.  The matrix
+   maps from the coordinate space of OUT to IMAGE.  Use a
+   nearest-neighbor filter.  */
+
+void
+android_project_image_nearest (struct android_image *image,
+                              struct android_image *out,
+                              struct android_transform *transform)
+{
+  int x, y;
+  unsigned int pixel;
+  float xout, yout;
+  char *word, *byte;
+
+  if (image->depth == 1)
+    {
+      for (y = 0; y < out->height; ++y)
+       {
+         for (x = 0; x < out->width; ++x)
+           {
+             /* Transform the coordinates by TRANSFORM.  */
+             android_transform_coordinates (x, y, transform,
+                                            &xout, &yout);
+
+             /* Interpolate back to IMAGE.  */
+             pixel = android_fetch_pixel_nearest_1 (image, xout, yout);
+
+             /* Put the pixel back in OUT.  */
+             byte = out->data + y * out->bytes_per_line + x / 8;
+
+             if (pixel)
+               *byte |= (1 << x % 8);
+             else
+               *byte &= ~(1 << x % 8);
+           }
+       }
+
+      return;
+    }
+
+  for (y = 0; y < out->height; ++y)
+    {
+      for (x = 0; x < out->width; ++x)
+       {
+         /* Transform the coordinates by TRANSFORM.  */
+         android_transform_coordinates (x, y, transform,
+                                        &xout, &yout);
+
+         /* Interpolate back to IMAGE.  */
+         pixel = android_fetch_pixel_nearest_24 (image, xout, yout);
+
+         /* Put the pixel back in OUT.  */
+         word = out->data + y * out->bytes_per_line + x * 4;
+         memcpy (word, &pixel, sizeof pixel);
+       }
+    }
+}
+
+
+
+/* Other miscellaneous functions.  */
+
+/* Ask the system to start browsing the specified URL.  Upon failure,
+   return a string describing the error.  Else, value is nil.  URL
+   should be encoded unless SEND.
+
+   If SEND, open the URL with applications that can ``send'' or
+   ``share'' the URL (through mail, for example.)  */
+
+Lisp_Object
+android_browse_url (Lisp_Object url, Lisp_Object send)
+{
+  jobject value, string;
+  Lisp_Object tem;
+  const char *buffer;
+
+  string = android_build_string (url);
+  value = (*android_java_env)->CallObjectMethod (android_java_env,
+                                                emacs_service,
+                                                service_class.browse_url,
+                                                string,
+                                                (jboolean) !NILP (send));
+  android_exception_check ();
+
+  ANDROID_DELETE_LOCAL_REF (string);
+
+  /* If no string was returned, return Qnil.  */
+  if (!value)
+    return Qnil;
+
+  buffer = (*android_java_env)->GetStringUTFChars (android_java_env,
+                                                  (jstring) value,
+                                                  NULL);
+  android_exception_check_1 (string);
+
+  /* Otherwise, build the string describing the error.  */
+  tem = build_string_from_utf8 (buffer);
+
+  (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+                                             (jstring) value,
+                                             buffer);
+
+  /* And return it.  */
+  ANDROID_DELETE_LOCAL_REF (value);
+  return tem;
+}
+
+/* Tell the system to restart Emacs in a short amount of time, and
+   then kill Emacs.  Never return.  This is used to implement
+   `restart-emacs'.  */
+
+_Noreturn void
+android_restart_emacs (void)
+{
+  /* Try to call the Java side function.  Normally, this should call
+     System.exit to terminate this process.  */
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                emacs_service,
+                                                service_class.class,
+                                                service_class.restart_emacs);
+
+  /* Exit anyway, in case EmacsService did not do so.  */
+  exit (0);
+}
+
+/* Return a number from 1 to 33 describing the version of Android
+   Emacs is running on.
+
+   This is different from __ANDROID_API__, as that describes the
+   minimum version of Android this build of Emacs will run on, and in
+   turn which APIs Emacs can safely use.  */
+
+int
+(android_get_current_api_level) (void)
+{
+  return android_api_level;
+}
+
+/* Query the status of the battery, and place it in *STATUS.
+   Value is 1 upon failure, else 0.  */
+
+int
+android_query_battery (struct android_battery_state *status)
+{
+  jlongArray array;
+  jlong *longs;
+
+  array = (*android_java_env)->CallObjectMethod (android_java_env,
+                                                emacs_service,
+                                                service_class.query_battery);
+  android_exception_check ();
+
+  /* A NULL return with no exception means that battery information
+     could not be obtained.  */
+
+  if (!array)
+    return 1;
+
+  longs = (*android_java_env)->GetLongArrayElements (android_java_env,
+                                                    array, NULL);
+  android_exception_check_nonnull (longs, array);
+
+  status->capacity = longs[0];
+  status->charge_counter = longs[1];
+  status->current_average = longs[2];
+  status->current_now = longs[3];
+  status->remaining = longs[4];
+  status->status = longs[5];
+  status->plugged = longs[6];
+  status->temperature = longs[7];
+
+  (*android_java_env)->ReleaseLongArrayElements (android_java_env,
+                                                array, longs,
+                                                JNI_ABORT);
+  ANDROID_DELETE_LOCAL_REF (array);
+
+  return 0;
+}
+
+/* Display a file panel and grant Emacs access to the SAF directory
+   within it.  Value is 1 upon failure and 0 upon success (which only
+   indicates that the panel has been displayed successfully; the panel
+   may still be dismissed without a file being selected.)  */
+
+int
+android_request_directory_access (void)
+{
+  jint rc;
+  jmethodID method;
+
+  method = service_class.request_directory_access;
+  rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
+                                                    emacs_service,
+                                                    service_class.class,
+                                                    method);
+  android_exception_check ();
+
+  return rc;
+}
+
+
+
+/* The thread from which a query against a thread is currently being
+   made, if any.  Value is 0 if no query is in progress, 1 if a query
+   is being made from the UI thread to the main thread, and 2 if a
+   query is being made the other way around.  */
+static char android_servicing_query;
+
+/* Function that is waiting to be run in the Emacs thread.  */
+static void (*android_query_function) (void *);
+
+/* Context for that function.  */
+static void *android_query_context;
+
+/* Deadlock protection.  The UI thread and the Emacs thread must
+   sometimes make synchronous queries to each other, which are
+   normally answered inside each thread's respective event loop.
+   Deadlocks can happen when both threads simultaneously make such
+   synchronous queries and block waiting for each others responses.
+
+   The Emacs thread can be interrupted to service any queries made by
+   the UI thread, but is not possible the other way around.
+
+   To avoid such deadlocks, an atomic counter is provided.  This
+   counter is set to two every time a query starts from the main
+   thread, and is set to zero every time one ends.  If the UI thread
+   tries to make a query and sees that the counter is two, it simply
+   returns so that its event loop can proceed to perform and respond
+   to the query.  If the Emacs thread sees that the counter is one,
+   then it stops to service all queries being made by the input
+   method, then proceeds to make its query with the counter set to
+   2.
+
+   The memory synchronization is simple: all writes to
+   `android_query_context' and `android_query_function' are depended
+   on by writes to the atomic counter.  Loads of the new value from
+   the counter are then guaranteed to make those writes visible.  The
+   separate flag `android_urgent_query' does not depend on anything
+   itself; however, the input signal handler executes a memory fence
+   to ensure that all query related writes become visible.  */
+
+/* Run any function that the UI thread has asked to run, and then
+   signal its completion.  */
+
+static void
+android_check_query (void)
+{
+  void (*proc) (void *);
+  void *closure;
+
+  if (!__atomic_load_n (&android_servicing_query, __ATOMIC_ACQUIRE))
+    return;
+
+  /* First, load the procedure and closure.  */
+  closure = android_query_context;
+  proc = android_query_function;
+
+  if (!proc)
+    return;
+
+  proc (closure);
+
+  /* Finish the query.  */
+  android_query_context = NULL;
+  android_query_function = NULL;
+  __atomic_store_n (&android_servicing_query, 0, __ATOMIC_RELEASE);
+  __atomic_clear (&android_urgent_query, __ATOMIC_RELEASE);
+
+  /* Signal completion.  */
+  sem_post (&android_query_sem);
+}
+
+/* Run any function that the UI thread has asked to run, if the UI
+   thread has been waiting for more than two seconds.
+
+   Call this from `process_pending_signals' to ensure that the UI
+   thread always receives an answer within a reasonable amount of
+   time.  */
+
+void
+android_check_query_urgent (void)
+{
+  void (*proc) (void *);
+  void *closure;
+
+  if (!__atomic_load_n (&android_urgent_query, __ATOMIC_ACQUIRE))
+    return;
+
+  __android_log_print (ANDROID_LOG_VERBOSE, __func__,
+                      "Responding to urgent query...");
+
+  if (!__atomic_load_n (&android_servicing_query, __ATOMIC_ACQUIRE))
+    return;
+
+  /* First, load the procedure and closure.  */
+  closure = android_query_context;
+  proc = android_query_function;
+
+  if (!proc)
+    return;
+
+  proc (closure);
+
+  /* Finish the query.  Don't clear `android_urgent_query'; instead,
+     do that the next time Emacs enters the keyboard loop.  */
+
+  android_query_context = NULL;
+  android_query_function = NULL;
+  __atomic_store_n (&android_servicing_query, 0, __ATOMIC_RELEASE);
+
+  /* Signal completion.  */
+  sem_post (&android_query_sem);
+}
+
+/* Run the function that the UI thread has asked to run, and then
+   signal its completion.  Do not change `android_servicing_query'
+   after it completes.  */
+
+static void
+android_answer_query (void)
+{
+  void (*proc) (void *);
+  void *closure;
+
+  eassert (__atomic_load_n (&android_servicing_query,
+                           __ATOMIC_ACQUIRE)
+          == 1);
+
+  /* First, load the procedure and closure.  */
+  closure = android_query_context;
+  proc = android_query_function;
+
+  if (!proc)
+    return;
+
+  proc (closure);
+
+  /* Finish the query.  */
+  android_query_context = NULL;
+  android_query_function = NULL;
+  __atomic_clear (&android_urgent_query, __ATOMIC_RELEASE);
+
+  /* Signal completion.  */
+  sem_post (&android_query_sem);
+}
+
+/* Like `android_answer_query'.  However, the query may not have
+   begun; spin until it has.  */
+
+static void
+android_answer_query_spin (void)
+{
+  int n;
+
+  while (!(n = __atomic_load_n (&android_servicing_query,
+                               __ATOMIC_ACQUIRE)))
+    eassert (!n);
+
+  /* Note that this function is supposed to be called before
+     `android_begin_query' starts, so clear the service flag.  */
+  android_check_query ();
+}
+
+/* Notice that the Emacs thread will start blocking waiting for a
+   response from the UI thread.  Process any pending queries from the
+   UI thread.
+
+   This function may be called from Java.  */
+
+static void
+android_begin_query (void)
+{
+  char old;
+
+  /* Load the previous value of `android_servicing_query' and then set
+     it to 2.  */
+
+  old = __atomic_exchange_n (&android_servicing_query,
+                            2, __ATOMIC_ACQ_REL);
+
+  /* See if a query was previously in progress.  */
+  if (old == 1)
+    {
+      /* Answer the query that is currently being made.  */
+      assert (android_query_function != NULL);
+      android_answer_query ();
+    }
+
+  /* `android_servicing_query' is now 2.  */
+}
+
+/* Notice that a query has stopped.  This function may be called from
+   Java.  */
+
+static void
+android_end_query (void)
+{
+  __atomic_store_n (&android_servicing_query, 0, __ATOMIC_RELEASE);
+  __atomic_clear (&android_urgent_query, __ATOMIC_RELEASE);
+}
+
+/* Synchronously ask the Emacs thread to run the specified PROC with
+   the given CLOSURE.  Return if this fails, or once PROC is run.
+
+   PROC may be run from inside maybe_quit.
+
+   It is not okay to run Lisp code which signals or performs non
+   trivial tasks inside PROC.
+
+   Return 1 if the Emacs thread is currently waiting for the UI thread
+   to respond and PROC could not be run, or 0 otherwise.  */
+
+int
+android_run_in_emacs_thread (void (*proc) (void *), void *closure)
+{
+  union android_event event;
+  char old;
+  int rc;
+  struct timespec timeout;
+
+  event.xaction.type = ANDROID_WINDOW_ACTION;
+  event.xaction.serial = ++event_serial;
+  event.xaction.window = 0;
+  event.xaction.action = 0;
+
+  /* Set android_query_function and android_query_context.  */
+  android_query_context = closure;
+  android_query_function = proc;
+
+  /* Don't allow deadlocks to happen; make sure the Emacs thread is
+     not waiting for something to be done (in that case,
+     `android_query_context' is 2.)  */
+
+  old = 0;
+  if (!__atomic_compare_exchange_n (&android_servicing_query, &old,
+                                   1, false, __ATOMIC_ACQ_REL,
+                                   __ATOMIC_ACQUIRE))
+    {
+      android_query_context = NULL;
+      android_query_function = NULL;
+
+      /* The two variables above may still be non-NULL from the POV of
+        the main thread, as no happens-before constraint is placed on
+         those stores wrt a future load from `android_servicing_query'.  */
+
+      return 1;
+    }
+
+  /* Send a dummy event.  `android_check_query' will be called inside
+     wait_reading_process_output after the event arrives.
+
+     Otherwise, android_select will call android_check_thread the next
+     time it is entered.  */
+  android_write_event (&event);
+
+  /* Start waiting for the function to be executed.  First, wait two
+     seconds for the query to execute normally.  */
+
+  timeout.tv_sec = 2;
+  timeout.tv_nsec = 0;
+  timeout = timespec_add (current_timespec (), timeout);
+
+  /* See if an urgent query was recently answered without entering the
+     keyboard loop in between.  When that happens, raise SIGIO to
+     continue processing queries as soon as possible.  */
+
+  if (__atomic_load_n (&android_urgent_query, __ATOMIC_ACQUIRE))
+    kill (getpid (), SIGIO);
+
+ again:
+  rc = sem_timedwait (&android_query_sem, &timeout);
+
+  if (rc < 0)
+    {
+      if (errno == EINTR)
+       goto again;
+
+      eassert (errno == ETIMEDOUT);
+
+      __android_log_print (ANDROID_LOG_VERBOSE, __func__,
+                          "Timed out waiting for response"
+                          " from main thread...");
+
+      /* The query timed out.  At this point, set
+        `android_urgent_query' to true.  */
+      __atomic_store_n (&android_urgent_query, true,
+                       __ATOMIC_RELEASE);
+
+    kill_again:
+
+      /* And raise SIGIO.  Now that the query is considered urgent,
+        the main thread will reply while reading async input.
+
+        Normally, the main thread waits for the keyboard loop to be
+        entered before responding, in order to avoid responding with
+        inaccurate results taken during command executioon.  */
+      kill (getpid (), SIGIO);
+
+      /* Wait for the query to complete.  `android_urgent_query' is
+        only cleared by either `android_select' or
+        `android_check_query', so there's no need to worry about the
+        flag being cleared before the query is processed.
+
+        Send SIGIO again periodically until the query is answered, on
+        the off chance that SIGIO arrived too late to preempt a
+        system call, but too early for it to return EINTR.  */
+
+      timeout.tv_sec = 4;
+      timeout.tv_nsec = 0;
+      timeout = timespec_add (current_timespec (), timeout);
+
+      while (sem_timedwait (&android_query_sem, &timeout) < 0)
+       {
+         /* If waiting timed out, send SIGIO to the main thread
+            again.  */
+
+         if (errno == ETIMEDOUT)
+           goto kill_again;
+
+         /* Otherwise, continue waiting.  */
+         eassert (errno == EINTR);
+       }
+    }
+
+  /* At this point, `android_servicing_query' should either be zero if
+     the query was answered or two if the main thread has started a
+     query.  */
+
+  eassert (!__atomic_load_n (&android_servicing_query,
+                            __ATOMIC_ACQUIRE)
+          || (__atomic_load_n (&android_servicing_query,
+                               __ATOMIC_ACQUIRE) == 2));
+
+  return 0;
+}
+
+
+
+/* Input method related functions.  */
+
+/* Change WINDOW's active selection to the characters between
+   SELECTION_START and SELECTION_END.
+
+   Also, update the composing region to COMPOSING_REGION_START and
+   COMPOSING_REGION_END.
+
+   If any value cannot fit in jint, then the behavior of the input
+   method is undefined.  */
+
+void
+android_update_ic (android_window window, ptrdiff_t selection_start,
+                  ptrdiff_t selection_end, ptrdiff_t composing_region_start,
+                  ptrdiff_t composing_region_end)
+{
+  jobject object;
+
+  object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                emacs_service,
+                                                service_class.class,
+                                                service_class.update_ic,
+                                                object,
+                                                (jint) selection_start,
+                                                (jint) selection_end,
+                                                (jint) composing_region_start,
+                                                (jint) composing_region_end);
+  android_exception_check ();
+}
+
+/* Reinitialize any ongoing input method connection on WINDOW.
+
+   Any input method that is connected to WINDOW will invalidate its
+   cache of the buffer contents.
+
+   MODE controls certain aspects of the input method's behavior:
+
+     - If MODE is ANDROID_IC_MODE_NULL, the input method will be
+       deactivated, and an ASCII only keyboard will be displayed
+       instead.
+
+     - If MODE is ANDROID_IC_MODE_ACTION, the input method will
+       edit text normally, but send ``return'' as a key event.
+       This is useful inside the mini buffer.
+
+     - If MODE is ANDROID_IC_MODE_TEXT, the input method is free
+       to behave however it wants.  */
+
+void
+android_reset_ic (android_window window, enum android_ic_mode mode)
+{
+  jobject object;
+
+  object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                emacs_service,
+                                                service_class.class,
+                                                service_class.reset_ic,
+                                                object, (jint) mode);
+  android_exception_check ();
+}
+
+/* Make updates to extracted text known to the input method on
+   WINDOW.  TEXT should be a local reference to the new
+   extracted text.  TOKEN should be the token specified by the
+   input method.  */
+
+void
+android_update_extracted_text (android_window window, void *text,
+                              int token)
+{
+  jobject object;
+  jmethodID method;
+
+  object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+  method = service_class.update_extracted_text;
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                emacs_service,
+                                                service_class.class,
+                                                method, object,
+                                                /* N.B. that text is
+                                                   not jobject,
+                                                   because that type
+                                                   is not available
+                                                   in
+                                                   androidgui.h.  */
+                                                (jobject) text,
+                                                (jint) token);
+  android_exception_check_1 (text);
+}
+
+/* Report the position of the cursor to the input method connection on
+   WINDOW.
+
+   X is the horizontal position of the end of the insertion marker.  Y
+   is the top of the insertion marker.  Y_BASELINE is the baseline of
+   the row containing the insertion marker, and Y_BOTTOM is the bottom
+   of the insertion marker.  */
+
+void
+android_update_cursor_anchor_info (android_window window, float x,
+                                  float y, float y_baseline,
+                                  float y_bottom)
+{
+  jobject object;
+  jmethodID method;
+
+  object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+  method = service_class.update_cursor_anchor_info;
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                emacs_service,
+                                                service_class.class,
+                                                method,
+                                                object,
+                                                (jfloat) x,
+                                                (jfloat) y,
+                                                (jfloat) y_baseline,
+                                                (jfloat) y_bottom);
+  android_exception_check ();
+}
+
+
+
+/* Window decoration management functions.  */
+
+/* Make the specified WINDOW fullscreen, i.e. obscure all of the
+   system navigation and status bars.  If not FULLSCREEN, make it
+   maximized instead.
+
+   Value is 1 if the system does not support this, else 0.  */
+
+int
+android_set_fullscreen (android_window window, bool fullscreen)
+{
+  jobject object;
+
+  /* Android 4.0 and earlier don't support fullscreen windows.  */
+
+  if (android_api_level < 16)
+    return 1;
+
+  object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                object,
+                                                window_class.class,
+                                                window_class.set_fullscreen,
+                                                (jboolean) fullscreen);
+  android_exception_check ();
+  return 0;
+}
+
+
+
+/* Window cursor support.  */
+
+android_cursor
+android_create_font_cursor (enum android_cursor_shape shape)
+{
+  android_cursor id;
+  short prev_max_handle;
+  jobject object;
+
+  /* First, allocate the cursor handle.  */
+  prev_max_handle = max_handle;
+  id = android_alloc_id ();
+
+  if (!id)
+    error ("Out of cursor handles!");
+
+  /* Next, create the cursor.  */
+  object = (*android_java_env)->NewObject (android_java_env,
+                                          cursor_class.class,
+                                          cursor_class.constructor,
+                                          (jshort) id,
+                                          (jint) shape);
+  if (!object)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      max_handle = prev_max_handle;
+      memory_full (0);
+    }
+
+  android_handles[id].type = ANDROID_HANDLE_CURSOR;
+  android_handles[id].handle
+    = (*android_java_env)->NewGlobalRef (android_java_env, object);
+  (*android_java_env)->ExceptionClear (android_java_env);
+  ANDROID_DELETE_LOCAL_REF (object);
+
+  if (!android_handles[id].handle)
+    memory_full (0);
+
+  return id;
+}
+
+void
+android_define_cursor (android_window window, android_cursor cursor)
+{
+  jobject window1, cursor1;
+  jmethodID method;
+
+  window1 = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+  cursor1 = android_resolve_handle (cursor, ANDROID_HANDLE_CURSOR);
+  method = window_class.define_cursor;
+
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                window1,
+                                                window_class.class,
+                                                method, cursor1);
+  android_exception_check ();
+}
+
+void
+android_free_cursor (android_cursor cursor)
+{
+  if (android_handles[cursor].type != ANDROID_HANDLE_CURSOR)
+    {
+      __android_log_print (ANDROID_LOG_ERROR, __func__,
+                          "Trying to destroy something not a CURSOR!");
+      emacs_abort ();
+    }
+
+  android_destroy_handle (cursor);
+}
+
+
+
+/* Process execution.
+
+   Newer Android systems use SELinux to restrict user programs from
+   executing programs installed in the application data directory for
+   security reasons.  Emacs uses a `loader' binary installed in the
+   application data directory to manually load executables and replace
+   the `execve' system call.  */
+
+enum
+  {
+    /* Maximum number of arguments available.  */
+    MAXARGS = 1024,
+  };
+
+/* Rewrite the command line given in *ARGV to utilize the `exec1'
+   bootstrap binary if necessary.
+
+   Value is 0 upon success, else 1.  Set errno upon failure.
+
+   ARGV holds a pointer to a NULL-terminated array of arguments given
+   to `emacs_spawn'.  */
+
+int
+android_rewrite_spawn_argv (const char ***argv)
+{
+  static const char *new_args[MAXARGS];
+  static char exec1_name[PATH_MAX], loader_name[PATH_MAX];
+  size_t i, nargs;
+
+  /* This isn't required on Android 9 or earlier.  */
+
+  if (android_api_level < 29 || !android_use_exec_loader)
+    return 0;
+
+  /* Get argv[0]; this should never be NULL.
+     Then, verify that it exists and is executable.  */
+
+  eassert (**argv);
+  if (access (**argv, R_OK | X_OK))
+    return 1;
+
+  /* Count the number of arguments in *argv.  */
+
+  nargs = 0;
+  while ((*argv)[nargs])
+    ++nargs;
+
+  /* nargs now holds the number of arguments in argv.  If it's larger
+     than MAXARGS, return failure.  */
+
+  if (nargs + 2 > MAXARGS)
+    {
+      errno = E2BIG;
+      return 1;
+    }
+
+  /* Fill in the name of `libexec1.so'.  */
+  snprintf (exec1_name, PATH_MAX, "%s/libexec1.so",
+           android_lib_dir);
+
+  /* And libloader.so.  */
+  snprintf (loader_name, PATH_MAX, "%s/libloader.so",
+           android_lib_dir);
+
+  /* Now fill in the first two arguments.  */
+  new_args[0] = exec1_name;
+  new_args[1] = loader_name;
+
+  /* And insert the rest, including the trailing NULL.  */
+  for (i = 0; i < nargs + 1; ++i)
+    new_args[i + 2] = (*argv)[i];
+
+  /* Replace argv.  */
+  *argv = new_args;
+
+  /* Return success.  */
+  return 0;
+}
+
+
+
+#else /* ANDROID_STUBIFY */
+
+/* X emulation functions for Android.  */
+
+struct android_gc *
+android_create_gc (enum android_gc_value_mask mask,
+                  struct android_gc_values *values)
+{
+  /* This function should never be called when building stubs.  */
+  emacs_abort ();
+}
+
+void
+android_free_gc (struct android_gc *gc)
+{
+  /* This function should never be called when building stubs.  */
+  emacs_abort ();
+}
+
+struct android_image *
+android_create_image (unsigned int depth, enum android_image_format format,
+                     char *data, unsigned int width, unsigned int height)
+{
+  emacs_abort ();
+}
+
+void
+android_destroy_image (struct android_image *ximg)
+{
+  emacs_abort ();
+}
+
+void
+android_put_pixel (struct android_image *ximg, int x, int y,
+                  unsigned long pixel)
+{
+  emacs_abort ();
+}
+
+unsigned long
+android_get_pixel (struct android_image *ximg, int x, int y)
+{
+  emacs_abort ();
+}
+
+struct android_image *
+android_get_image (android_drawable drawable,
+                  enum android_image_format format)
+{
+  emacs_abort ();
+}
+
+void
+android_put_image (android_pixmap pixmap,
+                  struct android_image *image)
+{
+  emacs_abort ();
+}
+
+void
+android_project_image_bilinear (struct android_image *image,
+                               struct android_image *out,
+                               struct android_transform *transform)
+{
+  emacs_abort ();
+}
+
+void
+android_project_image_nearest (struct android_image *image,
+                              struct android_image *out,
+                              struct android_transform *transform)
+{
+  emacs_abort ();
+}
+
+#endif /* !ANDROID_STUBIFY */
diff --git a/src/android.h b/src/android.h
new file mode 100644
index 00000000000..a052d3a3b21
--- /dev/null
+++ b/src/android.h
@@ -0,0 +1,323 @@
+/* Android initialization for GNU Emacs.
+
+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/>.  */
+
+/* On Android, Emacs is built as a shared library loaded from Java
+   using the Java Native Interface.  Emacs's `main' function is
+   renamed `android_emacs_init', and runs with some modifications
+   inside a separate thread, communicating with the Java code through
+   a table of function pointers.  */
+
+#ifndef _ANDROID_H_
+#ifndef ANDROID_STUBIFY
+#include <jni.h>
+#include <pwd.h>
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <stdio.h>
+
+#include <android/bitmap.h>
+
+#include "androidgui.h"
+#include "lisp.h"
+#endif /* ANDROID_STUBIFY */
+
+extern bool android_init_gui;
+
+#ifndef ANDROID_STUBIFY
+
+extern char *android_cache_dir;
+
+extern int android_emacs_init (int, char **, char *);
+extern int android_select (int, fd_set *, fd_set *, fd_set *,
+                          struct timespec *);
+extern char *android_user_full_name (struct passwd *);
+
+
+
+/* File I/O operations.  Many of these are defined in
+   androidvfs.c.  */
+
+extern const char *android_is_special_directory (const char *, const char *);
+extern const char *android_get_home_directory (void);
+
+extern void android_vfs_init (JNIEnv *, jobject);
+
+extern int android_open (const char *, int, mode_t);
+extern int android_fstat (int, struct stat *);
+extern int android_fstatat (int, const char *restrict,
+                           struct stat *restrict, int);
+extern int android_faccessat (int, const char *, int, int);
+extern int android_close (int);
+extern FILE *android_fdopen (int, const char *);
+extern int android_fclose (FILE *);
+extern int android_unlink (const char *);
+extern int android_symlink (const char *, const char *);
+extern int android_rmdir (const char *);
+extern int android_mkdir (const char *, mode_t);
+extern int android_renameat_noreplace (int, const char *,
+                                      int, const char *);
+extern int android_rename (const char *, const char *);
+extern int android_fchmodat (int, const char *, mode_t, int);
+
+
+
+extern double android_pixel_density_x, android_pixel_density_y;
+extern double android_scaled_pixel_density;
+
+enum android_handle_type
+  {
+    ANDROID_HANDLE_WINDOW,
+    ANDROID_HANDLE_GCONTEXT,
+    ANDROID_HANDLE_PIXMAP,
+    ANDROID_HANDLE_CURSOR,
+  };
+
+extern jobject android_resolve_handle (android_handle,
+                                      enum android_handle_type);
+extern unsigned char *android_lock_bitmap (android_drawable,
+                                          AndroidBitmapInfo *,
+                                          jobject *);
+extern void android_damage_window (android_window,
+                                  struct android_rectangle *);
+extern int android_get_screen_width (void);
+extern int android_get_screen_height (void);
+extern int android_get_mm_width (void);
+extern int android_get_mm_height (void);
+extern bool android_detect_mouse (void);
+
+extern void android_set_dont_focus_on_map (android_window, bool);
+extern void android_set_dont_accept_focus (android_window, bool);
+
+extern int android_verify_jni_string (const char *);
+extern jstring android_build_string (Lisp_Object);
+extern jstring android_build_jstring (const char *);
+extern void android_exception_check (void);
+extern void android_exception_check_1 (jobject);
+extern void android_exception_check_2 (jobject, jobject);
+extern void android_exception_check_3 (jobject, jobject, jobject);
+extern void android_exception_check_4 (jobject, jobject, jobject, jobject);
+extern void android_exception_check_nonnull (void *, jobject);
+extern void android_exception_check_nonnull_1 (void *, jobject, jobject);
+
+extern void android_get_keysym_name (int, char *, size_t);
+extern void android_wait_event (void);
+extern void android_toggle_on_screen_keyboard (android_window, bool);
+extern _Noreturn void android_restart_emacs (void);
+extern int android_request_directory_access (void);
+extern int android_get_current_api_level (void)
+  __attribute__ ((pure));
+
+/* Define `android_get_current_api_level' to a macro that the compiler
+   knows will always return at least __ANDROID_API__.  */
+
+#define android_get_current_api_level()                                \
+  ({ int value;                                                        \
+                                                               \
+     value = (android_get_current_api_level) ();               \
+     eassume (value >= __ANDROID_API__); value; })
+
+
+
+/* Directory listing emulation.  */
+
+struct android_vdir;
+
+extern struct android_vdir *android_opendir (const char *);
+extern int android_dirfd (struct android_vdir *);
+extern struct dirent *android_readdir (struct android_vdir *);
+extern void android_closedir (struct android_vdir *);
+
+
+
+/* External asset manager interface.  */
+
+struct android_fd_or_asset
+{
+  /* The file descriptor.  */
+  int fd;
+
+  /* The asset.  If set, FD is not a real file descriptor.  */
+  void *asset;
+};
+
+extern struct android_fd_or_asset android_open_asset (const char *,
+                                                     int, mode_t);
+extern int android_close_asset (struct android_fd_or_asset);
+extern ssize_t android_asset_read_quit (struct android_fd_or_asset,
+                                       void *, size_t);
+extern ssize_t android_asset_read (struct android_fd_or_asset,
+                                  void *, size_t);
+extern off_t android_asset_lseek (struct android_fd_or_asset, off_t, int);
+extern int android_asset_fstat (struct android_fd_or_asset,
+                               struct stat *);
+
+
+
+/* Very miscellaneous functions.  */
+
+struct android_battery_state
+{
+  /* Battery charge level in integer percentage.  */
+  intmax_t capacity;
+
+  /* Battery charge level in microampere-hours.  */
+  intmax_t charge_counter;
+
+  /* Battery current in microampere-hours.  */
+  intmax_t current_average;
+
+  /* Instantaneous battery current in microampere-hours.  */
+  intmax_t current_now;
+
+  /* Estimate as to the amount of time remaining until the battery is
+     charged, in milliseconds.  */
+  intmax_t remaining;
+
+  /* Battery status.  The value is either:
+
+       2, if the battery is charging.
+       3, if the battery is discharging.
+       5, if the battery is full.
+       4, if the battery is not full or discharging,
+          but is not charging either.
+       1, if the battery state is unknown.  */
+  int status;
+
+  /* The power source of the battery.  Value is:
+
+       0, if on battery power.
+       1, for line power.
+       8, for dock power.
+       2, for USB power.
+       4, for wireless power.  */
+  int plugged;
+
+  /* The temperature of the battery in 10 * degrees centigrade.  */
+  int temperature;
+};
+
+extern Lisp_Object android_browse_url (Lisp_Object, Lisp_Object);
+extern int android_query_battery (struct android_battery_state *);
+extern void android_display_toast (const char *);
+
+
+
+/* Event loop functions.  */
+
+extern void android_check_query_urgent (void);
+extern int android_run_in_emacs_thread (void (*) (void *), void *);
+extern void android_write_event (union android_event *);
+
+extern unsigned int event_serial;
+
+
+
+/* Process related functions.  */
+extern int android_rewrite_spawn_argv (const char ***);
+
+#else /* ANDROID_STUBIFY */
+
+/* Define a substitute for use during Emacs compilation.  */
+
+#define android_is_special_directory(name, dir) ((const char *) NULL)
+
+#endif /* !ANDROID_STUBIFY */
+
+/* JNI functions should not be built when Emacs is stubbed out for the
+   build.  These should be documented in EmacsNative.java.  */
+
+#ifndef ANDROID_STUBIFY
+#include <jni.h>
+
+struct android_emacs_service
+{
+  jclass class;
+  jmethodID fill_rectangle;
+  jmethodID fill_polygon;
+  jmethodID draw_rectangle;
+  jmethodID draw_line;
+  jmethodID draw_point;
+  jmethodID clear_window;
+  jmethodID clear_area;
+  jmethodID ring_bell;
+  jmethodID query_tree;
+  jmethodID get_screen_width;
+  jmethodID get_screen_height;
+  jmethodID detect_mouse;
+  jmethodID name_keysym;
+  jmethodID browse_url;
+  jmethodID restart_emacs;
+  jmethodID update_ic;
+  jmethodID reset_ic;
+  jmethodID open_content_uri;
+  jmethodID check_content_uri;
+  jmethodID query_battery;
+  jmethodID update_extracted_text;
+  jmethodID update_cursor_anchor_info;
+  jmethodID get_document_authorities;
+  jmethodID request_directory_access;
+  jmethodID get_document_trees;
+  jmethodID document_id_from_name;
+  jmethodID get_tree_uri;
+  jmethodID stat_document;
+  jmethodID access_document;
+  jmethodID open_document_directory;
+  jmethodID read_directory_entry;
+  jmethodID open_document;
+  jmethodID create_document;
+  jmethodID create_directory;
+  jmethodID delete_document;
+  jmethodID rename_document;
+  jmethodID move_document;
+  jmethodID valid_authority;
+};
+
+extern JNIEnv *android_java_env;
+
+/* The EmacsService object.  */
+extern jobject emacs_service;
+
+/* Various methods associated with the EmacsService.  */
+extern struct android_emacs_service service_class;
+
+#define ANDROID_DELETE_LOCAL_REF(ref)                          \
+  ((*android_java_env)->DeleteLocalRef (android_java_env,      \
+                                       (ref)))
+
+#define NATIVE_NAME(name) Java_org_gnu_emacs_EmacsNative_##name
+
+/* Prologue which must be inserted before each JNI function.
+   See initEmacs for why.  */
+
+#if defined __i386__
+extern void *unused_pointer;
+
+#define JNI_STACK_ALIGNMENT_PROLOGUE                           \
+  __attribute__ ((aligned (32))) char stack_align_buffer[32];  \
+                                                               \
+  /* Trick GCC into not optimizing this variable away.  */     \
+  unused_pointer = stack_align_buffer;
+
+#else /* !__i386__ */
+#define JNI_STACK_ALIGNMENT_PROLOGUE ((void) 0)
+#endif /* __i386__ */
+
+#endif /* !ANDROID_STUBIFY */
+#endif /* _ANDROID_H_ */
diff --git a/src/androidfns.c b/src/androidfns.c
new file mode 100644
index 00000000000..0270f58b6b9
--- /dev/null
+++ b/src/androidfns.c
@@ -0,0 +1,3266 @@
+/* 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 <math.h>
+
+#include "lisp.h"
+#include "android.h"
+#include "androidterm.h"
+#include "blockinput.h"
+#include "keyboard.h"
+#include "buffer.h"
+#include "androidgui.h"
+
+#ifndef ANDROID_STUBIFY
+
+/* Some kind of reference count for the image cache.  */
+static ptrdiff_t image_cache_refcount;
+
+/* The frame of the currently visible tooltip, or nil if none.  */
+static Lisp_Object tip_frame;
+
+/* The window-system window corresponding to the frame of the
+   currently visible tooltip.  */
+static android_window tip_window;
+
+/* The X and Y deltas of the last call to `x-show-tip'.  */
+static Lisp_Object tip_dx, tip_dy;
+
+/* A timer that hides or deletes the currently visible tooltip when it
+   fires.  */
+static Lisp_Object tip_timer;
+
+/* STRING argument of last `x-show-tip' call.  */
+static Lisp_Object tip_last_string;
+
+/* Normalized FRAME argument of last `x-show-tip' call.  */
+static Lisp_Object tip_last_frame;
+
+/* PARMS argument of last `x-show-tip' call.  */
+static Lisp_Object tip_last_parms;
+
+#endif
+
+static struct android_display_info *
+android_display_info_for_name (Lisp_Object name)
+{
+  struct android_display_info *dpyinfo;
+
+  CHECK_STRING (name);
+
+  for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
+    {
+      if (!NILP (Fstring_equal (XCAR (dpyinfo->name_list_element),
+                               name)))
+       return dpyinfo;
+    }
+
+  error ("Cannot connect to Android if it was not initialized"
+        " at startup");
+}
+
+static struct android_display_info *
+check_android_display_info (Lisp_Object object)
+{
+  struct android_display_info *dpyinfo;
+  struct frame *sf, *f;
+  struct terminal *t;
+
+  if (NILP (object))
+    {
+      sf = XFRAME (selected_frame);
+
+      if (FRAME_ANDROID_P (sf) && FRAME_LIVE_P (sf))
+       dpyinfo = FRAME_DISPLAY_INFO (sf);
+      else if (x_display_list)
+       dpyinfo = x_display_list;
+      else
+       error ("Android windows are not in use or not initialized");
+    }
+  else if (TERMINALP (object))
+    {
+      t = decode_live_terminal (object);
+
+      if (t->type != output_android)
+        error ("Terminal %d is not an Android display", t->id);
+
+      dpyinfo = t->display_info.android;
+    }
+  else if (STRINGP (object))
+    dpyinfo = android_display_info_for_name (object);
+  else
+    {
+      f = decode_window_system_frame (object);
+      dpyinfo = FRAME_DISPLAY_INFO (f);
+    }
+
+  return dpyinfo;
+}
+
+Display_Info *
+check_x_display_info (Lisp_Object object)
+{
+  return check_android_display_info (object);
+}
+
+
+
+#ifndef ANDROID_STUBIFY
+
+void
+gamma_correct (struct frame *f, Emacs_Color *color)
+{
+  if (f->gamma)
+    {
+      color->red = pow (color->red / 65535.0, f->gamma) * 65535.0 + 0.5;
+      color->green = pow (color->green / 65535.0, f->gamma) * 65535.0 + 0.5;
+      color->blue = pow (color->blue / 65535.0, f->gamma) * 65535.0 + 0.5;
+    }
+}
+
+/* Decide if color named COLOR_NAME is valid for use on frame F.  If
+   so, return the RGB values in COLOR.  If ALLOC_P, allocate the
+   color.  Value is false if COLOR_NAME is invalid, or no color could
+   be allocated.  MAKE_INDEX is some mysterious argument used on
+   NS. */
+
+bool
+android_defined_color (struct frame *f, const char *color_name,
+                      Emacs_Color *color, bool alloc_p,
+                      bool make_index)
+{
+  bool success_p;
+
+  success_p = false;
+
+  block_input ();
+  success_p = android_parse_color (f, color_name, color);
+  if (success_p && alloc_p)
+    success_p = android_alloc_nearest_color (f, color);
+  unblock_input ();
+
+  return success_p;
+}
+
+/* Return the pixel color value for color COLOR_NAME on frame F.  If F
+   is a monochrome frame, return MONO_COLOR regardless of what ARG
+   says.  Signal an error if color can't be allocated.  */
+
+static unsigned long
+android_decode_color (struct frame *f, Lisp_Object color_name, int mono_color)
+{
+  Emacs_Color cdef;
+
+  CHECK_STRING (color_name);
+
+  if (android_defined_color (f, SSDATA (color_name), &cdef,
+                            true, false))
+    return cdef.pixel;
+
+  signal_error ("Undefined color", color_name);
+}
+
+static void
+android_set_parent_frame (struct frame *f, Lisp_Object new_value,
+                         Lisp_Object old_value)
+{
+  struct frame *p;
+
+  p = NULL;
+
+  if (!NILP (new_value)
+      && (!FRAMEP (new_value)
+         || !FRAME_LIVE_P (p = XFRAME (new_value))
+         || !FRAME_ANDROID_P (p)))
+    {
+      store_frame_param (f, Qparent_frame, old_value);
+      error ("Invalid specification of `parent-frame'");
+    }
+
+  if (p != FRAME_PARENT_FRAME (f))
+    {
+      block_input ();
+      android_reparent_window (FRAME_ANDROID_WINDOW (f),
+                              (p ? FRAME_ANDROID_WINDOW (p)
+                               : FRAME_DISPLAY_INFO (f)->root_window),
+                              f->left_pos, f->top_pos);
+      unblock_input ();
+
+      fset_parent_frame (f, new_value);
+    }
+
+  /* Update the fullscreen frame parameter as well.  */
+  FRAME_TERMINAL (f)->fullscreen_hook (f);
+}
+
+void
+android_implicitly_set_name (struct frame *f, Lisp_Object arg,
+                            Lisp_Object oldval)
+{
+
+}
+
+void
+android_explicitly_set_name (struct frame *f, Lisp_Object arg,
+                            Lisp_Object oldval)
+{
+
+}
+
+/* Set the number of lines used for the tool bar of frame F to VALUE.
+   VALUE not an integer, or < 0 means set the lines to zero.  OLDVAL
+   is the old number of tool bar lines.  This function changes the
+   height of all windows on frame F to match the new tool bar height.
+   The frame's height doesn't change.  */
+
+static void
+android_set_tool_bar_lines (struct frame *f, Lisp_Object value,
+                           Lisp_Object oldval)
+{
+  int nlines;
+
+  /* Treat tool bars like menu bars.  */
+  if (FRAME_MINIBUF_ONLY_P (f))
+    return;
+
+  /* Use VALUE only if an int >= 0.  */
+  if (RANGED_FIXNUMP (0, value, INT_MAX))
+    nlines = XFIXNAT (value);
+  else
+    nlines = 0;
+
+  android_change_tool_bar_height (f, nlines * FRAME_LINE_HEIGHT (f));
+}
+
+static void
+android_set_tool_bar_position (struct frame *f,
+                              Lisp_Object new_value,
+                              Lisp_Object old_value)
+{
+  if (!EQ (new_value, Qtop) && !EQ (new_value, Qbottom))
+    error ("Tool bar position must be either `top' or `bottom'");
+
+  if (EQ (new_value, old_value))
+    return;
+
+  /* Set the tool bar position.  */
+  fset_tool_bar_position (f, new_value);
+
+  /* Now reconfigure frame glyphs to place the tool bar at the
+     bottom.  While the inner height has not changed, call
+     `resize_frame_windows' to place each of the windows at its
+     new position.  */
+
+  adjust_frame_size (f, -1, -1, 3, false, Qtool_bar_position);
+  adjust_frame_glyphs (f);
+  SET_FRAME_GARBAGED (f);
+
+  if (FRAME_ANDROID_WINDOW (f))
+    android_clear_under_internal_border (f);
+}
+
+void
+android_change_tool_bar_height (struct frame *f, int height)
+{
+  int unit = FRAME_LINE_HEIGHT (f);
+  int old_height = FRAME_TOOL_BAR_HEIGHT (f);
+  int lines = (height + unit - 1) / unit;
+  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
+
+  /* Make sure we redisplay all windows in this frame.  */
+  fset_redisplay (f);
+
+  FRAME_TOOL_BAR_HEIGHT (f) = height;
+  FRAME_TOOL_BAR_LINES (f) = lines;
+  store_frame_param (f, Qtool_bar_lines, make_fixnum (lines));
+
+  if (FRAME_ANDROID_WINDOW (f) && FRAME_TOOL_BAR_HEIGHT (f) == 0)
+    {
+      clear_frame (f);
+      clear_current_matrices (f);
+    }
+
+  if ((height < old_height) && WINDOWP (f->tool_bar_window))
+    clear_glyph_matrix (XWINDOW (f->tool_bar_window)->current_matrix);
+
+  if (!f->tool_bar_resized)
+    {
+      /* As long as tool_bar_resized is false, effectively try to change
+        F's native height.  */
+      if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
+       adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+                          1, false, Qtool_bar_lines);
+      else
+       adjust_frame_size (f, -1, -1, 4, false, Qtool_bar_lines);
+
+      f->tool_bar_resized =  f->tool_bar_redisplayed;
+    }
+  else
+    /* Any other change may leave the native size of F alone.  */
+    adjust_frame_size (f, -1, -1, 3, false, Qtool_bar_lines);
+
+  /* adjust_frame_size might not have done anything, garbage frame
+     here.  */
+  adjust_frame_glyphs (f);
+  SET_FRAME_GARBAGED (f);
+}
+
+/* Set the number of lines used for the tab bar of frame F to VALUE.
+   VALUE not an integer, or < 0 means set the lines to zero.  OLDVAL
+   is the old number of tab bar lines.  This function may change the
+   height of all windows on frame F to match the new tab bar height.
+   The frame's height may change if frame_inhibit_implied_resize was
+   set accordingly.  */
+
+static void
+android_set_tab_bar_lines (struct frame *f, Lisp_Object value,
+                          Lisp_Object oldval)
+{
+  int olines;
+  int nlines;
+
+  olines = FRAME_TAB_BAR_LINES (f);
+
+  /* Treat tab bars like menu bars.  */
+  if (FRAME_MINIBUF_ONLY_P (f))
+    return;
+
+  /* Use VALUE only if an int >= 0.  */
+  if (RANGED_FIXNUMP (0, value, INT_MAX))
+    nlines = XFIXNAT (value);
+  else
+    nlines = 0;
+
+  if (nlines != olines && (olines == 0 || nlines == 0))
+    android_change_tab_bar_height (f, nlines * FRAME_LINE_HEIGHT (f));
+}
+
+void
+android_change_tab_bar_height (struct frame *f, int height)
+{
+  int unit, old_height, lines;
+  Lisp_Object fullscreen;
+
+  unit = FRAME_LINE_HEIGHT (f);
+  old_height = FRAME_TAB_BAR_HEIGHT (f);
+  fullscreen = get_frame_param (f, Qfullscreen);
+
+  /* This differs from the tool bar code in that the tab bar height is
+     not rounded up.  Otherwise, if redisplay_tab_bar decides to grow
+     the tab bar by even 1 pixel, FRAME_TAB_BAR_LINES will be changed,
+     leading to the tab bar height being incorrectly set upon the next
+     call to android_set_font.  (bug#59285) */
+  lines = height / unit;
+
+  /* Make sure we redisplay all windows in this frame.  */
+  fset_redisplay (f);
+
+  /* Recalculate tab bar and frame text sizes.  */
+  FRAME_TAB_BAR_HEIGHT (f) = height;
+  FRAME_TAB_BAR_LINES (f) = lines;
+  store_frame_param (f, Qtab_bar_lines, make_fixnum (lines));
+
+  if (FRAME_ANDROID_WINDOW (f) && FRAME_TAB_BAR_HEIGHT (f) == 0)
+    {
+      clear_frame (f);
+      clear_current_matrices (f);
+    }
+
+  if ((height < old_height) && WINDOWP (f->tab_bar_window))
+    clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
+
+  if (!f->tab_bar_resized)
+    {
+      /* As long as tab_bar_resized is false, effectively try to change
+        F's native height.  */
+      if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
+       adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+                          1, false, Qtab_bar_lines);
+      else
+       adjust_frame_size (f, -1, -1, 4, false, Qtab_bar_lines);
+
+      f->tab_bar_resized = f->tab_bar_redisplayed;
+    }
+  else
+    /* Any other change may leave the native size of F alone.  */
+    adjust_frame_size (f, -1, -1, 3, false, Qtab_bar_lines);
+
+  /* adjust_frame_size might not have done anything, garbage frame
+     here.  */
+  adjust_frame_glyphs (f);
+  SET_FRAME_GARBAGED (f);
+}
+
+void
+android_set_scroll_bar_default_height (struct frame *f)
+{
+  int height;
+
+  height = FRAME_LINE_HEIGHT (f);
+
+  /* The height of a non-toolkit scrollbar is 14 pixels.  */
+  FRAME_CONFIG_SCROLL_BAR_LINES (f) = (14 + height - 1) / height;
+
+  /* Use all of that space (aside from required margins) for the
+     scroll bar.  */
+  FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) = 14;
+}
+
+void
+android_set_scroll_bar_default_width (struct frame *f)
+{
+  int unit;
+
+  unit = FRAME_COLUMN_WIDTH (f);
+
+  FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + unit - 1) / unit;
+  FRAME_CONFIG_SCROLL_BAR_WIDTH (f)
+    = FRAME_CONFIG_SCROLL_BAR_COLS (f) * unit;
+}
+
+
+/* Verify that the icon position args for this window are valid.  */
+
+static void
+android_icon_verify (struct frame *f, Lisp_Object parms)
+{
+  Lisp_Object icon_x, icon_y;
+
+  /* Set the position of the icon.  Note that twm groups all
+     icons in an icon window.  */
+  icon_x = gui_frame_get_and_record_arg (f, parms, Qicon_left, 0, 0,
+                                        RES_TYPE_NUMBER);
+  icon_y = gui_frame_get_and_record_arg (f, parms, Qicon_top, 0, 0,
+                                        RES_TYPE_NUMBER);
+
+  if (!BASE_EQ (icon_x, Qunbound) && !BASE_EQ (icon_y, Qunbound))
+    {
+      CHECK_FIXNUM (icon_x);
+      CHECK_FIXNUM (icon_y);
+    }
+  else if (!BASE_EQ (icon_x, Qunbound) || !BASE_EQ (icon_y, Qunbound))
+    error ("Both left and top icon corners of icon must be specified");
+}
+
+/* Handle the icon stuff for this window.  Perhaps later we might
+   want an x_set_icon_position which can be called interactively as
+   well.  */
+
+static void
+android_icon (struct frame *f, Lisp_Object parms)
+{
+  /* Set the position of the icon.  Note that twm groups all
+     icons in an icon window.  */
+  Lisp_Object icon_x
+    = gui_frame_get_and_record_arg (f, parms, Qicon_left, 0, 0,
+                                   RES_TYPE_NUMBER);
+  Lisp_Object icon_y
+    = gui_frame_get_and_record_arg (f, parms, Qicon_top, 0, 0,
+                                   RES_TYPE_NUMBER);
+
+  bool xgiven = !BASE_EQ (icon_x, Qunbound);
+  bool ygiven = !BASE_EQ (icon_y, Qunbound);
+
+  if (xgiven != ygiven)
+    error ("Both left and top icon corners of icon must be specified");
+
+  if (xgiven)
+    {
+      check_integer_range (icon_x, INT_MIN, INT_MAX);
+      check_integer_range (icon_y, INT_MIN, INT_MAX);
+    }
+
+  /* Now return as this is not supported on Android.  */
+}
+
+/* Make the GCs needed for this window, setting the background
+   color.  */
+
+static void
+android_make_gc (struct frame *f)
+{
+  struct android_gc_values gc_values;
+
+  block_input ();
+
+  /* Create the GCs of this frame.
+     Note that many default values are used.  */
+
+  gc_values.foreground = FRAME_FOREGROUND_PIXEL (f);
+  gc_values.background = FRAME_BACKGROUND_PIXEL (f);
+  f->output_data.android->normal_gc
+    = android_create_gc (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND,
+                        &gc_values);
+
+  /* Reverse video style.  */
+  gc_values.foreground = FRAME_BACKGROUND_PIXEL (f);
+  gc_values.background = FRAME_FOREGROUND_PIXEL (f);
+  f->output_data.android->reverse_gc
+    = android_create_gc (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND,
+                        &gc_values);
+
+  /* Cursor has cursor-color background, background-color foreground.  */
+  gc_values.foreground = FRAME_BACKGROUND_PIXEL (f);
+  gc_values.background = f->output_data.android->cursor_pixel;
+  f->output_data.android->cursor_gc
+    = android_create_gc (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND,
+                        &gc_values);
+  unblock_input ();
+}
+
+
+/* Free what was allocated in android_make_gc.  */
+
+void
+android_free_gcs (struct frame *f)
+{
+  block_input ();
+
+  if (f->output_data.android->normal_gc)
+    {
+      android_free_gc (f->output_data.android->normal_gc);
+      f->output_data.android->normal_gc = 0;
+    }
+
+  if (f->output_data.android->reverse_gc)
+    {
+      android_free_gc (f->output_data.android->reverse_gc);
+      f->output_data.android->reverse_gc = 0;
+    }
+
+  if (f->output_data.android->cursor_gc)
+    {
+      android_free_gc (f->output_data.android->cursor_gc);
+      f->output_data.android->cursor_gc = 0;
+    }
+
+  unblock_input ();
+}
+
+/* Handler for signals raised during x_create_frame and
+   Fx_create_tip_frame.  FRAME is the frame which is partially
+   constructed.  */
+
+static Lisp_Object
+unwind_create_frame (Lisp_Object frame)
+{
+  struct frame *f = XFRAME (frame);
+
+  /* If frame is already dead, nothing to do.  This can happen if the
+     display is disconnected after the frame has become official, but
+     before Fx_create_frame removes the unwind protect.  */
+  if (!FRAME_LIVE_P (f))
+    return Qnil;
+
+  /* If frame is ``official'', nothing to do.  */
+  if (NILP (Fmemq (frame, Vframe_list)))
+    {
+      /* If the frame's image cache refcount is still the same as our
+        private shadow variable, it means we are unwinding a frame
+        for which we didn't yet call init_frame_faces, where the
+        refcount is incremented.  Therefore, we increment it here, so
+        that free_frame_faces, called in x_free_frame_resources
+        below, will not mistakenly decrement the counter that was not
+        incremented yet to account for this new frame.  */
+      if (FRAME_IMAGE_CACHE (f) != NULL
+         && FRAME_IMAGE_CACHE (f)->refcount == image_cache_refcount)
+       FRAME_IMAGE_CACHE (f)->refcount++;
+
+      android_free_frame_resources (f);
+      free_glyphs (f);
+      return Qt;
+    }
+
+  return Qnil;
+}
+
+static void
+do_unwind_create_frame (Lisp_Object frame)
+{
+  unwind_create_frame (frame);
+}
+
+void
+android_default_font_parameter (struct frame *f, Lisp_Object parms)
+{
+  struct android_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+  Lisp_Object font_param = gui_display_get_arg (dpyinfo, parms, Qfont, NULL, 
NULL,
+                                                RES_TYPE_STRING);
+  Lisp_Object font = Qnil;
+  if (BASE_EQ (font_param, Qunbound))
+    font_param = Qnil;
+
+  if (NILP (font))
+    font = (!NILP (font_param)
+           ? font_param
+           : gui_display_get_arg (dpyinfo, parms,
+                                  Qfont, "font", "Font",
+                                  RES_TYPE_STRING));
+
+  if (! FONTP (font) && ! STRINGP (font))
+    {
+      const char *names[] = {
+       "Droid Sans Mono-12",
+       "Monospace-12",
+       "DroidSansMono-12",
+       NULL
+      };
+      int i;
+
+      for (i = 0; names[i]; i++)
+       {
+         font = font_open_by_name (f, build_unibyte_string (names[i]));
+         if (! NILP (font))
+           break;
+       }
+
+      if (NILP (font))
+       error ("No suitable font was found");
+    }
+
+  gui_default_parameter (f, parms, Qfont, font, "font", "Font", 
RES_TYPE_STRING);
+}
+
+static void
+android_create_frame_window (struct frame *f)
+{
+  struct android_set_window_attributes attributes;
+  enum android_window_value_mask attribute_mask;
+
+  attributes.background_pixel = FRAME_BACKGROUND_PIXEL (f);
+  attribute_mask = ANDROID_CW_BACK_PIXEL;
+
+  block_input ();
+  FRAME_ANDROID_WINDOW (f)
+    = android_create_window (FRAME_DISPLAY_INFO (f)->root_window,
+                            f->left_pos,
+                            f->top_pos,
+                            FRAME_PIXEL_WIDTH (f),
+                            FRAME_PIXEL_HEIGHT (f),
+                            attribute_mask, &attributes);
+  unblock_input ();
+}
+
+#endif /* ANDROID_STUBIFY */
+
+
+
+DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
+       1, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object parms)
+{
+#ifdef ANDROID_STUBIFY
+  error ("Android cross-compilation stub called!");
+  return Qnil;
+#else
+  struct frame *f;
+  Lisp_Object frame, tem;
+  Lisp_Object name;
+  bool minibuffer_only;
+  bool undecorated, override_redirect;
+  long window_prompting;
+  specpdl_ref count;
+  Lisp_Object display;
+  struct android_display_info *dpyinfo;
+  Lisp_Object parent, parent_frame;
+  struct kboard *kb;
+
+  minibuffer_only = false;
+  undecorated = false;
+  override_redirect = false;
+  window_prompting = 0;
+  count = SPECPDL_INDEX ();
+  dpyinfo = NULL;
+
+  /* Not actually used, but be consistent with X.  */
+  ((void) window_prompting);
+
+  parms = Fcopy_alist (parms);
+
+  /* Use this general default value to start with
+     until we know if this frame has a specified name.  */
+  Vx_resource_name = Vinvocation_name;
+
+  display = gui_display_get_arg (dpyinfo, parms, Qterminal, 0, 0,
+                                 RES_TYPE_NUMBER);
+  if (BASE_EQ (display, Qunbound))
+    display = gui_display_get_arg (dpyinfo, parms, Qdisplay, 0, 0,
+                                   RES_TYPE_STRING);
+  if (BASE_EQ (display, Qunbound))
+    display = Qnil;
+  dpyinfo = check_android_display_info (display);
+  kb = dpyinfo->terminal->kboard;
+
+  if (!dpyinfo->terminal->name)
+    error ("Terminal is not live, can't create new frames on it");
+
+  name = gui_display_get_arg (dpyinfo, parms, Qname, "name", "Name",
+                              RES_TYPE_STRING);
+  if (!STRINGP (name)
+      && ! BASE_EQ (name, Qunbound)
+      && ! NILP (name))
+    error ("Invalid frame name--not a string or nil");
+
+  if (STRINGP (name))
+    Vx_resource_name = name;
+
+  /* See if parent window is specified.  */
+  parent = gui_display_get_arg (dpyinfo, parms, Qparent_id, NULL, NULL,
+                                RES_TYPE_NUMBER);
+  if (BASE_EQ (parent, Qunbound))
+    parent = Qnil;
+  if (! NILP (parent))
+    CHECK_FIXNUM (parent);
+
+  frame = Qnil;
+  tem = gui_display_get_arg (dpyinfo,
+                             parms, Qminibuffer, "minibuffer", "Minibuffer",
+                             RES_TYPE_SYMBOL);
+  if (EQ (tem, Qnone) || NILP (tem))
+    f = make_frame_without_minibuffer (Qnil, kb, display);
+  else if (EQ (tem, Qonly))
+    {
+      f = make_minibuffer_frame ();
+      minibuffer_only = true;
+    }
+  else if (WINDOWP (tem))
+    f = make_frame_without_minibuffer (tem, kb, display);
+  else
+    f = make_frame (true);
+
+  parent_frame = gui_display_get_arg (dpyinfo,
+                                      parms,
+                                      Qparent_frame,
+                                      NULL,
+                                      NULL,
+                                      RES_TYPE_SYMBOL);
+  /* Accept parent-frame iff parent-id was not specified.  */
+  if (!NILP (parent)
+      || BASE_EQ (parent_frame, Qunbound)
+      || NILP (parent_frame)
+      || !FRAMEP (parent_frame)
+      || !FRAME_LIVE_P (XFRAME (parent_frame))
+      || !FRAME_ANDROID_P (XFRAME (parent_frame)))
+    parent_frame = Qnil;
+
+  fset_parent_frame (f, parent_frame);
+  store_frame_param (f, Qparent_frame, parent_frame);
+
+  if (!NILP (tem = (gui_display_get_arg (dpyinfo,
+                                         parms,
+                                         Qundecorated,
+                                         NULL,
+                                         NULL,
+                                         RES_TYPE_BOOLEAN)))
+      && !(BASE_EQ (tem, Qunbound)))
+    undecorated = true;
+
+  FRAME_UNDECORATED (f) = undecorated;
+  store_frame_param (f, Qundecorated, undecorated ? Qt : Qnil);
+
+  if (!NILP (tem = (gui_display_get_arg (dpyinfo,
+                                         parms,
+                                         Qoverride_redirect,
+                                         NULL,
+                                         NULL,
+                                         RES_TYPE_BOOLEAN)))
+      && !(BASE_EQ (tem, Qunbound)))
+    override_redirect = true;
+
+  FRAME_OVERRIDE_REDIRECT (f) = override_redirect;
+  store_frame_param (f, Qoverride_redirect, override_redirect ? Qt : Qnil);
+
+  XSETFRAME (frame, f);
+
+  f->terminal = dpyinfo->terminal;
+
+  f->output_method = output_android;
+  f->output_data.android = xzalloc (sizeof *f->output_data.android);
+  FRAME_FONTSET (f) = -1;
+  f->output_data.android->scroll_bar_foreground_pixel = -1;
+  f->output_data.android->scroll_bar_background_pixel = -1;
+  f->output_data.android->white_relief.pixel = -1;
+  f->output_data.android->black_relief.pixel = -1;
+
+  fset_icon_name (f, gui_display_get_arg (dpyinfo,
+                                          parms,
+                                          Qicon_name,
+                                          "iconName",
+                                          "Title",
+                                          RES_TYPE_STRING));
+  if (! STRINGP (f->icon_name))
+    fset_icon_name (f, Qnil);
+
+  FRAME_DISPLAY_INFO (f) = dpyinfo;
+
+  /* With FRAME_DISPLAY_INFO set up, this unwind-protect is safe.  */
+  record_unwind_protect (do_unwind_create_frame, frame);
+
+  /* These colors will be set anyway later, but it's important
+     to get the color reference counts right, so initialize them!
+
+     (Not really on Android, but it's best to be consistent with
+     X.) */
+  {
+    Lisp_Object black;
+
+    /* Function x_decode_color can signal an error.  Make
+       sure to initialize color slots so that we won't try
+       to free colors we haven't allocated.  */
+    FRAME_FOREGROUND_PIXEL (f) = -1;
+    FRAME_BACKGROUND_PIXEL (f) = -1;
+    f->output_data.android->cursor_pixel = -1;
+    f->output_data.android->cursor_foreground_pixel = -1;
+    f->output_data.android->mouse_pixel = -1;
+
+    black = build_string ("black");
+    FRAME_FOREGROUND_PIXEL (f)
+      = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+    FRAME_BACKGROUND_PIXEL (f)
+      = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+    f->output_data.android->cursor_pixel
+      = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+    f->output_data.android->cursor_foreground_pixel
+      = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+    f->output_data.android->mouse_pixel
+      = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+  }
+
+  /* Set the name; the functions to which we pass f expect the name to
+     be set.  */
+  if (BASE_EQ (name, Qunbound) || NILP (name))
+    {
+      fset_name (f, build_string ("GNU Emacs"));
+      f->explicit_name = false;
+    }
+  else
+    {
+      fset_name (f, name);
+      f->explicit_name = true;
+      /* Use the frame's title when getting resources for this frame.  */
+      specbind (Qx_resource_name, name);
+    }
+
+  register_font_driver (&androidfont_driver, f);
+  register_font_driver (&android_sfntfont_driver, f);
+
+  image_cache_refcount = (FRAME_IMAGE_CACHE (f)
+                         ? FRAME_IMAGE_CACHE (f)->refcount
+                         : 0);
+
+  gui_default_parameter (f, parms, Qfont_backend, Qnil,
+                         "fontBackend", "FontBackend", RES_TYPE_STRING);
+
+  /* Extract the window parameters from the supplied values
+     that are needed to determine window geometry.  */
+  android_default_font_parameter (f, parms);
+  if (!FRAME_FONT (f))
+    {
+      delete_frame (frame, Qnoelisp);
+      error ("Invalid frame font");
+    }
+
+  if (NILP (Fassq (Qinternal_border_width, parms)))
+    {
+      Lisp_Object value;
+
+      value = gui_display_get_arg (dpyinfo, parms, Qinternal_border_width,
+                                   "internalBorder", "internalBorder",
+                                   RES_TYPE_NUMBER);
+      if (! BASE_EQ (value, Qunbound))
+       parms = Fcons (Fcons (Qinternal_border_width, value),
+                      parms);
+    }
+
+  gui_default_parameter (f, parms, Qinternal_border_width,
+                        make_fixnum (0),
+                        "internalBorderWidth", "internalBorderWidth",
+                        RES_TYPE_NUMBER);
+
+  /* Same for child frames.  */
+  if (NILP (Fassq (Qchild_frame_border_width, parms)))
+    {
+      Lisp_Object value;
+
+      value = gui_display_get_arg (dpyinfo, parms, Qchild_frame_border_width,
+                                   "childFrameBorder", "childFrameBorder",
+                                   RES_TYPE_NUMBER);
+      if (! BASE_EQ (value, Qunbound))
+       parms = Fcons (Fcons (Qchild_frame_border_width, value),
+                      parms);
+    }
+
+  gui_default_parameter (f, parms, Qchild_frame_border_width, Qnil,
+                        "childFrameBorderWidth", "childFrameBorderWidth",
+                        RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qright_divider_width, make_fixnum (0),
+                         NULL, NULL, RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0),
+                         NULL, NULL, RES_TYPE_NUMBER);
+
+  /* `vertical-scroll-bars' defaults to nil on Android as a
+     consequence of scroll bars not being supported at all.  */
+
+  gui_default_parameter (f, parms, Qvertical_scroll_bars, Qnil,
+                         "verticalScrollBars", "ScrollBars",
+                         RES_TYPE_SYMBOL);
+  gui_default_parameter (f, parms, Qhorizontal_scroll_bars, Qnil,
+                         "horizontalScrollBars", "ScrollBars",
+                         RES_TYPE_SYMBOL);
+
+  /* Also do the stuff which must be set before the window exists.  */
+  gui_default_parameter (f, parms, Qforeground_color, build_string ("black"),
+                         "foreground", "Foreground", RES_TYPE_STRING);
+  gui_default_parameter (f, parms, Qbackground_color, build_string ("white"),
+                         "background", "Background", RES_TYPE_STRING);
+  gui_default_parameter (f, parms, Qmouse_color, build_string ("black"),
+                         "pointerColor", "Foreground", RES_TYPE_STRING);
+  gui_default_parameter (f, parms, Qborder_color, build_string ("black"),
+                         "borderColor", "BorderColor", RES_TYPE_STRING);
+  gui_default_parameter (f, parms, Qscreen_gamma, Qnil,
+                         "screenGamma", "ScreenGamma", RES_TYPE_FLOAT);
+  gui_default_parameter (f, parms, Qline_spacing, Qnil,
+                         "lineSpacing", "LineSpacing", RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qleft_fringe, Qnil,
+                         "leftFringe", "LeftFringe", RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qright_fringe, Qnil,
+                         "rightFringe", "RightFringe", RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qno_special_glyphs, Qnil,
+                         NULL, NULL, RES_TYPE_BOOLEAN);
+
+#if 0
+  android_default_scroll_bar_color_parameter (f, parms, Qscroll_bar_foreground,
+                                             "scrollBarForeground",
+                                             "ScrollBarForeground", true);
+  android_default_scroll_bar_color_parameter (f, parms, Qscroll_bar_background,
+                                             "scrollBarBackground",
+                                             "ScrollBarBackground", false);
+#endif
+
+  /* Init faces before gui_default_parameter is called for the
+     scroll-bar-width parameter because otherwise we end up in
+     init_iterator with a null face cache, which should not
+     happen.  */
+
+  init_frame_faces (f);
+
+  tem = gui_display_get_arg (dpyinfo, parms, Qmin_width, NULL, NULL,
+                             RES_TYPE_NUMBER);
+  if (FIXNUMP (tem))
+    store_frame_param (f, Qmin_width, tem);
+  tem = gui_display_get_arg (dpyinfo, parms, Qmin_height, NULL, NULL,
+                             RES_TYPE_NUMBER);
+  if (FIXNUMP (tem))
+    store_frame_param (f, Qmin_height, tem);
+
+  adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
+                    FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 5, true,
+                    Qx_create_frame_1);
+
+  /* Set the menu-bar-lines and tool-bar-lines parameters.  We don't
+     look up the X resources controlling the menu-bar and tool-bar
+     here; they are processed specially at startup, and reflected in
+     the values of the mode variables.  */
+
+  gui_default_parameter (f, parms, Qmenu_bar_lines,
+                         NILP (Vmenu_bar_mode)
+                         ? make_fixnum (0) : make_fixnum (1),
+                         NULL, NULL, RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qtab_bar_lines,
+                         NILP (Vtab_bar_mode)
+                         ? make_fixnum (0) : make_fixnum (1),
+                         NULL, NULL, RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qtool_bar_lines,
+                         NILP (Vtool_bar_mode)
+                         ? make_fixnum (0) : make_fixnum (1),
+                         NULL, NULL, RES_TYPE_NUMBER);
+
+  gui_default_parameter (f, parms, Qbuffer_predicate, Qnil,
+                         "bufferPredicate", "BufferPredicate",
+                         RES_TYPE_SYMBOL);
+  gui_default_parameter (f, parms, Qtitle, Qnil,
+                         "title", "Title", RES_TYPE_STRING);
+  gui_default_parameter (f, parms, Qwait_for_wm, Qt,
+                         "waitForWM", "WaitForWM", RES_TYPE_BOOLEAN);
+  gui_default_parameter (f, parms, Qtool_bar_position,
+                         FRAME_TOOL_BAR_POSITION (f), 0, 0, RES_TYPE_SYMBOL);
+  gui_default_parameter (f, parms, Qinhibit_double_buffering, Qnil,
+                         "inhibitDoubleBuffering", "InhibitDoubleBuffering",
+                         RES_TYPE_BOOLEAN);
+
+  /* Compute the size of the X window.  */
+  window_prompting = gui_figure_window_size (f, parms, true, true);
+
+  tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0,
+                             RES_TYPE_BOOLEAN);
+  f->no_split = minibuffer_only || EQ (tem, Qt);
+
+  android_icon_verify (f, parms);
+  android_create_frame_window (f);
+  android_icon (f, parms);
+  android_make_gc (f);
+
+  /* Now consider the frame official.  */
+  f->terminal->reference_count++;
+  Vframe_list = Fcons (frame, Vframe_list);
+
+  /* We need to do this after creating the window, so that the
+     icon-creation functions can say whose icon they're
+     describing.  */
+  gui_default_parameter (f, parms, Qicon_type, Qt,
+                         "bitmapIcon", "BitmapIcon", RES_TYPE_BOOLEAN);
+
+  gui_default_parameter (f, parms, Qauto_raise, Qnil,
+                         "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+  gui_default_parameter (f, parms, Qauto_lower, Qnil,
+                         "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+  gui_default_parameter (f, parms, Qcursor_type, Qbox,
+                         "cursorType", "CursorType", RES_TYPE_SYMBOL);
+  /* Scroll bars are not supported on Android, as they are near
+     useless.  */
+  gui_default_parameter (f, parms, Qscroll_bar_width, Qnil,
+                         "scrollBarWidth", "ScrollBarWidth",
+                         RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qscroll_bar_height, Qnil,
+                         "scrollBarHeight", "ScrollBarHeight",
+                         RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qalpha, Qnil,
+                         "alpha", "Alpha", RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qalpha_background, Qnil,
+                         "alphaBackground", "AlphaBackground", 
RES_TYPE_NUMBER);
+
+  if (!NILP (parent_frame))
+    {
+      struct frame *p = XFRAME (parent_frame);
+
+      block_input ();
+      android_reparent_window (FRAME_ANDROID_WINDOW (f),
+                              FRAME_ANDROID_WINDOW (p),
+                              f->left_pos, f->top_pos);
+      unblock_input ();
+    }
+
+  gui_default_parameter (f, parms, Qno_focus_on_map, Qnil,
+                         NULL, NULL, RES_TYPE_BOOLEAN);
+  gui_default_parameter (f, parms, Qno_accept_focus, Qnil,
+                         NULL, NULL, RES_TYPE_BOOLEAN);
+
+  /* Consider frame official, now.  */
+  f->can_set_window_size = true;
+
+  adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+                    0, true, Qx_create_frame_2);
+
+  /* Process fullscreen parameter here in the hope that normalizing a
+     fullheight/fullwidth frame will produce the size set by the last
+     adjust_frame_size call.  Note that Android only supports the
+     `maximized' state.  */
+  gui_default_parameter (f, parms, Qfullscreen, Qmaximized,
+                         "fullscreen", "Fullscreen", RES_TYPE_SYMBOL);
+
+  /* When called from `x-create-frame-with-faces' visibility is
+     always explicitly nil.  */
+  Lisp_Object visibility
+    = gui_display_get_arg (dpyinfo, parms, Qvisibility, 0, 0,
+                          RES_TYPE_SYMBOL);
+  Lisp_Object height
+    = gui_display_get_arg (dpyinfo, parms, Qheight, 0, 0, RES_TYPE_NUMBER);
+  Lisp_Object width
+    = gui_display_get_arg (dpyinfo, parms, Qwidth, 0, 0, RES_TYPE_NUMBER);
+
+  if (EQ (visibility, Qicon))
+    {
+      f->was_invisible = true;
+      android_iconify_frame (f);
+    }
+  else
+    {
+      if (BASE_EQ (visibility, Qunbound))
+       visibility = Qt;
+
+      if (!NILP (visibility))
+       android_make_frame_visible (f);
+      else
+       f->was_invisible = true;
+    }
+
+  /* Leave f->was_invisible true only if height or width were
+     specified too.  This takes effect only when we are not called
+     from `x-create-frame-with-faces' (see above comment).  */
+  f->was_invisible
+    = (f->was_invisible
+       && (!BASE_EQ (height, Qunbound) || !BASE_EQ (width, Qunbound)));
+
+  store_frame_param (f, Qvisibility, visibility);
+
+  /* Set whether or not frame synchronization is enabled.  */
+  gui_default_parameter (f, parms, Quse_frame_synchronization, Qt,
+                        NULL, NULL, RES_TYPE_BOOLEAN);
+
+  /* Works iff frame has been already mapped.  */
+  gui_default_parameter (f, parms, Qskip_taskbar, Qnil,
+                         NULL, NULL, RES_TYPE_BOOLEAN);
+  /* The `z-group' parameter works only for visible frames.  */
+  gui_default_parameter (f, parms, Qz_group, Qnil,
+                         NULL, NULL, RES_TYPE_SYMBOL);
+
+  /* Initialize `default-minibuffer-frame' in case this is the first
+     frame on this terminal.  */
+  if (FRAME_HAS_MINIBUF_P (f)
+      && (!FRAMEP (KVAR (kb, Vdefault_minibuffer_frame))
+          || !FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame)))))
+    kset_default_minibuffer_frame (kb, frame);
+
+  /* All remaining specified parameters, which have not been "used" by
+     gui_display_get_arg and friends, now go in the misc. alist of the
+     frame.  */
+  for (tem = parms; CONSP (tem); tem = XCDR (tem))
+    if (CONSP (XCAR (tem)) && !NILP (XCAR (XCAR (tem))))
+      fset_param_alist (f, Fcons (XCAR (tem), f->param_alist));
+
+  /* Make sure windows on this frame appear in calls to next-window
+     and similar functions.  */
+  Vwindow_list = Qnil;
+
+  return unbind_to (count, frame);
+#endif
+}
+
+DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p,
+       1, 2, 0, doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object color, Lisp_Object frame)
+{
+#ifdef ANDROID_STUBIFY
+  error ("Android cross-compilation stub called!");
+  return Qnil;
+#else
+  Emacs_Color foo;
+  struct frame *f;
+
+  f = decode_window_system_frame (frame);
+
+  CHECK_STRING (color);
+
+  if (android_defined_color (f, SSDATA (color), &foo, false, false))
+    return Qt;
+  else
+    return Qnil;
+#endif
+}
+
+DEFUN ("xw-color-values", Fxw_color_values, Sxw_color_values, 1, 2,
+       0, doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object color, Lisp_Object frame)
+{
+#ifdef ANDROID_STUBIFY
+  error ("Android cross-compilation stub called!");
+  return Qnil;
+#else
+  Emacs_Color foo;
+  struct frame *f;
+
+  f = decode_window_system_frame (frame);
+
+  CHECK_STRING (color);
+
+  if (android_defined_color (f, SSDATA (color), &foo, false, false))
+    return list3i (foo.red, foo.green, foo.blue);
+  else
+    return Qnil;
+#endif
+}
+
+DEFUN ("xw-display-color-p", Fxw_display_color_p,
+       Sxw_display_color_p, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  return Qt;
+}
+
+DEFUN ("x-display-grayscale-p", Fx_display_grayscale_p,
+       Sx_display_grayscale_p, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  return Qnil;
+}
+
+DEFUN ("x-display-pixel-width", Fx_display_pixel_width,
+       Sx_display_pixel_width, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+  error ("Android cross-compilation stub called!");
+  return Qnil;
+#else
+  return make_fixnum (android_get_screen_width ());
+#endif
+}
+
+DEFUN ("x-display-pixel-height", Fx_display_pixel_height,
+       Sx_display_pixel_height, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+  error ("Android cross-compilation stub called!");
+  return Qnil;
+#else
+  return make_fixnum (android_get_screen_height ());
+#endif
+}
+
+DEFUN ("x-display-planes", Fx_display_planes, Sx_display_planes,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  struct android_display_info *dpyinfo;
+
+  dpyinfo = check_android_display_info (terminal);
+
+  return make_fixnum (dpyinfo->n_planes);
+}
+
+DEFUN ("x-display-color-cells", Fx_display_color_cells, Sx_display_color_cells,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  struct android_display_info *dpyinfo;
+  int nr_planes;
+
+  dpyinfo = check_android_display_info (terminal);
+  nr_planes = dpyinfo->n_planes;
+
+  /* Truncate nr_planes to 24 to avoid integer overflow.  */
+
+  if (nr_planes > 24)
+    nr_planes = 24;
+
+  return make_fixnum (1 << nr_planes);
+}
+
+DEFUN ("x-server-vendor", Fx_server_vendor, Sx_server_vendor, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+  error ("Android cross-compilation stub called!");
+  return Qnil;
+#else
+  check_android_display_info (terminal);
+  return Vandroid_build_manufacturer;
+#endif
+}
+
+DEFUN ("x-server-version", Fx_server_version, Sx_server_version, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+  error ("Android cross-compilation stub called!");
+  return Qnil;
+#else
+  check_android_display_info (terminal);
+  return list3i (android_get_current_api_level (), 0, 0);
+#endif
+}
+
+DEFUN ("x-display-screens", Fx_display_screens, Sx_display_screens,
+       0, 1, 0, doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  check_android_display_info (terminal);
+  return make_fixnum (1);
+}
+
+DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width,
+       0, 1, 0, doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+  error ("Android cross-compilation stub called!");
+  return Qnil;
+#else
+  return make_fixnum (android_get_mm_width ());
+#endif
+}
+
+DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height,
+       0, 1, 0, doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+  error ("Android cross-compilation stub called!");
+  return Qnil;
+#else
+  return make_fixnum (android_get_mm_height ());
+#endif
+}
+
+DEFUN ("x-display-backing-store", Fx_display_backing_store,
+       Sx_display_backing_store, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  check_android_display_info (terminal);
+
+  /* The Java part is implemented in a way that it always does the
+     equivalent of backing store.  */
+  return Qalways;
+}
+
+DEFUN ("x-display-visual-class", Fx_display_visual_class,
+       Sx_display_visual_class, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  check_android_display_info (terminal);
+
+  return Qtrue_color;
+}
+
+#ifndef ANDROID_STUBIFY
+
+static Lisp_Object
+android_make_monitor_attribute_list (struct MonitorInfo *monitors,
+                                    int n_monitors,
+                                    int primary_monitor)
+{
+  Lisp_Object monitor_frames;
+  Lisp_Object frame, rest;
+  struct frame *f;
+
+  monitor_frames = make_nil_vector (n_monitors);
+
+  FOR_EACH_FRAME (rest, frame)
+    {
+      f = XFRAME (frame);
+
+      /* Associate all frames with the primary monitor.  */
+
+      if (FRAME_WINDOW_P (f)
+         && !FRAME_TOOLTIP_P (f))
+       ASET (monitor_frames, primary_monitor,
+             Fcons (frame, AREF (monitor_frames,
+                                 primary_monitor)));
+    }
+
+  return make_monitor_attribute_list (monitors, n_monitors,
+                                     primary_monitor,
+                                      monitor_frames, NULL);
+}
+
+#endif
+
+DEFUN ("android-display-monitor-attributes-list",
+       Fandroid_display_monitor_attributes_list,
+       Sandroid_display_monitor_attributes_list,
+       0, 1, 0,
+       doc: /* Return a list of physical monitor attributes on the X display 
TERMINAL.
+
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.
+
+Internal use only, use `display-monitor-attributes-list' instead.  */)
+  (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+  error ("Android cross-compilation stub called!");
+  return Qnil;
+#else
+  struct MonitorInfo monitor;
+
+  memset (&monitor, 0, sizeof monitor);
+  monitor.geom.width = android_get_screen_width ();
+  monitor.geom.height = android_get_screen_height ();
+  monitor.mm_width = android_get_mm_width ();
+  monitor.mm_height = android_get_mm_height ();
+  monitor.work = monitor.geom;
+  monitor.name = (char *) "Android device monitor";
+
+  return android_make_monitor_attribute_list (&monitor, 1, 0);
+#endif
+}
+
+#ifndef ANDROID_STUBIFY
+
+static Lisp_Object
+frame_geometry (Lisp_Object frame, Lisp_Object attribute)
+{
+  struct frame *f = decode_live_frame (frame);
+  android_window rootw;
+  unsigned int native_width, native_height, x_border_width = 0;
+  int x_native = 0, y_native = 0, xptr = 0, yptr = 0;
+  int left_off = 0, right_off = 0, top_off = 0, bottom_off = 0;
+  int outer_left, outer_top, outer_right, outer_bottom;
+  int native_left, native_top, native_right, native_bottom;
+  int inner_left, inner_top, inner_right, inner_bottom;
+  int internal_border_width;
+  bool menu_bar_external = false, tool_bar_external = false;
+  int menu_bar_height = 0, menu_bar_width = 0;
+  int tab_bar_height = 0, tab_bar_width = 0;
+  int tool_bar_height = 0, tool_bar_width = 0;
+
+  if (FRAME_INITIAL_P (f) || !FRAME_ANDROID_P (f)
+      || !FRAME_ANDROID_WINDOW (f))
+    return Qnil;
+
+  block_input ();
+  android_get_geometry (FRAME_ANDROID_WINDOW (f),
+                       &rootw, &x_native, &y_native,
+                       &native_width, &native_height, &x_border_width);
+  unblock_input ();
+
+  if (FRAME_PARENT_FRAME (f))
+    {
+      Lisp_Object parent, edges;
+
+      XSETFRAME (parent, FRAME_PARENT_FRAME (f));
+      edges = Fandroid_frame_edges (parent, Qnative_edges);
+      if (!NILP (edges))
+       {
+         x_native += XFIXNUM (Fnth (make_fixnum (0), edges));
+         y_native += XFIXNUM (Fnth (make_fixnum (1), edges));
+       }
+
+      outer_left = x_native;
+      outer_top = y_native;
+      outer_right = outer_left + native_width + 2 * x_border_width;
+      outer_bottom = outer_top + native_height + 2 * x_border_width;
+
+      native_left = x_native + x_border_width;
+      native_top = y_native + x_border_width;
+      native_right = native_left + native_width;
+      native_bottom = native_top + native_height;
+    }
+  else
+    {
+      outer_left = xptr;
+      outer_top = yptr;
+      outer_right = outer_left + left_off + native_width + right_off;
+      outer_bottom = outer_top + top_off + native_height + bottom_off;
+
+      native_left = outer_left + left_off;
+      native_top = outer_top + top_off;
+      native_right = native_left + native_width;
+      native_bottom = native_top + native_height;
+    }
+
+  internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f);
+  inner_left = native_left + internal_border_width;
+  inner_top = native_top + internal_border_width;
+  inner_right = native_right - internal_border_width;
+  inner_bottom = native_bottom - internal_border_width;
+
+  menu_bar_height = FRAME_MENU_BAR_HEIGHT (f);
+  inner_top += menu_bar_height;
+  menu_bar_width = menu_bar_height ? native_width : 0;
+
+  tab_bar_height = FRAME_TAB_BAR_HEIGHT (f);
+  tab_bar_width = (tab_bar_height
+                  ? native_width - 2 * internal_border_width
+                   : 0);
+  inner_top += tab_bar_height;
+
+  tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f);
+  tool_bar_width = (tool_bar_height
+                   ? native_width - 2 * internal_border_width
+                   : 0);
+
+  /* Subtract or add to the inner dimensions based on the tool bar
+     position.  */
+
+  if (EQ (FRAME_TOOL_BAR_POSITION (f), Qtop))
+    inner_top += tool_bar_height;
+  else
+    inner_bottom -= tool_bar_height;
+
+  /* Construct list.  */
+  if (EQ (attribute, Qouter_edges))
+    return list4i (outer_left, outer_top, outer_right, outer_bottom);
+  else if (EQ (attribute, Qnative_edges))
+    return list4i (native_left, native_top, native_right, native_bottom);
+  else if (EQ (attribute, Qinner_edges))
+    return list4i (inner_left, inner_top, inner_right, inner_bottom);
+  else
+    return
+       list (Fcons (Qouter_position,
+                   Fcons (make_fixnum (outer_left),
+                          make_fixnum (outer_top))),
+            Fcons (Qouter_size,
+                   Fcons (make_fixnum (outer_right - outer_left),
+                          make_fixnum (outer_bottom - outer_top))),
+            /* Approximate.  */
+            Fcons (Qexternal_border_size,
+                   Fcons (make_fixnum (right_off),
+                          make_fixnum (bottom_off))),
+            Fcons (Qouter_border_width, make_fixnum (x_border_width)),
+            /* Approximate.  */
+            Fcons (Qtitle_bar_size,
+                   Fcons (make_fixnum (0),
+                          make_fixnum (top_off - bottom_off))),
+            Fcons (Qmenu_bar_external, menu_bar_external ? Qt : Qnil),
+            Fcons (Qmenu_bar_size,
+                   Fcons (make_fixnum (menu_bar_width),
+                          make_fixnum (menu_bar_height))),
+            Fcons (Qtab_bar_size,
+                   Fcons (make_fixnum (tab_bar_width),
+                          make_fixnum (tab_bar_height))),
+            Fcons (Qtool_bar_external, tool_bar_external ? Qt : Qnil),
+            Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)),
+            Fcons (Qtool_bar_size,
+                   Fcons (make_fixnum (tool_bar_width),
+                          make_fixnum (tool_bar_height))),
+            Fcons (Qinternal_border_width,
+                   make_fixnum (internal_border_width)));
+}
+
+#endif
+
+DEFUN ("android-frame-geometry", Fandroid_frame_geometry,
+       Sandroid_frame_geometry,
+       0, 1, 0,
+       doc: /* Return geometric attributes of FRAME.
+FRAME must be a live frame and defaults to the selected one.  The return
+value is an association list of the attributes listed below.  All height
+and width values are in pixels.
+
+`outer-position' is a cons of the outer left and top edges of FRAME
+  relative to the origin - the position (0, 0) - of FRAME's display.
+
+`outer-size' is a cons of the outer width and height of FRAME.  The
+  outer size includes the title bar and the external borders as well as
+  any menu and/or tool bar of frame.
+
+`external-border-size' is a cons of the horizontal and vertical width of
+  FRAME's external borders as supplied by the window manager.
+
+`title-bar-size' is a cons of the width and height of the title bar of
+  FRAME as supplied by the window manager.  If both of them are zero,
+  FRAME has no title bar.  If only the width is zero, Emacs was not
+  able to retrieve the width information.
+
+`menu-bar-external', if non-nil, means the menu bar is external (never
+  included in the inner edges of FRAME).
+
+`menu-bar-size' is a cons of the width and height of the menu bar of
+  FRAME.
+
+`tool-bar-external', if non-nil, means the tool bar is external (never
+  included in the inner edges of FRAME).
+
+`tool-bar-position' tells on which side the tool bar on FRAME is and can
+  be one of `left', `top', `right' or `bottom'.  If this is nil, FRAME
+  has no tool bar.
+
+`tool-bar-size' is a cons of the width and height of the tool bar of
+  FRAME.
+
+`internal-border-width' is the width of the internal border of
+  FRAME.  */)
+  (Lisp_Object frame)
+{
+#ifdef ANDROID_STUBIFY
+  error ("Android cross-compilation stub called!");
+  return Qnil;
+#else
+  return frame_geometry (frame, Qnil);
+#endif
+}
+
+DEFUN ("android-frame-edges", Fandroid_frame_edges, Sandroid_frame_edges, 0, 
2, 0,
+       doc: /* Return edge coordinates of FRAME.
+FRAME must be a live frame and defaults to the selected one.  The return
+value is a list of the form (LEFT, TOP, RIGHT, BOTTOM).  All values are
+in pixels relative to the origin - the position (0, 0) - of FRAME's
+display.
+
+If optional argument TYPE is the symbol `outer-edges', return the outer
+edges of FRAME.  The outer edges comprise the decorations of the window
+manager (like the title bar or external borders) as well as any external
+menu or tool bar of FRAME.  If optional argument TYPE is the symbol
+`native-edges' or nil, return the native edges of FRAME.  The native
+edges exclude the decorations of the window manager and any external
+menu or tool bar of FRAME.  If TYPE is the symbol `inner-edges', return
+the inner edges of FRAME.  These edges exclude title bar, any borders,
+menu bar or tool bar of FRAME.  */)
+  (Lisp_Object frame, Lisp_Object type)
+{
+#ifndef ANDROID_STUBIFY
+  return frame_geometry (frame, ((EQ (type, Qouter_edges)
+                                 || EQ (type, Qinner_edges))
+                                ? type
+                                : Qnative_edges));
+#else
+  return Qnil;
+#endif
+}
+
+#ifndef ANDROID_STUBIFY
+
+static Lisp_Object
+android_frame_list_z_order (struct android_display_info *dpyinfo,
+                           android_window window)
+{
+  android_window root, parent, *children;
+  unsigned int nchildren;
+  unsigned long i;
+  Lisp_Object frames;
+
+  frames = Qnil;
+
+  if (android_query_tree (window, &root, &parent,
+                         &children, &nchildren))
+    {
+      for (i = 0; i < nchildren; i++)
+       {
+         Lisp_Object frame, tail;
+
+         FOR_EACH_FRAME (tail, frame)
+            {
+              struct frame *cf = XFRAME (frame);
+
+              if (FRAME_ANDROID_P (cf)
+                  && (FRAME_ANDROID_WINDOW (cf) == children[i]))
+                frames = Fcons (frame, frames);
+            }
+       }
+
+      if (children)
+       xfree (children);
+    }
+
+  return frames;
+}
+
+#endif
+
+DEFUN ("android-frame-list-z-order", Fandroid_frame_list_z_order,
+       Sandroid_frame_list_z_order, 0, 1, 0,
+       doc: /* Return list of Emacs' frames, in Z (stacking) order.
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be either a frame or a display name (a string).  If
+omitted or nil, that stands for the selected frame's display.  Return
+nil if TERMINAL contains no Emacs frame.
+
+As a special case, if TERMINAL is non-nil and specifies a live frame,
+return the child frames of that frame in Z (stacking) order.
+
+Frames are listed from topmost (first) to bottommost (last).
+
+On Android, the order of the frames returned is undefined unless
+TERMINAL is a frame.  */)
+  (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+  error ("Android cross-compilation stub called!");
+  return Qnil;
+#else
+  struct android_display_info *dpyinfo;
+  android_window window;
+
+  dpyinfo = check_android_display_info (terminal);
+
+  if (FRAMEP (terminal) && FRAME_LIVE_P (XFRAME (terminal)))
+    window = FRAME_ANDROID_WINDOW (XFRAME (terminal));
+  else
+    window = dpyinfo->root_window;
+
+  return android_frame_list_z_order (dpyinfo, window);
+#endif
+}
+
+DEFUN ("android-frame-restack", Fandroid_frame_restack,
+       Sandroid_frame_restack, 2, 3, 0,
+       doc: /* Restack FRAME1 below FRAME2.
+This means that if both frames are visible and the display areas of
+these frames overlap, FRAME2 (partially) obscures FRAME1.  If optional
+third argument ABOVE is non-nil, restack FRAME1 above FRAME2.  This
+means that if both frames are visible and the display areas of these
+frames overlap, FRAME1 (partially) obscures FRAME2.
+
+This may be thought of as an atomic action performed in two steps: The
+first step removes FRAME1's window-step window from the display.  The
+second step reinserts FRAME1's window below (above if ABOVE is true)
+that of FRAME2.  Hence the position of FRAME2 in its display's Z
+\(stacking) order relative to all other frames excluding FRAME1 remains
+unaltered.
+
+The Android system refuses to restack windows, so this does not
+work.  */)
+  (Lisp_Object frame1, Lisp_Object frame2, Lisp_Object frame3)
+{
+#ifdef ANDROID_STUBIFY
+  error ("Android cross-compilation stub called!");
+  return Qnil;
+#else
+  /* This is not supported on Android because of limitations in the
+     platform that prevent ViewGroups from restacking
+     SurfaceViews.  */
+  return Qnil;
+#endif
+}
+
+DEFUN ("android-mouse-absolute-pixel-position",
+       Fandroid_mouse_absolute_pixel_position,
+       Sandroid_mouse_absolute_pixel_position, 0, 0, 0,
+       doc: /* Return absolute position of mouse cursor in pixels.
+The position is returned as a cons cell (X . Y) of the coordinates of
+the mouse cursor position in pixels relative to a position (0, 0) of the
+selected frame's display.  This does not work on Android.  */)
+  (void)
+{
+  /* This cannot be implemented on Android.  */
+  return Qnil;
+}
+
+DEFUN ("android-set-mouse-absolute-pixel-position",
+       Fandroid_set_mouse_absolute_pixel_position,
+       Sandroid_set_mouse_absolute_pixel_position, 2, 2, 0,
+       doc: /* Move mouse pointer to a pixel position at (X, Y).  The
+coordinates X and Y are interpreted to start from the top-left corner
+of the screen.  This does not work on Android.  */)
+  (Lisp_Object x, Lisp_Object y)
+{
+  /* This cannot be implemented on Android.  */
+  return Qnil;
+}
+
+DEFUN ("android-get-connection", Fandroid_get_connection,
+       Sandroid_get_connection, 0, 0, 0,
+       doc: /* Get the connection to the display server.
+Return the terminal if it exists, else nil.
+
+Emacs cannot open a connection to the display server itself under
+Android, so there is no equivalent of `x-open-connection'.  */)
+  (void)
+{
+#ifdef ANDROID_STUBIFY
+  error ("Android cross-compilation stub called!");
+  return Qnil;
+#else
+  Lisp_Object terminal;
+
+  terminal = Qnil;
+
+  if (x_display_list)
+    XSETTERMINAL (terminal, x_display_list->terminal);
+
+  return terminal;
+#endif
+}
+
+DEFUN ("x-display-list", Fx_display_list, Sx_display_list, 0, 0, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (void)
+{
+  Lisp_Object result;
+
+  result = Qnil;
+
+  if (x_display_list)
+    result = Fcons (XCAR (x_display_list->name_list_element),
+                   result);
+
+  return result;
+}
+
+#ifndef ANDROID_STUBIFY
+
+static void
+unwind_create_tip_frame (Lisp_Object frame)
+{
+  Lisp_Object deleted;
+
+  deleted = unwind_create_frame (frame);
+  if (EQ (deleted, Qt))
+    {
+      tip_window = ANDROID_NONE;
+      tip_frame = Qnil;
+    }
+}
+
+static Lisp_Object
+android_create_tip_frame (struct android_display_info *dpyinfo,
+                         Lisp_Object parms)
+{
+  struct frame *f;
+  Lisp_Object frame;
+  Lisp_Object name;
+  specpdl_ref count = SPECPDL_INDEX ();
+  bool face_change_before = face_change;
+
+  if (!dpyinfo->terminal->name)
+    error ("Terminal is not live, can't create new frames on it");
+
+  parms = Fcopy_alist (parms);
+
+  /* Get the name of the frame to use for resource lookup.  */
+  name = gui_display_get_arg (dpyinfo, parms, Qname, "name", "Name",
+                              RES_TYPE_STRING);
+  if (!STRINGP (name)
+      && !BASE_EQ (name, Qunbound)
+      && !NILP (name))
+    error ("Invalid frame name--not a string or nil");
+
+  frame = Qnil;
+  f = make_frame (false);
+  f->wants_modeline = false;
+  XSETFRAME (frame, f);
+  record_unwind_protect (unwind_create_tip_frame, frame);
+
+  f->terminal = dpyinfo->terminal;
+
+  /* By setting the output method, we're essentially saying that
+     the frame is live, as per FRAME_LIVE_P.  If we get a signal
+     from this point on, x_destroy_window might screw up reference
+     counts etc.  */
+  f->output_method = output_android;
+  f->output_data.android = xzalloc (sizeof *f->output_data.android);
+  FRAME_FONTSET (f) = -1;
+  f->output_data.android->white_relief.pixel = -1;
+  f->output_data.android->black_relief.pixel = -1;
+
+  f->tooltip = true;
+  fset_icon_name (f, Qnil);
+  FRAME_DISPLAY_INFO (f) = dpyinfo;
+  f->output_data.android->parent_desc = FRAME_DISPLAY_INFO (f)->root_window;
+
+  /* These colors will be set anyway later, but it's important
+     to get the color reference counts right, so initialize them!  */
+  {
+    Lisp_Object black;
+
+    /* Function android_decode_color can signal an error.  Make sure
+       to initialize color slots so that we won't try to free colors
+       we haven't allocated.  */
+    FRAME_FOREGROUND_PIXEL (f) = -1;
+    FRAME_BACKGROUND_PIXEL (f) = -1;
+    f->output_data.android->cursor_pixel = -1;
+    f->output_data.android->cursor_foreground_pixel = -1;
+    f->output_data.android->mouse_pixel = -1;
+
+    black = build_string ("black");
+    FRAME_FOREGROUND_PIXEL (f)
+      = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+    FRAME_BACKGROUND_PIXEL (f)
+      = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+    f->output_data.android->cursor_pixel
+      = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+    f->output_data.android->cursor_foreground_pixel
+      = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+    f->output_data.android->mouse_pixel
+      = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+  }
+
+  /* Set the name; the functions to which we pass f expect the name to
+     be set.  */
+  if (BASE_EQ (name, Qunbound) || NILP (name))
+    f->explicit_name = false;
+  else
+    {
+      fset_name (f, name);
+      f->explicit_name = true;
+      /* use the frame's title when getting resources for this frame.  */
+      specbind (Qx_resource_name, name);
+    }
+
+  register_font_driver (&androidfont_driver, f);
+  register_font_driver (&android_sfntfont_driver, f);
+
+  image_cache_refcount
+    = FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount : 0;
+#ifdef GLYPH_DEBUG
+  dpyinfo_refcount = dpyinfo->reference_count;
+#endif /* GLYPH_DEBUG */
+
+  gui_default_parameter (f, parms, Qfont_backend, Qnil,
+                         "fontBackend", "FontBackend", RES_TYPE_STRING);
+
+  /* Extract the window parameters from the supplied values that are
+     needed to determine window geometry.  */
+  android_default_font_parameter (f, parms);
+
+  gui_default_parameter (f, parms, Qborder_width, make_fixnum (0),
+                         "borderWidth", "BorderWidth", RES_TYPE_NUMBER);
+
+  /* This defaults to 1 in order to match xterm.  We recognize either
+     internalBorderWidth or internalBorder (which is what xterm calls
+     it).  */
+  if (NILP (Fassq (Qinternal_border_width, parms)))
+    {
+      Lisp_Object value;
+
+      value = gui_display_get_arg (dpyinfo, parms, Qinternal_border_width,
+                                   "internalBorder", "internalBorder",
+                                   RES_TYPE_NUMBER);
+      if (! BASE_EQ (value, Qunbound))
+       parms = Fcons (Fcons (Qinternal_border_width, value),
+                      parms);
+    }
+
+  gui_default_parameter (f, parms, Qinternal_border_width, make_fixnum (1),
+                         "internalBorderWidth", "internalBorderWidth",
+                         RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qright_divider_width, make_fixnum (0),
+                         NULL, NULL, RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0),
+                         NULL, NULL, RES_TYPE_NUMBER);
+
+  /* Also do the stuff which must be set before the window exists.  */
+  gui_default_parameter (f, parms, Qforeground_color, build_string ("black"),
+                         "foreground", "Foreground", RES_TYPE_STRING);
+  gui_default_parameter (f, parms, Qbackground_color, build_string ("white"),
+                         "background", "Background", RES_TYPE_STRING);
+  gui_default_parameter (f, parms, Qmouse_color, build_string ("black"),
+                         "pointerColor", "Foreground", RES_TYPE_STRING);
+  gui_default_parameter (f, parms, Qcursor_color, build_string ("black"),
+                         "cursorColor", "Foreground", RES_TYPE_STRING);
+  gui_default_parameter (f, parms, Qborder_color, build_string ("black"),
+                         "borderColor", "BorderColor", RES_TYPE_STRING);
+  gui_default_parameter (f, parms, Qno_special_glyphs, Qnil,
+                         NULL, NULL, RES_TYPE_BOOLEAN);
+
+  {
+    struct android_set_window_attributes attrs;
+    unsigned long mask;
+
+    block_input ();
+    mask = ANDROID_CW_OVERRIDE_REDIRECT | ANDROID_CW_BACK_PIXEL;
+
+    attrs.override_redirect = true;
+    attrs.background_pixel = FRAME_BACKGROUND_PIXEL (f);
+    tip_window
+      = FRAME_ANDROID_WINDOW (f)
+      = android_create_window (FRAME_DISPLAY_INFO (f)->root_window,
+                              /* x, y, width, height, value-mask,
+                                 attrs.  */
+                              0, 0, 1, 1, mask, &attrs);
+    unblock_input ();
+  }
+
+  /* Init faces before gui_default_parameter is called for the
+     scroll-bar-width parameter because otherwise we end up in
+     init_iterator with a null face cache, which should not happen.  */
+  init_frame_faces (f);
+
+  gui_default_parameter (f, parms, Qinhibit_double_buffering, Qnil,
+                         "inhibitDoubleBuffering", "InhibitDoubleBuffering",
+                         RES_TYPE_BOOLEAN);
+
+  gui_figure_window_size (f, parms, false, false);
+
+  f->output_data.android->parent_desc = FRAME_DISPLAY_INFO (f)->root_window;
+
+  android_make_gc (f);
+
+  gui_default_parameter (f, parms, Qauto_raise, Qnil,
+                         "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+  gui_default_parameter (f, parms, Qauto_lower, Qnil,
+                         "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+  gui_default_parameter (f, parms, Qcursor_type, Qbox,
+                         "cursorType", "CursorType", RES_TYPE_SYMBOL);
+  gui_default_parameter (f, parms, Qalpha, Qnil,
+                         "alpha", "Alpha", RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qalpha_background, Qnil,
+                         "alphaBackground", "AlphaBackground", 
RES_TYPE_NUMBER);
+
+  /* Add `tooltip' frame parameter's default value. */
+  if (NILP (Fframe_parameter (frame, Qtooltip)))
+    {
+      AUTO_FRAME_ARG (arg, Qtooltip, Qt);
+      Fmodify_frame_parameters (frame, arg);
+    }
+
+  /* FIXME - can this be done in a similar way to normal frames?
+     https://lists.gnu.org/r/emacs-devel/2007-10/msg00641.html */
+
+  /* Set the `display-type' frame parameter before setting up faces. */
+  {
+    Lisp_Object disptype;
+
+    disptype = Qcolor;
+
+    if (NILP (Fframe_parameter (frame, Qdisplay_type)))
+      {
+       AUTO_FRAME_ARG (arg, Qdisplay_type, disptype);
+       Fmodify_frame_parameters (frame, arg);
+      }
+  }
+
+  /* Set up faces after all frame parameters are known.  This call
+     also merges in face attributes specified for new frames.  */
+  {
+    Lisp_Object bg = Fframe_parameter (frame, Qbackground_color);
+
+    call2 (Qface_set_after_frame_default, frame, Qnil);
+
+    if (!EQ (bg, Fframe_parameter (frame, Qbackground_color)))
+      {
+       AUTO_FRAME_ARG (arg, Qbackground_color, bg);
+       Fmodify_frame_parameters (frame, arg);
+      }
+  }
+
+  f->no_split = true;
+
+  /* Now that the frame will be official, it counts as a reference to
+     its display and terminal.  */
+  f->terminal->reference_count++;
+
+  /* It is now ok to make the frame official even if we get an error
+     below.  And the frame needs to be on Vframe_list or making it
+     visible won't work.  */
+  Vframe_list = Fcons (frame, Vframe_list);
+  f->can_set_window_size = true;
+  adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+                    0, true, Qtip_frame);
+
+  /* Setting attributes of faces of the tooltip frame from resources
+     and similar will set face_change, which leads to the clearing of
+     all current matrices.  Since this isn't necessary here, avoid it
+     by resetting face_change to the value it had before we created
+     the tip frame.  */
+  face_change = face_change_before;
+
+  /* Discard the unwind_protect.  */
+  return unbind_to (count, frame);
+}
+
+static Lisp_Object
+android_hide_tip (bool delete)
+{
+  if (!NILP (tip_timer))
+    {
+      call1 (Qcancel_timer, tip_timer);
+      tip_timer = Qnil;
+    }
+
+  if (NILP (tip_frame)
+      || (!delete
+         && !NILP (tip_frame)
+         && FRAME_LIVE_P (XFRAME (tip_frame))
+         && !FRAME_VISIBLE_P (XFRAME (tip_frame))))
+    return Qnil;
+  else
+    {
+      Lisp_Object was_open = Qnil;
+
+      specpdl_ref count = SPECPDL_INDEX ();
+      specbind (Qinhibit_redisplay, Qt);
+      specbind (Qinhibit_quit, Qt);
+
+      if (!NILP (tip_frame))
+       {
+         struct frame *f = XFRAME (tip_frame);
+
+         if (FRAME_LIVE_P (f))
+           {
+             if (delete)
+               {
+                 delete_frame (tip_frame, Qnil);
+                 tip_frame = Qnil;
+               }
+             else
+               android_make_frame_invisible (XFRAME (tip_frame));
+
+             was_open = Qt;
+           }
+         else
+           tip_frame = Qnil;
+       }
+      else
+       tip_frame = Qnil;
+
+      return unbind_to (count, was_open);
+    }
+}
+
+static void
+compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx,
+               Lisp_Object dy, int width, int height, int *root_x,
+               int *root_y)
+{
+  Lisp_Object left, top, right, bottom;
+  int min_x, min_y, max_x, max_y = -1;
+  android_window window;
+  struct frame *mouse_frame;
+
+  /* Initialize these values in case there is no mouse frame.  */
+  *root_x = 0;
+  *root_y = 0;
+
+  /* User-specified position?  */
+  left = CDR (Fassq (Qleft, parms));
+  top  = CDR (Fassq (Qtop, parms));
+  right = CDR (Fassq (Qright, parms));
+  bottom = CDR (Fassq (Qbottom, parms));
+
+  /* Move the tooltip window where the mouse pointer was last seen.
+     Resize and show it.  */
+  if ((!FIXNUMP (left) && !FIXNUMP (right))
+      || (!FIXNUMP (top) && !FIXNUMP (bottom)))
+    {
+      if (x_display_list->last_mouse_motion_frame)
+       {
+         *root_x = x_display_list->last_mouse_motion_x;
+         *root_y = x_display_list->last_mouse_motion_y;
+         mouse_frame = x_display_list->last_mouse_motion_frame;
+         window = FRAME_ANDROID_WINDOW (mouse_frame);
+
+         /* Translate the coordinates to the screen.  */
+         android_translate_coordinates (window, *root_x, *root_y,
+                                        root_x, root_y);
+       }
+    }
+
+  min_x = 0;
+  min_y = 0;
+  max_x = android_get_screen_width ();
+  max_y = android_get_screen_height ();
+
+  if (FIXNUMP (top))
+    *root_y = XFIXNUM (top);
+  else if (FIXNUMP (bottom))
+    *root_y = XFIXNUM (bottom) - height;
+  else if (*root_y + XFIXNUM (dy) <= min_y)
+    *root_y = min_y; /* Can happen for negative dy */
+  else if (*root_y + XFIXNUM (dy) + height <= max_y)
+    /* It fits below the pointer */
+    *root_y += XFIXNUM (dy);
+  else if (height + XFIXNUM (dy) + min_y <= *root_y)
+    /* It fits above the pointer.  */
+    *root_y -= height + XFIXNUM (dy);
+  else
+    /* Put it on the top.  */
+    *root_y = min_y;
+
+  if (FIXNUMP (left))
+    *root_x = XFIXNUM (left);
+  else if (FIXNUMP (right))
+    *root_x = XFIXNUM (right) - width;
+  else if (*root_x + XFIXNUM (dx) <= min_x)
+    *root_x = 0; /* Can happen for negative dx */
+  else if (*root_x + XFIXNUM (dx) + width <= max_x)
+    /* It fits to the right of the pointer.  */
+    *root_x += XFIXNUM (dx);
+  else if (width + XFIXNUM (dx) + min_x <= *root_x)
+    /* It fits to the left of the pointer.  */
+    *root_x -= width + XFIXNUM (dx);
+  else
+    /* Put it left justified on the screen -- it ought to fit that way.  */
+    *root_x = min_x;
+}
+
+#endif
+
+DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object string, Lisp_Object frame, Lisp_Object parms,
+   Lisp_Object timeout, Lisp_Object dx, Lisp_Object dy)
+{
+#ifdef ANDROID_STUBIFY
+  error ("Android cross-compilation stub called!");
+  return Qnil;
+#else
+  struct frame *f, *tip_f;
+  struct window *w;
+  int root_x, root_y;
+  struct buffer *old_buffer;
+  struct text_pos pos;
+  int width, height;
+  int old_windows_or_buffers_changed = windows_or_buffers_changed;
+  specpdl_ref count = SPECPDL_INDEX ();
+  Lisp_Object window, size, tip_buf;
+  bool displayed;
+#ifdef ENABLE_CHECKING
+  struct glyph_row *row, *end;
+#endif
+  AUTO_STRING (tip, " *tip*");
+
+  specbind (Qinhibit_redisplay, Qt);
+
+  CHECK_STRING (string);
+  if (SCHARS (string) == 0)
+    string = make_unibyte_string (" ", 1);
+
+  if (NILP (frame))
+    frame = selected_frame;
+  f = decode_window_system_frame (frame);
+
+  if (NILP (timeout))
+    timeout = Vx_show_tooltip_timeout;
+  CHECK_FIXNAT (timeout);
+
+  if (NILP (dx))
+    dx = make_fixnum (5);
+  else
+    CHECK_FIXNUM (dx);
+
+  if (NILP (dy))
+    dy = make_fixnum (-10);
+  else
+    CHECK_FIXNUM (dy);
+
+  tip_dx = dx;
+  tip_dy = dy;
+
+  if (!NILP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame)))
+    {
+      if (FRAME_VISIBLE_P (XFRAME (tip_frame))
+         && !NILP (Fequal_including_properties (tip_last_string,
+                                                string))
+         && !NILP (Fequal (tip_last_parms, parms)))
+       {
+         /* Only DX and DY have changed.  */
+         tip_f = XFRAME (tip_frame);
+         if (!NILP (tip_timer))
+           {
+             call1 (Qcancel_timer, tip_timer);
+             tip_timer = Qnil;
+           }
+
+         block_input ();
+         compute_tip_xy (tip_f, parms, dx, dy, FRAME_PIXEL_WIDTH (tip_f),
+                         FRAME_PIXEL_HEIGHT (tip_f), &root_x, &root_y);
+         android_move_window (FRAME_ANDROID_WINDOW (tip_f),
+                              root_x, root_y);
+         unblock_input ();
+
+         goto start_timer;
+       }
+      else
+       android_hide_tip (true);
+    }
+  else
+    android_hide_tip (true);
+
+  tip_last_frame = frame;
+  tip_last_string = string;
+  tip_last_parms = parms;
+
+  if (NILP (tip_frame) || !FRAME_LIVE_P (XFRAME (tip_frame)))
+    {
+      /* Add default values to frame parameters.  */
+      if (NILP (Fassq (Qname, parms)))
+       parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms);
+      if (NILP (Fassq (Qinternal_border_width, parms)))
+       parms = Fcons (Fcons (Qinternal_border_width, make_fixnum (3)),
+                      parms);
+      if (NILP (Fassq (Qborder_width, parms)))
+       parms = Fcons (Fcons (Qborder_width, make_fixnum (1)), parms);
+      if (NILP (Fassq (Qborder_color, parms)))
+       parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")),
+                      parms);
+      if (NILP (Fassq (Qbackground_color, parms)))
+       parms = Fcons (Fcons (Qbackground_color,
+                             build_string ("lightyellow")),
+                      parms);
+
+      /* Create a frame for the tooltip, and record it in the global
+        variable tip_frame.  */
+      if (NILP (tip_frame = android_create_tip_frame (FRAME_DISPLAY_INFO (f),
+                                                     parms)))
+       /* Creating the tip frame failed.  */
+       return unbind_to (count, Qnil);
+    }
+
+  tip_f = XFRAME (tip_frame);
+  window = FRAME_ROOT_WINDOW (tip_f);
+  tip_buf = Fget_buffer_create (tip, Qnil);
+  /* We will mark the tip window a "pseudo-window" below, and such
+     windows cannot have display margins.  */
+  bset_left_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
+  bset_right_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
+  set_window_buffer (window, tip_buf, false, false);
+  w = XWINDOW (window);
+  w->pseudo_window_p = true;
+  /* Try to avoid that `other-window' select us (Bug#47207).  */
+  Fset_window_parameter (window, Qno_other_window, Qt);
+
+  /* Set up the frame's root window.  Note: The following code does not
+     try to size the window or its frame correctly.  Its only purpose is
+     to make the subsequent text size calculations work.  The right
+     sizes should get installed when the toolkit gets back to us.  */
+  w->left_col = 0;
+  w->top_line = 0;
+  w->pixel_left = 0;
+  w->pixel_top = 0;
+
+  if (CONSP (Vx_max_tooltip_size)
+      && RANGED_FIXNUMP (1, XCAR (Vx_max_tooltip_size), INT_MAX)
+      && RANGED_FIXNUMP (1, XCDR (Vx_max_tooltip_size), INT_MAX))
+    {
+      w->total_cols = XFIXNAT (XCAR (Vx_max_tooltip_size));
+      w->total_lines = XFIXNAT (XCDR (Vx_max_tooltip_size));
+    }
+  else
+    {
+      w->total_cols = 80;
+      w->total_lines = 40;
+    }
+
+  w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (tip_f);
+  w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (tip_f);
+  FRAME_TOTAL_COLS (tip_f) = w->total_cols;
+  adjust_frame_glyphs (tip_f);
+
+  /* Insert STRING into root window's buffer and fit the frame to the
+     buffer.  */
+  specpdl_ref count_1 = SPECPDL_INDEX ();
+  old_buffer = current_buffer;
+  set_buffer_internal_1 (XBUFFER (w->contents));
+  bset_truncate_lines (current_buffer, Qnil);
+  specbind (Qinhibit_read_only, Qt);
+  specbind (Qinhibit_modification_hooks, Qt);
+  specbind (Qinhibit_point_motion_hooks, Qt);
+  Ferase_buffer ();
+  Finsert (1, &string);
+  clear_glyph_matrix (w->desired_matrix);
+  clear_glyph_matrix (w->current_matrix);
+  SET_TEXT_POS (pos, BEGV, BEGV_BYTE);
+  displayed = try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE);
+
+  if (!displayed && NILP (Vx_max_tooltip_size))
+    {
+#ifdef ENABLE_CHECKING
+      row = w->desired_matrix->rows;
+      end = w->desired_matrix->rows + w->desired_matrix->nrows;
+
+      while (row < end)
+       {
+         if (!row->displays_text_p
+             || row->ends_at_zv_p)
+           break;
+         ++row;
+       }
+
+      eassert (row < end && row->ends_at_zv_p);
+#endif
+    }
+
+  /* Calculate size of tooltip window.  */
+  size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil,
+                                 make_fixnum (w->pixel_height), Qnil,
+                                 Qnil);
+  /* Add the frame's internal border to calculated size.  */
+  width = XFIXNUM (CAR (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
+  height = XFIXNUM (CDR (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
+
+  /* Calculate position of tooltip frame.  */
+  compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y);
+
+  /* Show tooltip frame.  */
+  block_input ();
+  android_move_resize_window (FRAME_ANDROID_WINDOW (tip_f),
+                             root_x, root_y, width,
+                             height);
+  android_map_raised (FRAME_ANDROID_WINDOW (tip_f));
+  unblock_input ();
+
+  /* Garbage the tip frame too.  */
+  SET_FRAME_GARBAGED (tip_f);
+
+  w->must_be_updated_p = true;
+  update_single_window (w);
+  flush_frame (tip_f);
+  set_buffer_internal_1 (old_buffer);
+  unbind_to (count_1, Qnil);
+  windows_or_buffers_changed = old_windows_or_buffers_changed;
+
+  /* MapNotify events are not sent on Android, so make the frame
+     visible.  */
+
+  SET_FRAME_VISIBLE (tip_f, true);
+
+ start_timer:
+  /* Let the tip disappear after timeout seconds.  */
+  tip_timer = call3 (Qrun_at_time, timeout, Qnil,
+                    Qx_hide_tip);
+
+  return unbind_to (count, Qnil);
+#endif
+}
+
+DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (void)
+{
+#ifdef ANDROID_STUBIFY
+  error ("Android cross-compilation stub called!");
+  return Qnil;
+#else
+  return android_hide_tip (true);
+#endif
+}
+
+DEFUN ("android-detect-mouse", Fandroid_detect_mouse,
+       Sandroid_detect_mouse, 0, 0, 0,
+       doc: /* Figure out whether or not there is a mouse.
+Return non-nil if a mouse is connected to this computer, and nil if
+there is no mouse.  */)
+  (void)
+{
+#ifndef ANDROID_STUBIFY
+  /* If no display connection is present, just return nil.  */
+
+  if (!android_init_gui)
+    return Qnil;
+
+  return android_detect_mouse () ? Qt : Qnil;
+#else
+  return Qnil;
+#endif
+}
+
+DEFUN ("android-toggle-on-screen-keyboard",
+       Fandroid_toggle_on_screen_keyboard,
+       Sandroid_toggle_on_screen_keyboard, 2, 2, 0,
+       doc: /* Display or hide the on-screen keyboard.
+If HIDE is non-nil, hide the on screen keyboard if it is currently
+being displayed.  Else, request that the system display it on behalf
+of FRAME.  This request may be rejected if FRAME does not have the
+input focus.  */)
+  (Lisp_Object frame, Lisp_Object hide)
+{
+#ifndef ANDROID_STUBIFY
+  struct frame *f;
+
+  f = decode_window_system_frame (frame);
+
+  block_input ();
+  android_toggle_on_screen_keyboard (FRAME_ANDROID_WINDOW (f),
+                                    NILP (hide));
+  unblock_input ();
+#endif
+
+  return Qnil;
+}
+
+
+
+#ifndef ANDROID_STUBIFY
+
+static void
+android_set_background_color (struct frame *f, Lisp_Object arg,
+                             Lisp_Object oldval)
+{
+  struct android_output *x;
+  unsigned long bg;
+
+  x = f->output_data.android;
+  bg = android_decode_color (f, arg, WHITE_PIX_DEFAULT (f));
+  FRAME_BACKGROUND_PIXEL (f) = bg;
+
+  if (FRAME_ANDROID_WINDOW (f) != 0)
+    {
+      block_input ();
+      android_set_background (x->normal_gc, bg);
+      android_set_foreground (x->reverse_gc, bg);
+      android_set_window_background (FRAME_ANDROID_WINDOW (f), bg);
+      android_set_foreground (x->cursor_gc, bg);
+      unblock_input ();
+
+      update_face_from_frame_parameter (f, Qbackground_color, arg);
+
+      if (FRAME_VISIBLE_P (f))
+        redraw_frame (f);
+    }
+}
+
+static void
+android_set_border_color (struct frame *f, Lisp_Object arg,
+                         Lisp_Object oldval)
+{
+  /* Left unimplemented because Android has no window borders.  */
+  CHECK_STRING (arg);
+  android_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
+  update_face_from_frame_parameter (f, Qborder_color, arg);
+}
+
+static void
+android_set_cursor_color (struct frame *f, Lisp_Object arg,
+                         Lisp_Object oldval)
+{
+  unsigned long fore_pixel, pixel;
+  struct android_output *x;
+
+  x = f->output_data.android;
+
+  if (!NILP (Vx_cursor_fore_pixel))
+    fore_pixel = android_decode_color (f, Vx_cursor_fore_pixel,
+                                      WHITE_PIX_DEFAULT (f));
+  else
+    fore_pixel = FRAME_BACKGROUND_PIXEL (f);
+
+  pixel = android_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
+
+  /* Make sure that the cursor color differs from the background color.  */
+  if (pixel == FRAME_BACKGROUND_PIXEL (f))
+    {
+      pixel = FRAME_FOREGROUND_PIXEL (f);
+      if (pixel == fore_pixel)
+       fore_pixel = FRAME_BACKGROUND_PIXEL (f);
+    }
+
+  x->cursor_foreground_pixel = fore_pixel;
+  x->cursor_pixel = pixel;
+
+  if (FRAME_ANDROID_WINDOW (f) != 0)
+    {
+      block_input ();
+      android_set_background (x->cursor_gc, x->cursor_pixel);
+      android_set_foreground (x->cursor_gc, fore_pixel);
+      unblock_input ();
+
+      if (FRAME_VISIBLE_P (f))
+       {
+         gui_update_cursor (f, false);
+         gui_update_cursor (f, true);
+       }
+    }
+
+  update_face_from_frame_parameter (f, Qcursor_color, arg);
+}
+
+static void
+android_set_cursor_type (struct frame *f, Lisp_Object arg,
+                        Lisp_Object oldval)
+{
+  set_frame_cursor_types (f, arg);
+}
+
+static void
+android_set_foreground_color (struct frame *f, Lisp_Object arg,
+                             Lisp_Object oldval)
+{
+  struct android_output *x;
+  unsigned long fg, old_fg;
+
+  x = f->output_data.android;
+
+  fg = android_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
+  old_fg = FRAME_FOREGROUND_PIXEL (f);
+  FRAME_FOREGROUND_PIXEL (f) = fg;
+
+  if (FRAME_ANDROID_WINDOW (f) != 0)
+    {
+      block_input ();
+      android_set_foreground (x->normal_gc, fg);
+      android_set_background (x->reverse_gc, fg);
+
+      if (x->cursor_pixel == old_fg)
+       {
+         x->cursor_pixel = fg;
+         android_set_background (x->cursor_gc, x->cursor_pixel);
+       }
+
+      unblock_input ();
+
+      update_face_from_frame_parameter (f, Qforeground_color, arg);
+
+      if (FRAME_VISIBLE_P (f))
+        redraw_frame (f);
+    }
+}
+
+static void
+android_set_child_frame_border_width (struct frame *f, Lisp_Object arg,
+                                     Lisp_Object oldval)
+{
+  int border;
+
+  if (NILP (arg))
+    border = -1;
+  else if (RANGED_FIXNUMP (0, arg, INT_MAX))
+    border = XFIXNAT (arg);
+  else
+    signal_error ("Invalid child frame border width", arg);
+
+  if (border != FRAME_CHILD_FRAME_BORDER_WIDTH (f))
+    {
+      f->child_frame_border_width = border;
+
+      if (FRAME_ANDROID_WINDOW (f))
+       {
+         adjust_frame_size (f, -1, -1, 3, false, Qchild_frame_border_width);
+         android_clear_under_internal_border (f);
+       }
+    }
+}
+
+static void
+android_set_internal_border_width (struct frame *f, Lisp_Object arg,
+                                  Lisp_Object oldval)
+{
+  int border = check_int_nonnegative (arg);
+
+  if (border != FRAME_INTERNAL_BORDER_WIDTH (f))
+    {
+      f->internal_border_width = border;
+
+      if (FRAME_ANDROID_WINDOW (f))
+       {
+         adjust_frame_size (f, -1, -1, 3, false, Qinternal_border_width);
+         android_clear_under_internal_border (f);
+       }
+    }
+}
+
+static void
+android_set_menu_bar_lines (struct frame *f, Lisp_Object value,
+                           Lisp_Object oldval)
+{
+  int nlines;
+  int olines = FRAME_MENU_BAR_LINES (f);
+
+  /* Right now, menu bars don't work properly in minibuf-only frames;
+     most of the commands try to apply themselves to the minibuffer
+     frame itself, and get an error because you can't switch buffers
+     in or split the minibuffer window.  */
+  if (FRAME_MINIBUF_ONLY_P (f) || FRAME_PARENT_FRAME (f))
+    return;
+
+  if (TYPE_RANGED_FIXNUMP (int, value))
+    nlines = XFIXNUM (value);
+  else
+    nlines = 0;
+
+  /* Make sure we redisplay all windows in this frame.  */
+  fset_redisplay (f);
+
+  FRAME_MENU_BAR_LINES (f) = nlines;
+  FRAME_MENU_BAR_HEIGHT (f) = nlines * FRAME_LINE_HEIGHT (f);
+  if (FRAME_ANDROID_WINDOW (f))
+    android_clear_under_internal_border (f);
+
+  /* If the menu bar height gets changed, the internal border below
+     the top margin has to be cleared.  Also, if the menu bar gets
+     larger, the area for the added lines has to be cleared except for
+     the first menu bar line that is to be drawn later.  */
+  if (nlines != olines)
+    {
+      int height = FRAME_INTERNAL_BORDER_WIDTH (f);
+      int width = FRAME_PIXEL_WIDTH (f);
+      int y;
+
+      adjust_frame_size (f, -1, -1, 3, true, Qmenu_bar_lines);
+
+      /* height can be zero here. */
+      if (FRAME_ANDROID_WINDOW (f) && height > 0 && width > 0)
+       {
+         y = FRAME_TOP_MARGIN_HEIGHT (f);
+
+         block_input ();
+         android_clear_area (FRAME_ANDROID_DRAWABLE (f),
+                             0, y, width, height);
+         unblock_input ();
+       }
+
+      if (nlines > 1 && nlines > olines)
+       {
+         y = (olines == 0 ? 1 : olines) * FRAME_LINE_HEIGHT (f);
+         height = nlines * FRAME_LINE_HEIGHT (f) - y;
+
+         block_input ();
+         android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0, y,
+                             width, height);
+         unblock_input ();
+       }
+
+      if (nlines == 0 && WINDOWP (f->menu_bar_window))
+       clear_glyph_matrix (XWINDOW (f->menu_bar_window)->current_matrix);
+    }
+
+  adjust_frame_glyphs (f);
+}
+
+
+
+/* These enums must stay in sync with the mouse_cursor_types array
+   below!  */
+
+enum mouse_cursor
+  {
+    mouse_cursor_text,
+    mouse_cursor_nontext,
+    mouse_cursor_hourglass,
+    mouse_cursor_mode,
+    mouse_cursor_hand,
+    mouse_cursor_horizontal_drag,
+    mouse_cursor_vertical_drag,
+    mouse_cursor_left_edge,
+    mouse_cursor_top_left_corner,
+    mouse_cursor_top_edge,
+    mouse_cursor_top_right_corner,
+    mouse_cursor_right_edge,
+    mouse_cursor_bottom_right_corner,
+    mouse_cursor_bottom_edge,
+    mouse_cursor_bottom_left_corner,
+    mouse_cursor_max
+  };
+
+struct mouse_cursor_types
+{
+  /* Printable name for error messages (optional).  */
+  const char *name;
+
+  /* Lisp variable controlling the cursor shape.  */
+  /* FIXME: A couple of these variables are defined in the C code but
+     are not actually accessible from Lisp.  They should probably be
+     made accessible or removed.  */
+  Lisp_Object *shape_var_ptr;
+
+  /* The default shape.  */
+  int default_shape;
+};
+
+/* This array must stay in sync with enum mouse_cursor above!  */
+
+static const struct mouse_cursor_types mouse_cursor_types[] =
+  {
+    {"text", &Vx_pointer_shape, ANDROID_XC_XTERM, },
+    {"nontext", &Vx_nontext_pointer_shape, ANDROID_XC_LEFT_PTR, },
+    {"hourglass", &Vx_hourglass_pointer_shape, ANDROID_XC_WATCH, },
+    {"modeline", &Vx_mode_pointer_shape, ANDROID_XC_XTERM, },
+    {NULL, &Vx_sensitive_text_pointer_shape, ANDROID_XC_HAND2, },
+    {NULL, &Vx_window_horizontal_drag_shape, ANDROID_XC_SB_H_DOUBLE_ARROW, },
+    {NULL, &Vx_window_vertical_drag_shape, ANDROID_XC_SB_V_DOUBLE_ARROW, },
+    {NULL, &Vx_window_left_edge_shape, ANDROID_XC_LEFT_SIDE, },
+    {NULL, &Vx_window_top_left_corner_shape, ANDROID_XC_TOP_LEFT_CORNER, },
+    {NULL, &Vx_window_top_edge_shape, ANDROID_XC_TOP_SIDE, },
+    {NULL, &Vx_window_top_right_corner_shape, ANDROID_XC_TOP_RIGHT_CORNER, },
+    {NULL, &Vx_window_right_edge_shape, ANDROID_XC_RIGHT_SIDE, },
+    {NULL, &Vx_window_bottom_right_corner_shape,
+     ANDROID_XC_BOTTOM_RIGHT_CORNER, },
+    {NULL, &Vx_window_bottom_edge_shape, ANDROID_XC_BOTTOM_SIDE, },
+    {NULL, &Vx_window_bottom_left_corner_shape,
+       ANDROID_XC_BOTTOM_LEFT_CORNER, },
+  };
+
+struct mouse_cursor_data
+{
+  /* Cursor numbers chosen.  */
+  unsigned int cursor_num[mouse_cursor_max];
+
+  /* Allocated Cursor values, or zero for failed attempts.  */
+  android_cursor cursor[mouse_cursor_max];
+};
+
+
+
+static void
+android_set_mouse_color (struct frame *f, Lisp_Object arg,
+                        Lisp_Object oldval)
+{
+  struct android_output *x = f->output_data.android;
+  struct mouse_cursor_data cursor_data = { -1, -1 };
+  unsigned long pixel = android_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
+  unsigned long mask_color = FRAME_BACKGROUND_PIXEL (f);
+  int i;
+
+  /* Don't let pointers be invisible.  */
+  if (mask_color == pixel)
+    pixel = FRAME_FOREGROUND_PIXEL (f);
+
+  x->mouse_pixel = pixel;
+
+  for (i = 0; i < mouse_cursor_max; i++)
+    {
+      Lisp_Object shape_var = *mouse_cursor_types[i].shape_var_ptr;
+      cursor_data.cursor_num[i]
+       = (!NILP (shape_var)
+          ? check_uinteger_max (shape_var, UINT_MAX)
+          : mouse_cursor_types[i].default_shape);
+    }
+
+  block_input ();
+
+  for (i = 0; i < mouse_cursor_max; i++)
+    cursor_data.cursor[i]
+      = android_create_font_cursor (cursor_data.cursor_num[i]);
+
+  if (FRAME_ANDROID_WINDOW (f))
+    {
+      f->output_data.android->current_cursor
+       = cursor_data.cursor[mouse_cursor_text];
+      android_define_cursor (FRAME_ANDROID_WINDOW (f),
+                            f->output_data.android->current_cursor);
+    }
+
+#define INSTALL_CURSOR(FIELD, SHORT_INDEX)                             \
+   eassert (x->FIELD                                                   \
+           != cursor_data.cursor[mouse_cursor_ ## SHORT_INDEX]);       \
+   if (x->FIELD != 0)                                                  \
+     android_free_cursor (x->FIELD);                                   \
+   x->FIELD = cursor_data.cursor[mouse_cursor_ ## SHORT_INDEX];
+
+  INSTALL_CURSOR (text_cursor, text);
+  INSTALL_CURSOR (nontext_cursor, nontext);
+  INSTALL_CURSOR (hourglass_cursor, hourglass);
+  INSTALL_CURSOR (modeline_cursor, mode);
+  INSTALL_CURSOR (hand_cursor, hand);
+  INSTALL_CURSOR (horizontal_drag_cursor, horizontal_drag);
+  INSTALL_CURSOR (vertical_drag_cursor, vertical_drag);
+  INSTALL_CURSOR (left_edge_cursor, left_edge);
+  INSTALL_CURSOR (top_left_corner_cursor, top_left_corner);
+  INSTALL_CURSOR (top_edge_cursor, top_edge);
+  INSTALL_CURSOR (top_right_corner_cursor, top_right_corner);
+  INSTALL_CURSOR (right_edge_cursor, right_edge);
+  INSTALL_CURSOR (bottom_right_corner_cursor, bottom_right_corner);
+  INSTALL_CURSOR (bottom_edge_cursor, bottom_edge);
+  INSTALL_CURSOR (bottom_left_corner_cursor, bottom_left_corner);
+
+#undef INSTALL_CURSOR
+
+  unblock_input ();
+
+  update_face_from_frame_parameter (f, Qmouse_color, arg);
+}
+
+static void
+android_set_title (struct frame *f, Lisp_Object name,
+                  Lisp_Object old_name)
+{
+  /* Don't change the title if it's already NAME.  */
+  if (EQ (name, f->title))
+    return;
+
+  update_mode_lines = 38;
+
+  fset_title (f, name);
+
+  if (NILP (name))
+    name = f->name;
+  else
+    CHECK_STRING (name);
+}
+
+static void
+android_set_alpha (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+  double alpha = 1.0;
+  double newval[2];
+  int i;
+  Lisp_Object item;
+
+  /* N.B. that setting the window alpha is actually unsupported under
+     Android.  */
+
+  for (i = 0; i < 2; i++)
+    {
+      newval[i] = 1.0;
+      if (CONSP (arg))
+        {
+          item = CAR (arg);
+          arg  = CDR (arg);
+        }
+      else
+        item = arg;
+
+      if (NILP (item))
+       alpha = - 1.0;
+      else if (FLOATP (item))
+       {
+         alpha = XFLOAT_DATA (item);
+         if (! (0 <= alpha && alpha <= 1.0))
+           args_out_of_range (make_float (0.0), make_float (1.0));
+       }
+      else if (FIXNUMP (item))
+       {
+         EMACS_INT ialpha = XFIXNUM (item);
+         if (! (0 <= ialpha && ialpha <= 100))
+           args_out_of_range (make_fixnum (0), make_fixnum (100));
+         alpha = ialpha / 100.0;
+       }
+      else
+       wrong_type_argument (Qnumberp, item);
+      newval[i] = alpha;
+    }
+
+  for (i = 0; i < 2; i++)
+    f->alpha[i] = newval[i];
+
+  if (FRAME_TERMINAL (f)->set_frame_alpha_hook)
+    {
+      block_input ();
+      FRAME_TERMINAL (f)->set_frame_alpha_hook (f);
+      unblock_input ();
+    }
+}
+
+static void
+android_set_no_focus_on_map (struct frame *f, Lisp_Object new_value,
+                            Lisp_Object old_value)
+{
+  if (!EQ (new_value, old_value))
+    {
+      android_set_dont_focus_on_map (FRAME_ANDROID_WINDOW (f),
+                                    !NILP (new_value));
+      FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value);
+    }
+}
+
+static void
+android_set_no_accept_focus (struct frame *f, Lisp_Object new_value,
+                            Lisp_Object old_value)
+{
+  if (!EQ (new_value, old_value))
+    {
+      android_set_dont_accept_focus (FRAME_ANDROID_WINDOW (f),
+                                    !NILP (new_value));
+      FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value);
+    }
+}
+
+frame_parm_handler android_frame_parm_handlers[] =
+{
+  gui_set_autoraise,
+  gui_set_autolower,
+  android_set_background_color,
+  android_set_border_color,
+  gui_set_border_width,
+  android_set_cursor_color,
+  android_set_cursor_type,
+  gui_set_font,
+  android_set_foreground_color,
+  NULL,
+  NULL,
+  android_set_child_frame_border_width,
+  android_set_internal_border_width,
+  gui_set_right_divider_width,
+  gui_set_bottom_divider_width,
+  android_set_menu_bar_lines,
+  android_set_mouse_color,
+  android_explicitly_set_name,
+  gui_set_scroll_bar_width,
+  gui_set_scroll_bar_height,
+  android_set_title,
+  gui_set_unsplittable,
+  gui_set_vertical_scroll_bars,
+  gui_set_horizontal_scroll_bars,
+  gui_set_visibility,
+  android_set_tab_bar_lines,
+  android_set_tool_bar_lines,
+  NULL,
+  NULL,
+  gui_set_screen_gamma,
+  gui_set_line_spacing,
+  gui_set_left_fringe,
+  gui_set_right_fringe,
+  NULL,
+  gui_set_fullscreen,
+  gui_set_font_backend,
+  android_set_alpha,
+  NULL,
+  android_set_tool_bar_position,
+  NULL,
+  NULL,
+  android_set_parent_frame,
+  NULL,
+  android_set_no_focus_on_map,
+  android_set_no_accept_focus,
+  NULL,
+  NULL,
+  gui_set_no_special_glyphs,
+  NULL,
+  NULL,
+};
+
+
+
+/* Battery information support.  */
+
+DEFUN ("android-query-battery", Fandroid_query_battery,
+       Sandroid_query_battery, 0, 0, 0,
+       doc: /* Perform a query for battery information.
+Value is nil upon failure, or a list of the form:
+
+  (CAPACITY CHARGE-COUNTER CURRENT-AVERAGE CURRENT-NOW STATUS
+   REMAINING PLUGGED TEMP)
+
+where REMAINING, CURRENT-AVERAGE, and CURRENT-NOW are undefined prior
+to Android 5.0.
+
+See the documentation at
+
+  https://developer.android.com/reference/android/os/BatteryManager
+
+for more details about these values.  */)
+  (void)
+{
+  struct android_battery_state state;
+
+  /* Make sure the Android libraries have been initialized.  */
+
+  if (!android_init_gui)
+    return Qnil;
+
+  /* Perform the query.  */
+
+  if (android_query_battery (&state))
+    return Qnil;
+
+  return listn (8, make_int (state.capacity),
+               make_fixnum (state.charge_counter),
+               make_int (state.current_average),
+               make_int (state.current_now),
+               make_fixnum (state.status),
+               make_int (state.remaining),
+               make_fixnum (state.plugged),
+               make_fixnum (state.temperature));
+}
+
+
+
+/* Directory access requests.  */
+
+DEFUN ("android-request-directory-access", Fandroid_request_directory_access,
+       Sandroid_request_directory_access, 0, 0, "",
+       doc: /* Request access to a directory within external storage.
+On Android 5.0 and later, prompt for a directory within external or
+application storage, and grant access to it; some of these directories
+cannot be accessed through the regular `/sdcard' filesystem.
+
+If access to the directory is granted, it will eventually appear
+within the directory `/content/storage'.  */)
+  (void)
+{
+  if (android_get_current_api_level () < 21)
+    error ("Emacs can only access application storage on"
+          " Android 5.0 and later");
+
+  if (!android_init_gui)
+    return Qnil;
+
+  android_request_directory_access ();
+  return Qnil;
+}
+
+
+
+/* Miscellaneous input method related stuff.  */
+
+/* Report X, Y, by the phys cursor width and height as the cursor
+   anchor rectangle for W's frame.  */
+
+void
+android_set_preeditarea (struct window *w, int x, int y)
+{
+  struct frame *f;
+
+  f = WINDOW_XFRAME (w);
+
+  /* Convert the window coordinates to the frame's coordinate
+     space.  */
+  x = (WINDOW_TO_FRAME_PIXEL_X (w, x)
+       + WINDOW_LEFT_FRINGE_WIDTH (w)
+       + WINDOW_LEFT_MARGIN_WIDTH (w));
+  y = WINDOW_TO_FRAME_PIXEL_Y (w, y);
+
+  /* Note that calculating the baseline is too hard, so the bottom of
+     the cursor is used instead.  */
+  android_update_cursor_anchor_info (FRAME_ANDROID_WINDOW (f), x,
+                                    y, y + w->phys_cursor_height,
+                                    y + w->phys_cursor_height);
+}
+
+#endif /* !ANDROID_STUBIFY */
+
+
+
+void
+syms_of_androidfns (void)
+{
+  /* Miscellaneous symbols used by some functions here.  */
+  DEFSYM (Qtrue_color, "true-color");
+  DEFSYM (Qalways, "always");
+
+  DEFVAR_LISP ("x-pointer-shape", Vx_pointer_shape,
+    doc: /* SKIP: real text in xfns.c.  */);
+  Vx_pointer_shape = Qnil;
+
+#if false /* This doesn't really do anything.  */
+  DEFVAR_LISP ("x-nontext-pointer-shape", Vx_nontext_pointer_shape,
+    doc: /* SKIP: real doc in xfns.c.  */);
+#endif
+  Vx_nontext_pointer_shape = Qnil;
+
+  DEFVAR_LISP ("x-hourglass-pointer-shape", Vx_hourglass_pointer_shape,
+    doc: /* SKIP: real text in xfns.c.  */);
+  Vx_hourglass_pointer_shape = Qnil;
+
+  DEFVAR_LISP ("x-sensitive-text-pointer-shape",
+             Vx_sensitive_text_pointer_shape,
+    doc: /* SKIP: real text in xfns.c.  */);
+  Vx_sensitive_text_pointer_shape = Qnil;
+
+  DEFVAR_LISP ("x-window-horizontal-drag-cursor",
+             Vx_window_horizontal_drag_shape,
+    doc: /* SKIP: real text in xfns.c.  */);
+  Vx_window_horizontal_drag_shape = Qnil;
+
+  DEFVAR_LISP ("x-window-vertical-drag-cursor",
+             Vx_window_vertical_drag_shape,
+    doc: /* SKIP: real text in xfns.c.  */);
+  Vx_window_vertical_drag_shape = Qnil;
+
+  DEFVAR_LISP ("x-window-left-edge-cursor",
+              Vx_window_left_edge_shape,
+    doc: /* SKIP: real text in xfns.c.  */);
+  Vx_window_left_edge_shape = Qnil;
+
+  DEFVAR_LISP ("x-window-top-left-corner-cursor",
+              Vx_window_top_left_corner_shape,
+    doc: /* SKIP: real text in xfns.c.  */);
+  Vx_window_top_left_corner_shape = Qnil;
+
+  DEFVAR_LISP ("x-window-top-edge-cursor",
+              Vx_window_top_edge_shape,
+    doc: /* SKIP: real text in xfns.c.  */);
+  Vx_window_top_edge_shape = Qnil;
+
+  DEFVAR_LISP ("x-window-top-right-corner-cursor",
+              Vx_window_top_right_corner_shape,
+    doc: /* SKIP: real text in xfns.c.  */);
+  Vx_window_top_right_corner_shape = Qnil;
+
+  DEFVAR_LISP ("x-window-right-edge-cursor",
+              Vx_window_right_edge_shape,
+    doc: /* SKIP: real text in xfns.c.  */);
+  Vx_window_right_edge_shape = Qnil;
+
+  DEFVAR_LISP ("x-window-bottom-right-corner-cursor",
+              Vx_window_bottom_right_corner_shape,
+    doc: /* SKIP: real text in xfns.c.  */);
+  Vx_window_bottom_right_corner_shape = Qnil;
+
+  DEFVAR_LISP ("x-window-bottom-edge-cursor",
+              Vx_window_bottom_edge_shape,
+    doc: /* SKIP: real text in xfns.c.  */);
+  Vx_window_bottom_edge_shape = Qnil;
+
+#if false /* This doesn't really do anything.  */
+  DEFVAR_LISP ("x-mode-pointer-shape", Vx_mode_pointer_shape,
+    doc: /* SKIP: real doc in xfns.c.  */);
+#endif
+  Vx_mode_pointer_shape = Qnil;
+
+  DEFVAR_LISP ("x-window-bottom-left-corner-cursor",
+              Vx_window_bottom_left_corner_shape,
+    doc: /* SKIP: real text in xfns.c.  */);
+  Vx_window_bottom_left_corner_shape = Qnil;
+
+  DEFVAR_LISP ("x-cursor-fore-pixel", Vx_cursor_fore_pixel,
+    doc: /* SKIP: real doc in xfns.c.  */);
+  Vx_cursor_fore_pixel = Qnil;
+
+  /* Used by Fx_show_tip.  */
+  DEFSYM (Qrun_at_time, "run-at-time");
+  DEFSYM (Qx_hide_tip, "x-hide-tip");
+  DEFSYM (Qcancel_timer, "cancel-timer");
+  DEFSYM (Qassq_delete_all, "assq-delete-all");
+  DEFSYM (Qcolor, "color");
+
+  DEFVAR_LISP ("x-max-tooltip-size", Vx_max_tooltip_size,
+    doc: /* SKIP: real doc in xfns.c.  */);
+  Vx_max_tooltip_size = Qnil;
+
+  DEFVAR_BOOL ("android-pass-multimedia-buttons-to-system",
+              android_pass_multimedia_buttons_to_system,
+    doc: /* Whether or not to pass volume control buttons to the system.
+Generally, the `volume-up', `volume-down' and `volume-mute' keys are
+processed by Emacs, but setting this to non-nil they are passed to the
+operating system instead of being intercepted by Emacs.
+
+Note that if you set this, you will no longer be able to quit Emacs
+using the volume down button.  */);
+  android_pass_multimedia_buttons_to_system = false;
+
+  DEFVAR_BOOL ("android-use-exec-loader", android_use_exec_loader,
+    doc: /* Whether or not to bypass system restrictions on program execution.
+
+Android 10 and later prevent programs from executing files installed
+in writable directories, such as the application data directory.
+
+When non-nil, Emacs will bypass this restriction by running such
+executables under system call tracing, and replacing the `execve'
+system call with a version which ignores the system's security
+restrictions.
+
+This option has no effect on Android 9 and earlier.  */);
+  android_use_exec_loader = true;
+
+  /* Functions defined.  */
+  defsubr (&Sx_create_frame);
+  defsubr (&Sxw_color_defined_p);
+  defsubr (&Sxw_color_values);
+  defsubr (&Sxw_display_color_p);
+  defsubr (&Sx_display_grayscale_p);
+  defsubr (&Sx_display_pixel_width);
+  defsubr (&Sx_display_pixel_height);
+  defsubr (&Sx_display_planes);
+  defsubr (&Sx_display_color_cells);
+  defsubr (&Sx_display_screens);
+  defsubr (&Sx_display_mm_width);
+  defsubr (&Sx_display_mm_height);
+  defsubr (&Sx_display_backing_store);
+  defsubr (&Sx_display_visual_class);
+  defsubr (&Sandroid_display_monitor_attributes_list);
+  defsubr (&Sandroid_frame_geometry);
+  defsubr (&Sandroid_frame_edges);
+  defsubr (&Sandroid_frame_list_z_order);
+  defsubr (&Sandroid_frame_restack);
+  defsubr (&Sandroid_mouse_absolute_pixel_position);
+  defsubr (&Sandroid_set_mouse_absolute_pixel_position);
+  defsubr (&Sandroid_get_connection);
+  defsubr (&Sx_display_list);
+  defsubr (&Sx_show_tip);
+  defsubr (&Sx_hide_tip);
+  defsubr (&Sandroid_detect_mouse);
+  defsubr (&Sandroid_toggle_on_screen_keyboard);
+  defsubr (&Sx_server_vendor);
+  defsubr (&Sx_server_version);
+#ifndef ANDROID_STUBIFY
+  defsubr (&Sandroid_query_battery);
+  defsubr (&Sandroid_request_directory_access);
+
+  tip_timer = Qnil;
+  staticpro (&tip_timer);
+  tip_frame = Qnil;
+  staticpro (&tip_frame);
+  tip_last_frame = Qnil;
+  staticpro (&tip_last_frame);
+  tip_last_string = Qnil;
+  staticpro (&tip_last_string);
+  tip_last_parms = Qnil;
+  staticpro (&tip_last_parms);
+  tip_dx = Qnil;
+  staticpro (&tip_dx);
+  tip_dy = Qnil;
+  staticpro (&tip_dy);
+#endif /* !ANDROID_STUBIFY */
+}
diff --git a/src/androidfont.c b/src/androidfont.c
new file mode 100644
index 00000000000..db2f94008f2
--- /dev/null
+++ b/src/androidfont.c
@@ -0,0 +1,1104 @@
+/* Android fallback font driver.
+
+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/>.  */
+
+/* Due to the terrible nature of the Android Typeface subsystems, this
+   font driver is only used as a fallback when sfntfont-android.c
+   fails to enumerate any fonts at all.  */
+
+#include <config.h>
+
+#include "lisp.h"
+#include "dispextern.h"
+#include "composite.h"
+#include "blockinput.h"
+#include "charset.h"
+#include "frame.h"
+#include "window.h"
+#include "fontset.h"
+#include "androidterm.h"
+#include "character.h"
+#include "coding.h"
+#include "font.h"
+#include "termchar.h"
+#include "pdumper.h"
+#include "android.h"
+
+#ifndef ANDROID_STUBIFY
+
+#include <android/log.h>
+
+struct android_emacs_font_driver
+{
+  jclass class;
+  jmethodID list;
+  jmethodID match;
+  jmethodID list_families;
+  jmethodID open_font;
+  jmethodID has_char;
+  jmethodID text_extents;
+  jmethodID encode_char;
+  jmethodID draw;
+
+  /* Static methods.  */
+  jmethodID create_font_driver;
+};
+
+struct android_emacs_font_spec
+{
+  jclass class;
+  jfieldID foundry;
+  jfieldID family;
+  jfieldID adstyle;
+  jfieldID registry;
+  jfieldID width;
+  jfieldID weight;
+  jfieldID slant;
+  jfieldID size;
+  jfieldID spacing;
+  jfieldID avgwidth;
+  jfieldID dpi;
+};
+
+struct android_emacs_font_metrics
+{
+  jclass class;
+  jfieldID lbearing;
+  jfieldID rbearing;
+  jfieldID width;
+  jfieldID ascent;
+  jfieldID descent;
+};
+
+struct android_emacs_font_object
+{
+  jclass class;
+  jfieldID min_width;
+  jfieldID max_width;
+  jfieldID pixel_size;
+  jfieldID height;
+  jfieldID space_width;
+  jfieldID average_width;
+  jfieldID ascent;
+  jfieldID descent;
+  jfieldID underline_thickness;
+  jfieldID underline_position;
+  jfieldID baseline_offset;
+  jfieldID relative_compose;
+  jfieldID default_ascent;
+  jfieldID encoding_charset;
+  jfieldID repertory_charset;
+};
+
+struct android_integer
+{
+  jclass class;
+  jmethodID constructor;
+  jmethodID int_value;
+};
+
+struct androidfont_info
+{
+  /* The font pseudo-vector object.  */
+  struct font font;
+
+  /* The Java-side font.  */
+  jobject object;
+
+  /* Cached glyph metrics arranged in a two dimensional array.  */
+  struct font_metrics **metrics;
+};
+
+struct androidfont_entity
+{
+  /* The font entity pvec.  */
+  struct font_entity font;
+
+  /* The Java-side font entity.  */
+  jobject object;
+};
+
+/* Method and class identifiers associated with the EmacsFontDriver
+   class.  */
+
+struct android_emacs_font_driver font_driver_class;
+
+/* Field and class identifiers associated with the
+   EmacsFontDriver$FontSpec class.  */
+
+struct android_emacs_font_spec font_spec_class;
+
+/* Method and class identifiers associated with the Integer class.  */
+
+struct android_integer integer_class;
+
+/* Field and class identifiers associated with the
+   EmacsFontDriver$FontMetrics class.  */
+
+struct android_emacs_font_metrics font_metrics_class;
+
+/* Field and class identifiers associated with the
+   EmacsFontDriver$FontObject class.  */
+
+struct android_emacs_font_object font_object_class;
+
+/* The font cache.  */
+
+static Lisp_Object font_cache;
+
+/* The Java-side font driver.  */
+
+static jobject font_driver;
+
+
+
+/* Initialize the class and method identifiers for functions in the
+   EmacsFontDriver class, and place them in `font_driver_class'.  */
+
+static void
+android_init_font_driver (void)
+{
+  jclass old;
+
+  font_driver_class.class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "org/gnu/emacs/EmacsFontDriver");
+  eassert (font_driver_class.class);
+
+  old = font_driver_class.class;
+  font_driver_class.class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 (jobject) old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!font_driver_class.class)
+    emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature)                                   
\
+  font_driver_class.c_name                                                     
\
+    = (*android_java_env)->GetMethodID (android_java_env,                      
\
+                                       font_driver_class.class,                
\
+                                       name, signature);                       
\
+  eassert (font_driver_class.c_name);
+
+  FIND_METHOD (list, "list", "(Lorg/gnu/emacs/EmacsFontDriver$FontSpec;)"
+              "[Lorg/gnu/emacs/EmacsFontDriver$FontEntity;");
+  FIND_METHOD (match, "match", "(Lorg/gnu/emacs/EmacsFontDriver$FontSpec;)"
+              "Lorg/gnu/emacs/EmacsFontDriver$FontEntity;");
+  FIND_METHOD (list_families, "listFamilies", "()[Ljava/lang/String;");
+  FIND_METHOD (open_font, "openFont", "(Lorg/gnu/emacs/EmacsFontDriver$Font"
+              "Entity;I)Lorg/gnu/emacs/EmacsFontDriver$FontObject;");
+  FIND_METHOD (has_char, "hasChar", "(Lorg/gnu/emacs/EmacsFontDriver$Font"
+              "Spec;C)I");
+  FIND_METHOD (text_extents, "textExtents", "(Lorg/gnu/emacs/EmacsFontDriver"
+              "$FontObject;[ILorg/gnu/emacs/EmacsFontDriver$FontMetrics;)V");
+  FIND_METHOD (encode_char, "encodeChar", "(Lorg/gnu/emacs/EmacsFontDriver"
+              "$FontObject;C)I");
+  FIND_METHOD (draw, "draw", "(Lorg/gnu/emacs/EmacsFontDriver$FontObject;"
+              "Lorg/gnu/emacs/EmacsGC;Lorg/gnu/emacs/EmacsDrawable;[IIIIZ)I");
+
+  font_driver_class.create_font_driver
+    = (*android_java_env)->GetStaticMethodID (android_java_env,
+                                             font_driver_class.class,
+                                             "createFontDriver",
+                                             "()Lorg/gnu/emacs/"
+                                             "EmacsFontDriver;");
+  eassert (font_driver_class.create_font_driver);
+#undef FIND_METHOD
+}
+
+/* Initialize the class and field identifiers for functions in the
+   EmacsFontDriver$FontSpec class, and place them in
+   `font_spec_class'.  */
+
+static void
+android_init_font_spec (void)
+{
+  jclass old;
+
+  font_spec_class.class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "org/gnu/emacs/EmacsFontDriver"
+                                     "$FontSpec");
+  eassert (font_spec_class.class);
+
+  old = font_spec_class.class;
+  font_spec_class.class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 (jobject) old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!font_spec_class.class)
+    emacs_abort ();
+
+#define FIND_FIELD(c_name, name, signature)                                    
\
+  font_spec_class.c_name                                                       
\
+    = (*android_java_env)->GetFieldID (android_java_env,                       
\
+                                      font_spec_class.class,                   
\
+                                      name, signature);                        
\
+  eassert (font_spec_class.c_name);
+
+  FIND_FIELD (foundry, "foundry", "Ljava/lang/String;");
+  FIND_FIELD (family, "family", "Ljava/lang/String;");
+  FIND_FIELD (adstyle, "adstyle", "Ljava/lang/String;");
+  FIND_FIELD (registry, "registry", "Ljava/lang/String;");
+  FIND_FIELD (width, "width", "Ljava/lang/Integer;");
+  FIND_FIELD (weight, "weight", "Ljava/lang/Integer;");
+  FIND_FIELD (slant, "slant", "Ljava/lang/Integer;");
+  FIND_FIELD (size, "size", "Ljava/lang/Integer;");
+  FIND_FIELD (spacing, "spacing", "Ljava/lang/Integer;");
+  FIND_FIELD (avgwidth, "avgwidth", "Ljava/lang/Integer;");
+  FIND_FIELD (dpi, "dpi", "Ljava/lang/Integer;");
+#undef FIND_FIELD
+}
+
+static void
+android_init_font_metrics (void)
+{
+  jclass old;
+
+  font_metrics_class.class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "org/gnu/emacs/EmacsFontDriver"
+                                     "$FontMetrics");
+  eassert (font_metrics_class.class);
+
+  old = font_metrics_class.class;
+  font_metrics_class.class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 (jobject) old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!font_metrics_class.class)
+    emacs_abort ();
+
+#define FIND_FIELD(c_name, name, signature)                                    
\
+  font_metrics_class.c_name                                                    
\
+    = (*android_java_env)->GetFieldID (android_java_env,                       
\
+                                      font_metrics_class.class,                
\
+                                      name, signature);                        
\
+  eassert (font_metrics_class.c_name);
+
+  FIND_FIELD (lbearing, "lbearing", "S");
+  FIND_FIELD (rbearing, "rbearing", "S");
+  FIND_FIELD (width, "width", "S");
+  FIND_FIELD (ascent, "ascent", "S");
+  FIND_FIELD (descent, "descent", "S");
+#undef FIND_FIELD
+}
+
+static void
+android_init_integer (void)
+{
+  jclass old;
+
+  integer_class.class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "java/lang/Integer");
+  eassert (integer_class.class);
+
+  old = integer_class.class;
+  integer_class.class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 (jobject) old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!integer_class.class)
+    emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature)                                   
\
+  integer_class.c_name                                                         
\
+    = (*android_java_env)->GetMethodID (android_java_env,                      
\
+                                       integer_class.class,                    
\
+                                       name, signature);                       
\
+  eassert (integer_class.c_name);
+
+  FIND_METHOD (constructor, "<init>", "(I)V");
+  FIND_METHOD (int_value, "intValue", "()I");
+#undef FIND_METHOD
+}
+
+static void
+android_init_font_object (void)
+{
+  jclass old;
+
+  font_object_class.class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "org/gnu/emacs/EmacsFontDriver"
+                                     "$FontObject");
+  eassert (font_object_class.class);
+
+  old = font_object_class.class;
+  font_object_class.class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 (jobject) old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!font_object_class.class)
+    emacs_abort ();
+
+#define FIND_FIELD(c_name, name, signature)                                    
\
+  font_object_class.c_name                                                     
\
+    = (*android_java_env)->GetFieldID (android_java_env,                       
\
+                                      font_object_class.class,                 
\
+                                      name, signature);                        
\
+  eassert (font_object_class.c_name);
+
+  FIND_FIELD (min_width, "minWidth", "I");
+  FIND_FIELD (max_width, "maxWidth", "I");
+  FIND_FIELD (pixel_size, "pixelSize", "I");
+  FIND_FIELD (height, "height", "I");
+  FIND_FIELD (space_width, "spaceWidth", "I");
+  FIND_FIELD (average_width, "averageWidth", "I");
+  FIND_FIELD (ascent, "ascent", "I");
+  FIND_FIELD (descent, "descent", "I");
+  FIND_FIELD (underline_thickness, "underlineThickness", "I");
+  FIND_FIELD (underline_position, "underlinePosition", "I");
+  FIND_FIELD (baseline_offset, "baselineOffset", "I");
+  FIND_FIELD (relative_compose, "relativeCompose", "I");
+  FIND_FIELD (default_ascent, "defaultAscent", "I");
+  FIND_FIELD (encoding_charset, "encodingCharset", "I");
+  FIND_FIELD (repertory_charset, "repertoryCharset", "I");
+#undef FIND_FIELD
+}
+
+static Lisp_Object
+androidfont_get_cache (struct frame *frame)
+{
+  return font_cache;
+}
+
+/* Initialize the Java side of the font driver if it has not already
+   been initialized.  This is only done whenever necessary because the
+   font driver otherwise uses a lot of memory, as it has to keep every
+   typeface open.  */
+
+static void
+androidfont_check_init (void)
+{
+  jmethodID method;
+  jobject old;
+
+  if (font_driver)
+    return;
+
+  /* Log a loud message.  This font driver really should not be
+     used.  */
+  __android_log_print (ANDROID_LOG_WARN, __func__,
+                      "The Android font driver is being used."
+                      "  Please investigate why this is so.");
+
+  method = font_driver_class.create_font_driver;
+
+  /* Initialize the font driver on the Java side.  */
+  font_driver
+    = (*android_java_env)->CallStaticObjectMethod (android_java_env,
+                                                  font_driver_class.class,
+                                                  method);
+  android_exception_check ();
+
+  old = font_driver;
+  font_driver
+    = (*android_java_env)->NewGlobalRef (android_java_env, font_driver);
+  ANDROID_DELETE_LOCAL_REF (old);
+}
+
+/* Return a local reference to an instance of EmacsFontDriver$FontSpec
+   with the same values as FONT.  */
+
+static jobject
+androidfont_from_lisp (Lisp_Object font)
+{
+  jobject spec, integer;
+  jstring string;
+  Lisp_Object tem;
+
+  spec = (*android_java_env)->AllocObject (android_java_env,
+                                          font_spec_class.class);
+  android_exception_check ();
+
+#define DO_SYMBOL_FIELD(field, index)                                          
\
+  tem = AREF (font, index);                                                    
\
+  if (SYMBOLP (tem))                                                           
\
+    {                                                                          
\
+      /* Java seems to DTRT with the Emacs string encoding, so this does       
\
+        not matter at all.  */                                                 
\
+      string = (*android_java_env)->NewStringUTF (android_java_env,            
\
+                                                 SSDATA (SYMBOL_NAME (tem)));  
\
+      android_exception_check_1 (spec);                                        
        \
+                                                                               
\
+      (*android_java_env)->SetObjectField (android_java_env, spec,             
\
+                                          font_spec_class.field,               
\
+                                          string);                             
\
+      ANDROID_DELETE_LOCAL_REF (string);                                       
\
+    }                                                                          
\
+
+  DO_SYMBOL_FIELD (foundry, FONT_FOUNDRY_INDEX);
+  DO_SYMBOL_FIELD (family, FONT_FAMILY_INDEX);
+  DO_SYMBOL_FIELD (adstyle, FONT_ADSTYLE_INDEX);
+  DO_SYMBOL_FIELD (registry, FONT_REGISTRY_INDEX);
+
+#undef DO_SYMBOL_FIELD
+
+#define DO_CARDINAL_FIELD(field, value)                                        
        \
+  if (value != -1)                                                             
\
+    {                                                                          
\
+      integer = (*android_java_env)->NewObject (android_java_env,              
\
+                                               integer_class.class,            
\
+                                               integer_class.constructor,      
\
+                                               (jint) value);                  
\
+      android_exception_check_1 (spec);                                        
        \
+                                                                               
\
+      (*android_java_env)->SetObjectField (android_java_env, spec,             
\
+                                          font_spec_class.field,               
\
+                                          integer);                            
\
+      ANDROID_DELETE_LOCAL_REF (integer);                                      
\
+    }
+
+  DO_CARDINAL_FIELD (width, FONT_WIDTH_NUMERIC (font));
+  DO_CARDINAL_FIELD (weight, FONT_WEIGHT_NUMERIC (font));
+  DO_CARDINAL_FIELD (slant, FONT_SLANT_NUMERIC (font));
+  DO_CARDINAL_FIELD (size, (FIXNUMP (AREF (font, FONT_SIZE_INDEX))
+                           ? XFIXNUM (AREF (font, FONT_SIZE_INDEX))
+                           : -1));
+  DO_CARDINAL_FIELD (spacing, (FIXNUMP (AREF (font, FONT_SPACING_INDEX))
+                              ? XFIXNUM (AREF (font, FONT_SPACING_INDEX))
+                              : -1));
+  DO_CARDINAL_FIELD (avgwidth, (FIXNUMP (AREF (font, FONT_AVGWIDTH_INDEX))
+                               ? XFIXNUM (AREF (font, FONT_AVGWIDTH_INDEX))
+                               : -1));
+  DO_CARDINAL_FIELD (dpi, (FIXNUMP (AREF (font, FONT_DPI_INDEX))
+                          ? XFIXNUM (AREF (font, FONT_DPI_INDEX))
+                          : -1));
+
+#undef DO_CARDINAL_FIELD
+
+  return spec;
+}
+
+static void
+androidfont_from_java (jobject spec, Lisp_Object entity)
+{
+  jobject tem;
+  jint value;
+  const char *string;
+
+#define DO_SYMBOL_FIELD(field, index)                                          
\
+  tem = (*android_java_env)->GetObjectField (android_java_env,                 
\
+                                            spec,                              
\
+                                            font_spec_class.field);            
\
+  if (tem)                                                                     
\
+    {                                                                          
\
+      string = (*android_java_env)->GetStringUTFChars (android_java_env,       
\
+                                                      tem, NULL);              
\
+      if (!string)                                                             
\
+       memory_full (0);                                                        
\
+      ASET (entity, index, intern (string));                                   
\
+      (*android_java_env)->ReleaseStringUTFChars (android_java_env,            
\
+                                                 tem, string);                 
\
+      ANDROID_DELETE_LOCAL_REF (tem);                                          
\
+    }
+
+  DO_SYMBOL_FIELD (foundry, FONT_FOUNDRY_INDEX);
+  DO_SYMBOL_FIELD (family, FONT_FAMILY_INDEX);
+  DO_SYMBOL_FIELD (adstyle, FONT_ADSTYLE_INDEX);
+  DO_SYMBOL_FIELD (registry, FONT_REGISTRY_INDEX);
+
+#undef DO_SYMBOL_FIELD
+#define DO_CARDINAL_FIELD(field, index, is_style)                      \
+  tem = (*android_java_env)->GetObjectField (android_java_env,         \
+                                            spec,                      \
+                                            font_spec_class.field);    \
+  if (tem)                                                             \
+    {                                                                  \
+      value                                                            \
+       = (*android_java_env)->CallIntMethod (android_java_env,         \
+                                             tem,                      \
+                                             integer_class.int_value); \
+      if (!is_style)                                                   \
+       ASET (entity, index, make_fixnum (value));                      \
+      else                                                             \
+       FONT_SET_STYLE (entity, index, make_fixnum (value));            \
+      ANDROID_DELETE_LOCAL_REF (tem);                                  \
+    }
+
+  DO_CARDINAL_FIELD (width, FONT_WIDTH_INDEX, true);
+  DO_CARDINAL_FIELD (weight, FONT_WEIGHT_INDEX, true);
+  DO_CARDINAL_FIELD (slant, FONT_SLANT_INDEX, true);
+  DO_CARDINAL_FIELD (size, FONT_SIZE_INDEX, false);
+  DO_CARDINAL_FIELD (spacing, FONT_SPACING_INDEX, false);
+  DO_CARDINAL_FIELD (avgwidth, FONT_AVGWIDTH_INDEX, false);
+  DO_CARDINAL_FIELD (dpi, FONT_DPI_INDEX, false);
+
+#undef DO_CARDINAL_FIELD
+}
+
+/* Transfer the values from FONT, which must be some kind of font
+   entity, */
+
+static Lisp_Object
+androidfont_list (struct frame *f, Lisp_Object font_spec)
+{
+  jobject spec, array, tem;
+  jarray entities;
+  jsize i, size;
+  Lisp_Object value, entity;
+  struct androidfont_entity *info;
+
+  /* Maybe initialize the font driver.  */
+  androidfont_check_init ();
+
+  spec = androidfont_from_lisp (font_spec);
+  array = (*android_java_env)->CallObjectMethod (android_java_env,
+                                                font_driver,
+                                                font_driver_class.list,
+                                                spec);
+  android_exception_check_1 (spec);
+  ANDROID_DELETE_LOCAL_REF (spec);
+
+  entities = (jarray) array;
+  size = (*android_java_env)->GetArrayLength (android_java_env,
+                                             entities);
+  value = Qnil;
+
+  for (i = 0; i < size; ++i)
+    {
+      entity = font_make_entity_android (VECSIZE (struct androidfont_entity));
+      info = (struct androidfont_entity *) XFONT_ENTITY (entity);
+
+      /* The type must be set correctly, or font_open_entity won't be
+        able to find the right font driver.  */
+      ASET (entity, FONT_TYPE_INDEX, Qandroid);
+
+      /* Clear this now in case GC happens without it set, which can
+        happen if androidfont_from_java runs out of memory.  */
+      info->object = NULL;
+
+      tem = (*android_java_env)->GetObjectArrayElement (android_java_env,
+                                                       entities, i);
+      androidfont_from_java (tem, entity);
+
+      /* Now, make a global reference to the Java font entity.  */
+      info->object = (*android_java_env)->NewGlobalRef (android_java_env,
+                                                       (jobject) tem);
+      android_exception_check_2 (tem, entities);
+      ANDROID_DELETE_LOCAL_REF (tem);
+
+      value = Fcons (entity, value);
+    }
+
+  ANDROID_DELETE_LOCAL_REF (entities);
+  return Fnreverse (value);
+}
+
+static Lisp_Object
+androidfont_match (struct frame *f, Lisp_Object font_spec)
+{
+  jobject spec, result;
+  Lisp_Object entity;
+  struct androidfont_entity *info;
+
+  /* Maybe initialize the font driver.  */
+  androidfont_check_init ();
+
+  spec = androidfont_from_lisp (font_spec);
+  result = (*android_java_env)->CallObjectMethod (android_java_env,
+                                                 font_driver,
+                                                 font_driver_class.match,
+                                                 spec);
+  android_exception_check_1 (spec);
+  ANDROID_DELETE_LOCAL_REF (spec);
+
+  entity = font_make_entity_android (VECSIZE (struct androidfont_entity));
+  info = (struct androidfont_entity *) XFONT_ENTITY (entity);
+
+  /* The type must be set correctly, or font_open_entity won't be able
+     to find the right font driver.  */
+  ASET (entity, FONT_TYPE_INDEX, Qandroid);
+
+  info->object = NULL;
+  androidfont_from_java (result, entity);
+  info->object = (*android_java_env)->NewGlobalRef (android_java_env,
+                                                   (jobject) result);
+  android_exception_check_1 (result);
+  ANDROID_DELETE_LOCAL_REF (result);
+
+  return entity;
+}
+
+static int
+androidfont_draw (struct glyph_string *s, int from, int to,
+                 int x, int y, bool with_background)
+{
+  struct androidfont_info *info;
+  jarray chars;
+  int rc;
+  jobject gcontext, drawable;
+
+  /* Maybe initialize the font driver.  */
+  androidfont_check_init ();
+
+  verify (sizeof (unsigned int) == sizeof (jint));
+  info = (struct androidfont_info *) s->font;
+
+  gcontext = android_resolve_handle (s->gc->gcontext,
+                                    ANDROID_HANDLE_GCONTEXT);
+  drawable = android_resolve_handle (FRAME_ANDROID_DRAWABLE (s->f),
+                                    ANDROID_HANDLE_WINDOW);
+  chars = (*android_java_env)->NewIntArray (android_java_env,
+                                           to - from);
+  android_exception_check ();
+
+  (*android_java_env)->SetIntArrayRegion (android_java_env, chars,
+                                         0, to - from,
+                                         (jint *) s->char2b + from);
+
+  info = (struct androidfont_info *) s->font;
+  prepare_face_for_display (s->f, s->face);
+
+  rc = (*android_java_env)->CallIntMethod (android_java_env,
+                                          font_driver,
+                                          font_driver_class.draw,
+                                          info->object,
+                                          gcontext, drawable,
+                                          chars, (jint) x, (jint) y,
+                                          (jint) s->width,
+                                          (jboolean) with_background);
+  android_exception_check_1 (chars);
+  ANDROID_DELETE_LOCAL_REF (chars);
+
+  return rc;
+}
+
+static Lisp_Object
+androidfont_open_font (struct frame *f, Lisp_Object font_entity,
+                      int pixel_size)
+{
+  struct androidfont_info *font_info;
+  struct androidfont_entity *entity;
+  struct font *font;
+  Lisp_Object font_object;
+  jobject old;
+  jint value;
+
+  /* Maybe initialize the font driver.  */
+  androidfont_check_init ();
+
+  if (XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)) != 0)
+    pixel_size = XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX));
+  else if (pixel_size == 0)
+    {
+      /* This bit was copied from xfont.c.  The values might need
+        adjustment.  */
+
+      if (FRAME_FONT (f))
+       pixel_size = FRAME_FONT (f)->pixel_size;
+      else
+       pixel_size = 12;
+    }
+
+  entity = (struct androidfont_entity *) XFONT_ENTITY (font_entity);
+
+  block_input ();
+  font_object = font_make_object (VECSIZE (struct androidfont_info),
+                                 font_entity, pixel_size);
+  ASET (font_object, FONT_TYPE_INDEX, Qandroid);
+  font_info = (struct androidfont_info *) XFONT_OBJECT (font_object);
+  font = &font_info->font;
+  font->driver = &androidfont_driver;
+
+  /* Clear font_info->object and font_info->metrics early in case GC
+     happens later on! */
+  font_info->object = NULL;
+  font_info->metrics = NULL;
+  unblock_input ();
+
+  font_info->object
+    = (*android_java_env)->CallObjectMethod (android_java_env,
+                                            font_driver,
+                                            font_driver_class.open_font,
+                                            entity->object,
+                                            (jint) pixel_size);
+  android_exception_check ();
+
+  old = font_info->object;
+  font_info->object
+    = (*android_java_env)->NewGlobalRef (android_java_env, old);
+  android_exception_check_1 (old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!font_info->object)
+    return Qnil;
+
+  /* Copy the font attributes from the Java object.  */
+  androidfont_from_java (font_info->object, font_object);
+
+  /* Copy font attributes inside EmacsFontDriver$FontObject.  */
+#define DO_CARDINAL_FIELD(field)                                       \
+  value                                                                        
\
+    = (*android_java_env)->GetIntField (android_java_env,              \
+                                       font_info->object,              \
+                                       font_object_class.field);       \
+  font->field = value;
+
+  DO_CARDINAL_FIELD (min_width);
+  DO_CARDINAL_FIELD (max_width);
+  DO_CARDINAL_FIELD (pixel_size);
+  DO_CARDINAL_FIELD (height);
+  DO_CARDINAL_FIELD (space_width);
+  DO_CARDINAL_FIELD (average_width);
+  DO_CARDINAL_FIELD (ascent);
+  DO_CARDINAL_FIELD (descent);
+  DO_CARDINAL_FIELD (underline_thickness);
+  DO_CARDINAL_FIELD (underline_position);
+  DO_CARDINAL_FIELD (baseline_offset);
+  DO_CARDINAL_FIELD (relative_compose);
+  DO_CARDINAL_FIELD (default_ascent);
+  DO_CARDINAL_FIELD (encoding_charset);
+  DO_CARDINAL_FIELD (repertory_charset);
+
+#undef DO_CARDINAL_FIELD
+
+  /* This should eventually become unnecessary.  */
+  font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil);
+
+  return font_object;
+}
+
+static void
+androidfont_close_font (struct font *font)
+{
+  struct androidfont_info *info;
+  int i;
+
+  /* Maybe initialize the font driver.  */
+  androidfont_check_init ();
+
+  info = (struct androidfont_info *) font;
+
+  /* Free the font metrics cache if it exists.  */
+
+  if (info->metrics)
+    {
+      for (i = 0; i < 256; ++i)
+       xfree (info->metrics[i]);
+      xfree (info->metrics);
+    }
+
+  info->metrics = NULL;
+
+  /* If info->object is NULL, then FONT was unsuccessfully created,
+     and there is no global reference that has to be deleted.
+
+     Alternatively, FONT may have been closed by font_close_object,
+     with this function called from GC.  */
+
+  if (!info->object)
+    return;
+
+  (*android_java_env)->DeleteGlobalRef (android_java_env,
+                                       info->object);
+  info->object = NULL;
+}
+
+static int
+androidfont_has_char (Lisp_Object font, int c)
+{
+  struct androidfont_info *info;
+  struct androidfont_entity *entity;
+
+  /* Maybe initialize the font driver.  */
+  androidfont_check_init ();
+
+  if (FONT_ENTITY_P (font))
+    {
+      entity = (struct androidfont_entity *) XFONT_ENTITY (font);
+
+      return (*android_java_env)->CallIntMethod (android_java_env,
+                                                font_driver,
+                                                font_driver_class.has_char,
+                                                entity->object, (jint) c);
+    }
+  else
+    {
+      info = (struct androidfont_info *) XFONT_OBJECT (font);
+
+      return (*android_java_env)->CallIntMethod (android_java_env,
+                                                font_driver,
+                                                font_driver_class.has_char,
+                                                info->object, (jint) c);
+    }
+}
+
+static unsigned
+androidfont_encode_char (struct font *font, int c)
+{
+  struct androidfont_info *info;
+
+  /* Maybe initialize the font driver.  */
+  androidfont_check_init ();
+
+  info = (struct androidfont_info *) font;
+
+  return (*android_java_env)->CallIntMethod (android_java_env,
+                                            font_driver,
+                                            font_driver_class.encode_char,
+                                            info->object, (jchar) c);
+}
+
+static void
+androidfont_cache_text_extents (struct androidfont_info *info,
+                               unsigned int glyph,
+                               struct font_metrics *metrics)
+{
+  int i;
+
+  /* Glyphs larger than 65535 can't be cached.  */
+  if (glyph >= 256 * 256)
+    return;
+
+  if (!info->metrics)
+    info->metrics = xzalloc (256 * sizeof *info->metrics);
+
+  if (!info->metrics[glyph / 256])
+    {
+      info->metrics[glyph / 256]
+       = xnmalloc (256, sizeof **info->metrics);
+
+      /* Now, all the metrics in that array as invalid by setting
+        lbearing to SHRT_MAX.  */
+      for (i = 0; i < 256; ++i)
+       info->metrics[glyph / 256][i].lbearing = SHRT_MAX;
+    }
+
+  /* Finally, cache the glyph.  */
+  info->metrics[glyph / 256][glyph % 256] = *metrics;
+}
+
+static bool
+androidfont_check_cached_extents (struct androidfont_info *info,
+                                 unsigned int glyph,
+                                 struct font_metrics *metrics)
+{
+  if (info->metrics && info->metrics[glyph / 256]
+      && info->metrics[glyph / 256][glyph % 256].lbearing != SHRT_MAX)
+    {
+      *metrics = info->metrics[glyph / 256][glyph % 256];
+      return true;
+    }
+
+  return false;
+}
+
+static void
+androidfont_text_extents (struct font *font, const unsigned int *code,
+                         int nglyphs, struct font_metrics *metrics)
+{
+  struct androidfont_info *info;
+  jarray codepoint_array;
+  jobject metrics_object;
+  short value;
+
+  /* Maybe initialize the font driver.  */
+  androidfont_check_init ();
+
+  info = (struct androidfont_info *) font;
+
+  if (nglyphs == 1
+      && androidfont_check_cached_extents (info, *code, metrics))
+    return;
+
+  /* Allocate the arrays of code points and font metrics.  */
+  codepoint_array
+    = (*android_java_env)->NewIntArray (android_java_env,
+                                       nglyphs);
+  if (!codepoint_array)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      memory_full (0);
+    }
+
+  verify (sizeof (unsigned int) == sizeof (jint));
+
+  /* Always true on every Android device.  */
+  (*android_java_env)->SetIntArrayRegion (android_java_env,
+                                         codepoint_array,
+                                         0, nglyphs,
+                                         (jint *) code);
+
+  metrics_object
+    = (*android_java_env)->AllocObject (android_java_env,
+                                       font_metrics_class.class);
+
+  (*android_java_env)->CallVoidMethod (android_java_env,
+                                      font_driver,
+                                      font_driver_class.text_extents,
+                                      info->object, codepoint_array,
+                                      metrics_object);
+
+  if ((*android_java_env)->ExceptionCheck (android_java_env))
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      ANDROID_DELETE_LOCAL_REF (metrics_object);
+      ANDROID_DELETE_LOCAL_REF (codepoint_array);
+      memory_full (0);
+    }
+
+#define DO_CARDINAL_FIELD(field)                                       \
+  value                                                                        
\
+    = (*android_java_env)->GetShortField (android_java_env,            \
+                                         metrics_object,               \
+                                         font_metrics_class.field);    \
+  metrics->field = value;
+
+  DO_CARDINAL_FIELD (lbearing);
+  DO_CARDINAL_FIELD (rbearing);
+  DO_CARDINAL_FIELD (width);
+  DO_CARDINAL_FIELD (ascent);
+  DO_CARDINAL_FIELD (descent);
+
+#undef DO_CARDINAL_FIELD
+
+  ANDROID_DELETE_LOCAL_REF (metrics_object);
+  ANDROID_DELETE_LOCAL_REF (codepoint_array);
+
+  /* Emacs spends a lot of time in androidfont_text_extents, which
+     makes calling JNI too slow.  Cache the metrics for this single
+     glyph.  */
+
+  if (nglyphs == 1)
+    androidfont_cache_text_extents (info, *code, metrics);
+}
+
+static Lisp_Object
+androidfont_list_family (struct frame *f)
+{
+  Lisp_Object families;
+  jarray family_array;
+  jobject string;
+  jsize i, length;
+  const char *family;
+
+  /* Return if the Android font driver is not initialized.  Loading
+     every font under Android takes a non trivial amount of memory,
+     and is not something that should be done when the user tries to
+     list all of the font families.  */
+
+  if (!font_driver)
+    return Qnil;
+
+  family_array
+    = (*android_java_env)->CallObjectMethod (android_java_env,
+                                            font_driver,
+                                            font_driver_class.list_families);
+  android_exception_check ();
+
+  length = (*android_java_env)->GetArrayLength (android_java_env,
+                                               family_array);
+  families = Qnil;
+
+  for (i = 0; i < length; ++i)
+    {
+      string = (*android_java_env)->GetObjectArrayElement (android_java_env,
+                                                          family_array, i);
+      family = (*android_java_env)->GetStringUTFChars (android_java_env,
+                                                      (jstring) string, NULL);
+
+      if (!family)
+       {
+         ANDROID_DELETE_LOCAL_REF (string);
+         ANDROID_DELETE_LOCAL_REF (family_array);
+       }
+
+      families = Fcons (build_string_from_utf8 (string), families);
+      (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+                                                 (jstring) string,
+                                                 family);
+      ANDROID_DELETE_LOCAL_REF (string);
+    }
+
+  ANDROID_DELETE_LOCAL_REF (family_array);
+  return Fnreverse (families);
+}
+
+struct font_driver androidfont_driver =
+  {
+    .type = LISPSYM_INITIALLY (Qandroid),
+    .case_sensitive = true,
+    .get_cache = androidfont_get_cache,
+    .list = androidfont_list,
+    .match = androidfont_match,
+    .draw = androidfont_draw,
+    .open_font = androidfont_open_font,
+    .close_font = androidfont_close_font,
+    .has_char = androidfont_has_char,
+    .encode_char = androidfont_encode_char,
+    .text_extents = androidfont_text_extents,
+    .list_family = androidfont_list_family,
+  };
+
+static void
+syms_of_androidfont_for_pdumper (void)
+{
+  register_font_driver (&androidfont_driver, NULL);
+}
+
+void
+syms_of_androidfont (void)
+{
+  DEFSYM (Qfontsize, "fontsize");
+
+  pdumper_do_now_and_after_load (syms_of_androidfont_for_pdumper);
+
+  font_cache = list (Qnil);
+  staticpro (&font_cache);
+}
+
+void
+init_androidfont (void)
+{
+  if (!android_init_gui)
+    return;
+
+  android_init_font_driver ();
+  android_init_font_spec ();
+  android_init_font_metrics ();
+  android_init_font_object ();
+  android_init_integer ();
+
+  /* The Java font driver is not initialized here because it uses a lot
+     of memory.  */
+}
+
+void
+android_finalize_font_entity (struct font_entity *entity)
+{
+  struct androidfont_entity *info;
+
+  info = (struct androidfont_entity *) entity;
+
+  if (info->object)
+    (*android_java_env)->DeleteGlobalRef (android_java_env,
+                                         info->object);
+
+  /* Not sure if this can be called twice.  */
+  info->object = NULL;
+}
+
+#endif
diff --git a/src/androidgui.h b/src/androidgui.h
new file mode 100644
index 00000000000..14225f7bf80
--- /dev/null
+++ b/src/androidgui.h
@@ -0,0 +1,754 @@
+/* Android window system support.
+   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/>.  */
+
+#ifndef _ANDROID_GUI_H_
+#define _ANDROID_GUI_H_
+
+struct android_char_struct
+{
+  int rbearing;
+  int lbearing;
+  int width;
+  int ascent;
+  int descent;
+};
+
+typedef struct android_char_struct XCharStruct;
+
+typedef unsigned short android_handle;
+
+typedef android_handle android_pixmap, Emacs_Pixmap;
+typedef android_handle android_window, Emacs_Window;
+typedef android_handle android_gcontext, GContext;
+typedef android_handle android_drawable, Drawable;
+typedef android_handle android_cursor, Emacs_Cursor;
+
+typedef unsigned int android_time;
+
+struct android_rectangle
+{
+  int x, y;
+  unsigned width, height;
+};
+
+struct android_point
+{
+  int x, y;
+};
+
+/* Keep this in sync with EmacsGC.java! */
+
+enum android_gc_function
+  {
+    ANDROID_GC_COPY    = 0,
+    ANDROID_GC_XOR     = 1,
+  };
+
+enum android_gc_value_mask
+  {
+    ANDROID_GC_FOREGROUND        = (1 << 0),
+    ANDROID_GC_BACKGROUND        = (1 << 1),
+    ANDROID_GC_FUNCTION                  = (1 << 2),
+    ANDROID_GC_CLIP_X_ORIGIN     = (1 << 3),
+    ANDROID_GC_CLIP_Y_ORIGIN     = (1 << 4),
+    ANDROID_GC_CLIP_MASK         = (1 << 5),
+    ANDROID_GC_STIPPLE           = (1 << 6),
+    ANDROID_GC_FILL_STYLE        = (1 << 7),
+    ANDROID_GC_TILE_STIP_X_ORIGIN = (1 << 8),
+    ANDROID_GC_TILE_STIP_Y_ORIGIN = (1 << 9),
+  };
+
+enum android_fill_style
+  {
+    ANDROID_FILL_SOLID          = 0,
+    ANDROID_FILL_OPAQUE_STIPPLED = 1,
+  };
+
+enum android_window_value_mask
+  {
+    ANDROID_CW_BACK_PIXEL       = (1 << 1),
+    ANDROID_CW_OVERRIDE_REDIRECT = (1 << 2),
+  };
+
+struct android_set_window_attributes
+{
+  /* The background pixel.  */
+  unsigned long background_pixel;
+
+  /* Whether or not the window is override redirect.  This cannot be
+     set after creation on Android.  */
+  bool override_redirect;
+};
+
+struct android_gc_values
+{
+  /* The foreground and background.  */
+  unsigned long foreground, background;
+
+  /* The function.  */
+  enum android_gc_function function;
+
+  /* The fill style.  */
+  enum android_fill_style fill_style;
+
+  /* The clip X and Y origin.  */
+  int clip_x_origin, clip_y_origin;
+
+  /* The clip mask image and stipple.  */
+  android_pixmap clip_mask, stipple;
+
+  /* The tile-stipple X and Y origins.  */
+  int ts_x_origin, ts_y_origin;
+};
+
+/* X-like graphics context structure.  This is implemented in
+   EmacsGC.java, but a copy is kept here to avoid sending changes all
+   the time.  */
+
+struct android_gc
+{
+  /* Array of clip rectangles.  */
+  struct android_rectangle *clip_rects;
+
+  /* Number of clip rectangles.  When -1, it means clipping should not
+     be applied.  */
+  int num_clip_rects;
+
+  /* The Java-side handle.  */
+  android_gcontext gcontext;
+
+  /* Current foreground color.  */
+  unsigned long foreground;
+
+  /* Current background color.  */
+  unsigned long background;
+
+  /* The function.  */
+  enum android_gc_function function;
+
+  /* The fill style.  */
+  enum android_fill_style fill_style;
+
+  /* The clip X and Y origin.  */
+  int clip_x_origin, clip_y_origin;
+
+  /* The clip mask image and stipple.  */
+  android_pixmap clip_mask, stipple;
+
+  /* The tile-stipple X and Y origins.  */
+  int ts_x_origin, ts_y_origin;
+};
+
+enum android_swap_action
+  {
+    ANDROID_COPIED,
+  };
+
+enum android_shape
+  {
+    ANDROID_CONVEX,
+  };
+
+enum android_coord_mode
+  {
+    ANDROID_COORD_MODE_ORIGIN,
+  };
+
+struct android_swap_info
+{
+  /* The window to swap.  */
+  android_window swap_window;
+
+  /* Unused field present only for consistency with X.  */
+  enum android_swap_action swap_action;
+};
+
+#define NativeRectangle                        Emacs_Rectangle
+#define CONVERT_TO_NATIVE_RECT(xr, nr) ((xr) = (nr))
+#define CONVERT_FROM_EMACS_RECT(xr, nr) ((nr) = (xr))
+
+#define STORE_NATIVE_RECT(nr, rx, ry, rwidth, rheight) \
+  ((nr).x = (rx), (nr).y = (ry),                       \
+   (nr).width = (rwidth), (nr).height = (rheight))     \
+
+#define ForgetGravity          0
+#define NorthWestGravity       1
+#define NorthGravity           2
+#define NorthEastGravity       3
+#define WestGravity            4
+#define CenterGravity          5
+#define EastGravity            6
+#define SouthWestGravity       7
+#define SouthGravity           8
+#define SouthEastGravity       9
+#define StaticGravity          10
+
+#define NoValue                0x0000
+#define XValue         0x0001
+#define YValue         0x0002
+#define WidthValue     0x0004
+#define HeightValue    0x0008
+#define AllValues      0x000F
+#define XNegative      0x0010
+#define YNegative      0x0020
+
+#define USPosition     (1L << 0) /* user specified x, y */
+#define USSize         (1L << 1) /* user specified width, height */
+#define PPosition      (1L << 2) /* program specified position */
+#define PSize          (1L << 3) /* program specified size */
+#define PMinSize       (1L << 4) /* program specified minimum size */
+#define PMaxSize       (1L << 5) /* program specified maximum size */
+#define PResizeInc     (1L << 6) /* program specified resize increments */
+#define PAspect                (1L << 7) /* program specified min, max aspect 
ratios */
+#define PBaseSize      (1L << 8) /* program specified base for incrementing */
+#define PWinGravity    (1L << 9) /* program specified window gravity */
+
+#ifndef ANDROID_STUBIFY
+
+/* Universal NULL handle.  */
+static const int ANDROID_NONE, ANDROID_NO_SYMBOL;
+
+/* Keep these as conceptually close to X as possible: that makes
+   synchronizing code between the ports much easier.  */
+
+enum android_event_type
+  {
+    ANDROID_KEY_PRESS,
+    ANDROID_KEY_RELEASE,
+    ANDROID_CONFIGURE_NOTIFY,
+    ANDROID_FOCUS_IN,
+    ANDROID_FOCUS_OUT,
+    ANDROID_WINDOW_ACTION,
+    ANDROID_ENTER_NOTIFY,
+    ANDROID_LEAVE_NOTIFY,
+    ANDROID_MOTION_NOTIFY,
+    ANDROID_BUTTON_PRESS,
+    ANDROID_BUTTON_RELEASE,
+    ANDROID_TOUCH_DOWN,
+    ANDROID_TOUCH_UP,
+    ANDROID_TOUCH_MOVE,
+    ANDROID_WHEEL,
+    ANDROID_ICONIFIED,
+    ANDROID_DEICONIFIED,
+    ANDROID_CONTEXT_MENU,
+    ANDROID_EXPOSE,
+    ANDROID_INPUT_METHOD,
+  };
+
+struct android_any_event
+{
+  enum android_event_type type;
+  unsigned long serial;
+  android_window window;
+};
+
+enum android_modifier_mask
+  {
+    ANDROID_SHIFT_MASK  = 193,
+    ANDROID_CONTROL_MASK = 4096,
+    ANDROID_ALT_MASK    = 2,
+    ANDROID_SUPER_MASK  = 4,
+    ANDROID_META_MASK   = 65536,
+  };
+
+struct android_key_event
+{
+  enum android_event_type type;
+  unsigned long serial;
+  android_window window;
+  android_time time;
+  unsigned int state;
+  unsigned int keycode;
+
+  /* If this field is -1, then android_lookup_string should be called
+     to retrieve the associated individual characters.  */
+  unsigned int unicode_char;
+
+  /* If this field is non-zero, a text conversion barrier should be
+     generated with its value as the counter.  */
+  unsigned long counter;
+};
+
+typedef struct android_key_event android_key_pressed_event;
+
+/* These hard coded values are Android modifier keycodes derived
+   through experimentation.  */
+
+#define ANDROID_IS_MODIFIER_KEY(key)                                   \
+  ((key) == 57 || (key) == 58 || (key) == 113 || (key) == 114          \
+   || (key) == 119 || (key) == 117 || (key) == 118 || (key) == 78      \
+   || (key) == 94 || (key) == 59 || (key) == 60 || (key) == 95         \
+   || (key) == 63 || (key) == 115)
+
+struct android_configure_event
+{
+  enum android_event_type type;
+  unsigned long serial;
+  android_window window;
+  android_time time;
+  int x, y;
+  int width, height;
+};
+
+struct android_focus_event
+{
+  enum android_event_type type;
+  unsigned long serial;
+  android_window window;
+  android_time time;
+};
+
+struct android_window_action_event
+{
+  enum android_event_type type;
+  unsigned long serial;
+
+  /* The window handle.  This can be ANDROID_NONE.  */
+  android_window window;
+
+  /* Numerical identifier for this action.  If 0 and WINDOW is set,
+     then it means the frame associated with that window has been
+     destroyed.  Otherwise, it means Emacs should create a new
+     frame.  */
+  unsigned int action;
+};
+
+struct android_crossing_event
+{
+  enum android_event_type type;
+  unsigned long serial;
+  android_window window;
+  int x, y;
+  unsigned long time;
+};
+
+struct android_motion_event
+{
+  enum android_event_type type;
+  unsigned long serial;
+  android_window window;
+  int x, y;
+  unsigned long time;
+};
+
+struct android_button_event
+{
+  enum android_event_type type;
+  unsigned long serial;
+  android_window window;
+  int x, y;
+  unsigned long time;
+  unsigned int state;
+  unsigned int button;
+};
+
+struct android_expose_event
+{
+  enum android_event_type type;
+  unsigned long serial;
+  android_window window;
+  int x, y;
+  int width, height;
+};
+
+enum android_touch_event_flags
+  {
+    /* This touch sequence has been intercepted by the WM (probably
+       for back gesture navigation or some such.)  */
+    ANDROID_TOUCH_SEQUENCE_CANCELED = 1,
+  };
+
+struct android_touch_event
+{
+  /* Type of the event.  */
+  enum android_event_type type;
+
+  /* Serial identifying the event.  */
+  unsigned long serial;
+
+  /* Window associated with the event.  */
+  android_window window;
+
+  /* X and Y coordinates of the event.  */
+  int x, y;
+
+  /* Time of the event, and the pointer identifier.  */
+  unsigned long time;
+
+  /* Index of the pointer being tracked.  */
+  unsigned int pointer_id;
+
+  /* Flags associated with this event.  */
+  int flags;
+};
+
+struct android_wheel_event
+{
+  /* Type of the event.  */
+  enum android_event_type type;
+
+  /* Serial identifying the event.  */
+  unsigned long serial;
+
+  /* Window associated with the event.  */
+  android_window window;
+
+  /* X and Y coordinates of the event.  */
+  int x, y;
+
+  /* Time of the event, and the pointer identifier.  */
+  unsigned long time;
+
+  /* Modifier state at the time of the event.  */
+  int state;
+
+  /* Motion alongside the X and Y axes.  */
+  double x_delta, y_delta;
+};
+
+struct android_iconify_event
+{
+  /* Type of the event.  */
+  enum android_event_type type;
+
+  /* Serial identifying the event.  */
+  unsigned long serial;
+
+  /* Window associated with the event.  */
+  android_window window;
+};
+
+struct android_menu_event
+{
+  /* Type of the event.  */
+  enum android_event_type type;
+
+  /* Serial identifying the event.  */
+  unsigned long serial;
+
+  /* Window associated with the event.  Always None.  */
+  android_window window;
+
+  /* Menu event ID.  */
+  int menu_event_id;
+
+  /* Menu event serial; this counter identifies the context menu.  */
+  int menu_event_serial;
+};
+
+enum android_ime_operation
+  {
+    ANDROID_IME_COMMIT_TEXT,
+    ANDROID_IME_DELETE_SURROUNDING_TEXT,
+    ANDROID_IME_FINISH_COMPOSING_TEXT,
+    ANDROID_IME_SET_COMPOSING_TEXT,
+    ANDROID_IME_SET_COMPOSING_REGION,
+    ANDROID_IME_SET_POINT,
+    ANDROID_IME_START_BATCH_EDIT,
+    ANDROID_IME_END_BATCH_EDIT,
+    ANDROID_IME_REQUEST_SELECTION_UPDATE,
+    ANDROID_IME_REQUEST_CURSOR_UPDATES,
+  };
+
+enum
+  {
+    ANDROID_CURSOR_UPDATE_IMMEDIATE = 1,
+    ANDROID_CURSOR_UPDATE_MONITOR   = (1 << 1),
+  };
+
+struct android_ime_event
+{
+  /* Type of the event.  */
+  enum android_event_type type;
+
+  /* The event serial.  */
+  unsigned long serial;
+
+  /* The associated window.  */
+  android_window window;
+
+  /* What operation is being performed.  */
+  enum android_ime_operation operation;
+
+  /* The details of the operation.  START and END provide buffer
+     indices, and may actually mean ``left'' and ``right''.  */
+  ptrdiff_t start, end, position;
+
+  /* The number of characters in TEXT.
+
+     If OPERATION is ANDROID_IME_REQUEST_CURSOR_UPDATES, then this is
+     actually the cursor update mode associated with that
+     operation.  */
+  size_t length;
+
+  /* TEXT is either NULL, or a pointer to LENGTH bytes of malloced
+     UTF-16 encoded text that must be decoded by Emacs.
+
+     POSITION is where point should end up after the text is
+     committed, relative to TEXT.  If POSITION is less than 0, it is
+     relative to TEXT's start; otherwise, it is relative to its
+     end.  */
+  unsigned short *text;
+
+  /* Value to set the counter to after the operation completes.  */
+  unsigned long counter;
+};
+
+union android_event
+{
+  enum android_event_type type;
+  struct android_any_event xany;
+  struct android_key_event xkey;
+  struct android_configure_event xconfigure;
+  struct android_focus_event xfocus;
+  struct android_window_action_event xaction;
+  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.  */
+  struct android_touch_event touch;
+
+  /* This has no parallel in X outside the X Input Extension, and
+     emulating the input extension interface would be awfully
+     complicated.  */
+  struct android_wheel_event wheel;
+
+  /* This has no parallel in X because Android doesn't have window
+     properties.  */
+  struct android_iconify_event iconified;
+
+  /* This is only used to transmit selected menu items.  */
+  struct android_menu_event menu;
+
+  /* This is used to dispatch input method editing requests.  */
+  struct android_ime_event ime;
+};
+
+enum
+  {
+    ANDROID_CURRENT_TIME = 0L,
+  };
+
+enum android_lookup_status
+  {
+    ANDROID_BUFFER_OVERFLOW,
+    ANDROID_LOOKUP_NONE,
+    ANDROID_LOOKUP_CHARS,
+    ANDROID_LOOKUP_KEYSYM,
+    ANDROID_LOOKUP_BOTH,
+  };
+
+enum android_ic_mode
+  {
+    ANDROID_IC_MODE_NULL   = 0,
+    ANDROID_IC_MODE_ACTION = 1,
+    ANDROID_IC_MODE_TEXT   = 2,
+  };
+
+extern int android_pending (void);
+extern void android_next_event (union android_event *);
+extern bool android_check_if_event (union android_event *,
+                                   bool (*) (union android_event *,
+                                             void *),
+                                   void *);
+
+extern android_window android_create_window (android_window, int,
+                                            int, int, int,
+                                            enum android_window_value_mask,
+                                            struct
+                                            android_set_window_attributes *);
+extern void android_change_window_attributes (android_window,
+                                             enum android_window_value_mask,
+                                             struct
+                                             android_set_window_attributes *);
+extern void android_set_window_background (android_window, unsigned long);
+extern void android_destroy_window (android_window);
+extern void android_reparent_window (android_window, android_window,
+                                    int, int);
+extern void android_set_clip_rectangles (struct android_gc *,
+                                        int, int,
+                                        struct android_rectangle *,
+                                        int);
+extern void android_change_gc (struct android_gc *,
+                              enum android_gc_value_mask,
+                              struct android_gc_values *);
+
+extern void android_clear_window (android_window);
+extern void android_map_window (android_window);
+extern void android_unmap_window (android_window);
+extern void android_resize_window (android_window, unsigned int,
+                                  unsigned int);
+extern void android_move_window (android_window, int, int);
+extern void android_swap_buffers (struct android_swap_info *, int);
+extern void android_get_gc_values (struct android_gc *,
+                                  enum android_gc_value_mask,
+                                  struct android_gc_values *);
+extern void android_set_foreground (struct android_gc *,
+                                   unsigned long);
+extern void android_fill_rectangle (android_drawable, struct android_gc *,
+                                   int, int, unsigned int, unsigned int);
+extern android_pixmap android_create_pixmap_from_bitmap_data (char *,
+                                                             unsigned int,
+                                                             unsigned int,
+                                                             unsigned long,
+                                                             unsigned long,
+                                                             unsigned int);
+extern void android_set_clip_mask (struct android_gc *, android_pixmap);
+extern void android_set_fill_style (struct android_gc *,
+                                   enum android_fill_style);
+extern void android_copy_area (android_drawable, android_drawable,
+                              struct android_gc *, int, int,
+                              unsigned int, unsigned int, int, int);
+extern void android_free_pixmap (android_drawable);
+
+extern void android_set_background (struct android_gc *, unsigned long);
+extern void android_fill_polygon (android_drawable, struct android_gc *,
+                                 struct android_point *, int,
+                                 enum android_shape,
+                                 enum android_coord_mode);
+extern void android_draw_rectangle (android_drawable, struct android_gc *,
+                                   int, int, unsigned int, unsigned int);
+extern void android_draw_point (android_window, struct android_gc *,
+                               int, int);
+extern void android_draw_line (android_window, struct android_gc *,
+                              int, int, int, int);
+extern android_pixmap android_create_pixmap (unsigned int, unsigned int,
+                                            int);
+extern void android_set_ts_origin (struct android_gc *, int, int);
+extern void android_clear_area (android_window, int, int, unsigned int,
+                               unsigned int);
+extern android_pixmap android_create_bitmap_from_data (char *, unsigned int,
+                                                      unsigned int);
+
+extern void android_bell (void);
+extern void android_set_input_focus (android_window, unsigned long);
+extern void android_raise_window (android_window);
+extern void android_lower_window (android_window);
+extern int android_query_tree (android_window, android_window *,
+                              android_window *, android_window **,
+                              unsigned int *);
+extern void android_get_geometry (android_window, android_window *,
+                                 int *, int *, unsigned int *,
+                                 unsigned int *, unsigned int *);
+extern void android_move_resize_window (android_window, int, int,
+                                       unsigned int, unsigned int);
+extern void android_map_raised (android_window);
+extern void android_translate_coordinates (android_window, int,
+                                          int, int *, int *);
+extern int android_wc_lookup_string (android_key_pressed_event *,
+                                    wchar_t *, int, int *,
+                                    enum android_lookup_status *);
+extern void android_update_ic (android_window, ptrdiff_t, ptrdiff_t,
+                              ptrdiff_t, ptrdiff_t);
+extern void android_reset_ic (android_window, enum android_ic_mode);
+extern void android_update_extracted_text (android_window, void *,
+                                          int);
+extern void android_update_cursor_anchor_info (android_window, float,
+                                              float, float, float);
+extern int android_set_fullscreen (android_window, bool);
+
+enum android_cursor_shape
+  {
+    ANDROID_XC_XTERM = 1008,
+    ANDROID_XC_LEFT_PTR = 1000,
+    ANDROID_XC_WATCH = 1004,
+    ANDROID_XC_HAND2 = 1002,
+    ANDROID_XC_SB_H_DOUBLE_ARROW = 1014,
+    ANDROID_XC_SB_V_DOUBLE_ARROW = 1015,
+    ANDROID_XC_LEFT_SIDE = 1020,
+    ANDROID_XC_TOP_LEFT_CORNER = 1020,
+    ANDROID_XC_TOP_SIDE = 1020,
+    ANDROID_XC_TOP_RIGHT_CORNER = 1020,
+    ANDROID_XC_RIGHT_SIDE = 1020,
+    ANDROID_XC_BOTTOM_RIGHT_CORNER = 1020,
+    ANDROID_XC_BOTTOM_SIDE = 1020,
+    ANDROID_XC_BOTTOM_LEFT_CORNER = 1020,
+    ANDROID_XC_NULL = 0,
+  };
+
+extern android_cursor android_create_font_cursor (enum android_cursor_shape);
+extern void android_define_cursor (android_window, android_cursor);
+extern void android_free_cursor (android_cursor);
+
+#endif
+
+
+
+/* Image support.  Keep the API as similar to XImage as possible.  To
+   avoid leaving a huge mess of "#ifndef ANDROID_STUBIFY" in image.c,
+   stubs should be defined for all functions.  */
+
+enum android_image_format
+  {
+    ANDROID_Z_PIXMAP,
+  };
+
+struct android_image
+{
+  int width, height;
+  enum android_image_format format;
+  char *data;
+  int depth;
+  int bytes_per_line;
+  int bits_per_pixel;
+};
+
+extern struct android_image *android_create_image (unsigned int,
+                                                  enum android_image_format,
+                                                  char *, unsigned int,
+                                                  unsigned int);
+extern void android_destroy_image (struct android_image *);
+
+extern void android_put_pixel (struct android_image *, int, int,
+                              unsigned long);
+extern unsigned long android_get_pixel (struct android_image *, int, int);
+extern struct android_image *android_get_image (android_drawable,
+                                               enum android_image_format);
+extern void android_put_image (android_pixmap, struct android_image *);
+
+
+/* Native image transforms.  */
+
+/* 3x2 matrix describing a projective transform.  See
+   android_transform_coordinates for details.  */
+
+struct android_transform
+{
+  float m1, m2, m3;
+  float m4, m5, m6;
+};
+
+extern void android_project_image_bilinear (struct android_image *,
+                                           struct android_image *,
+                                           struct android_transform *);
+extern void android_project_image_nearest (struct android_image *,
+                                          struct android_image *,
+                                          struct android_transform *);
+
+
+
+/* X emulation stuff also needed while building stubs.  */
+
+extern struct android_gc *android_create_gc (enum android_gc_value_mask,
+                                            struct android_gc_values *);
+extern void android_free_gc (struct android_gc *);
+
+#endif /* _ANDROID_GUI_H_ */
diff --git a/src/androidmenu.c b/src/androidmenu.c
new file mode 100644
index 00000000000..94e3f646b44
--- /dev/null
+++ b/src/androidmenu.c
@@ -0,0 +1,844 @@
+/* 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 "lisp.h"
+#include "androidterm.h"
+#include "android.h"
+#include "blockinput.h"
+#include "keyboard.h"
+#include "menu.h"
+
+#ifndef ANDROID_STUBIFY
+
+#include <android/log.h>
+
+/* Flag indicating whether or not a popup menu has been posted and not
+   yet popped down.  */
+
+static int popup_activated_flag;
+
+/* Serial number used to identify which context menu events are
+   associated with the context menu currently being displayed.  */
+
+unsigned int current_menu_serial;
+
+int
+popup_activated (void)
+{
+  return popup_activated_flag;
+}
+
+
+
+/* Toolkit menu implementation.  */
+
+/* Structure describing the EmacsContextMenu class.  */
+
+struct android_emacs_context_menu
+{
+  jclass class;
+  jmethodID create_context_menu;
+  jmethodID add_item;
+  jmethodID add_submenu;
+  jmethodID add_pane;
+  jmethodID parent;
+  jmethodID display;
+  jmethodID dismiss;
+};
+
+/* Identifiers associated with the EmacsContextMenu class.  */
+static struct android_emacs_context_menu menu_class;
+
+static void
+android_init_emacs_context_menu (void)
+{
+  jclass old;
+
+  menu_class.class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "org/gnu/emacs/EmacsContextMenu");
+  eassert (menu_class.class);
+
+  old = menu_class.class;
+  menu_class.class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 (jobject) old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!menu_class.class)
+    emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature)                   \
+  menu_class.c_name                                            \
+    = (*android_java_env)->GetMethodID (android_java_env,      \
+                                       menu_class.class,       \
+                                       name, signature);       \
+  eassert (menu_class.c_name);
+
+#define FIND_METHOD_STATIC(c_name, name, signature)            \
+  menu_class.c_name                                            \
+    = (*android_java_env)->GetStaticMethodID (android_java_env,        \
+                                             menu_class.class, \
+                                             name, signature); \
+  eassert (menu_class.c_name);
+
+  FIND_METHOD_STATIC (create_context_menu, "createContextMenu",
+                     "(Ljava/lang/String;)"
+                     "Lorg/gnu/emacs/EmacsContextMenu;");
+
+  FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;ZZZ"
+              "Ljava/lang/String;Z)V");
+  FIND_METHOD (add_submenu, "addSubmenu", "(Ljava/lang/String;"
+              "Ljava/lang/String;)"
+              "Lorg/gnu/emacs/EmacsContextMenu;");
+  FIND_METHOD (add_pane, "addPane", "(Ljava/lang/String;)V");
+  FIND_METHOD (parent, "parent", "()Lorg/gnu/emacs/EmacsContextMenu;");
+  FIND_METHOD (display, "display", "(Lorg/gnu/emacs/EmacsWindow;III)Z");
+  FIND_METHOD (dismiss, "dismiss", "(Lorg/gnu/emacs/EmacsWindow;)V");
+
+#undef FIND_METHOD
+#undef FIND_METHOD_STATIC
+}
+
+static void
+android_unwind_local_frame (void)
+{
+  (*android_java_env)->PopLocalFrame (android_java_env, NULL);
+}
+
+/* Push a local reference frame to the JVM stack and record it on the
+   specpdl.  Release local references created within that frame when
+   the specpdl is unwound past where it is after returning.  */
+
+static void
+android_push_local_frame (void)
+{
+  int rc;
+
+  rc = (*android_java_env)->PushLocalFrame (android_java_env, 30);
+
+  /* This means the JVM ran out of memory.  */
+  if (rc < 1)
+    android_exception_check ();
+
+  record_unwind_protect_void (android_unwind_local_frame);
+}
+
+/* Data for android_dismiss_menu.  */
+
+struct android_dismiss_menu_data
+{
+  /* The menu object.  */
+  jobject menu;
+
+  /* The window object.  */
+  jobject window;
+};
+
+/* Cancel the context menu passed in POINTER.  Also, clear
+   popup_activated_flag.  */
+
+static void
+android_dismiss_menu (void *pointer)
+{
+  struct android_dismiss_menu_data *data;
+
+  data = pointer;
+  (*android_java_env)->CallVoidMethod (android_java_env,
+                                      data->menu,
+                                      menu_class.dismiss,
+                                      data->window);
+  popup_activated_flag = 0;
+}
+
+/* Recursively process events until a ANDROID_CONTEXT_MENU event
+   arrives.  Then, return the item ID specified in the event in
+   *ID.  */
+
+static void
+android_process_events_for_menu (int *id)
+{
+  int blocked;
+
+  /* Set menu_event_id to -1; handle_one_android_event will set it to
+     the event ID upon receiving a context menu event.  This can cause
+     a non-local exit.  */
+  x_display_list->menu_event_id = -1;
+
+  /* Unblock input completely.  */
+  blocked = interrupt_input_blocked;
+  totally_unblock_input ();
+
+  /* Now wait for the menu event ID to change.  */
+  while (x_display_list->menu_event_id == -1)
+    {
+      /* Wait for events to become available.  */
+      android_wait_event ();
+
+      /* Process pending signals.  */
+      process_pending_signals ();
+
+      /* Maybe quit.  This is important because the framework (on
+        Android 4.0.3) can sometimes fail to deliver context menu
+        closed events if a submenu was opened, and the user still
+        needs to be able to quit.  */
+      maybe_quit ();
+    }
+
+  /* Restore the input block.  */
+  interrupt_input_blocked = blocked;
+
+  /* Return the ID.  */
+  *id = x_display_list->menu_event_id;
+}
+
+/* Structure describing a ``subprefix'' in the menu.  */
+
+struct android_menu_subprefix
+{
+  /* The subprefix above.  */
+  struct android_menu_subprefix *last;
+
+  /* The subprefix itself.  */
+  Lisp_Object subprefix;
+};
+
+/* Free the subprefixes starting from *DATA.  */
+
+static void
+android_free_subprefixes (void *data)
+{
+  struct android_menu_subprefix **head, *subprefix;
+
+  head = data;
+
+  while (*head)
+    {
+      subprefix = *head;
+      *head = subprefix->last;
+
+      xfree (subprefix);
+    }
+}
+
+Lisp_Object
+android_menu_show (struct frame *f, int x, int y, int menuflags,
+                  Lisp_Object title, const char **error_name)
+{
+  jobject context_menu, current_context_menu;
+  jobject title_string, help_string, temp;
+  size_t i;
+  Lisp_Object pane_name, prefix;
+  const char *pane_string;
+  specpdl_ref count, count1;
+  Lisp_Object item_name, enable, def, tem, entry, type, selected;
+  Lisp_Object help;
+  jmethodID method;
+  jobject store;
+  bool rc;
+  jobject window;
+  int id, item_id, submenu_depth;
+  struct android_dismiss_menu_data data;
+  struct android_menu_subprefix *subprefix, *temp_subprefix;
+  struct android_menu_subprefix *subprefix_1;
+  bool checkmark;
+  unsigned int serial;
+
+  count = SPECPDL_INDEX ();
+  serial = ++current_menu_serial;
+
+  block_input ();
+
+  /* Push the first local frame.  */
+  android_push_local_frame ();
+
+  /* Set title_string to a Java string containing TITLE if non-nil.
+     If the menu consists of more than one pane, replace the title
+     with the pane header item so that the menu looks consistent.  */
+
+  title_string = NULL;
+  if (STRINGP (title) && menu_items_n_panes < 2)
+    title_string = android_build_string (title);
+
+  /* Push the first local frame for the context menu.  */
+  method = menu_class.create_context_menu;
+  current_context_menu = context_menu
+    = (*android_java_env)->CallStaticObjectMethod (android_java_env,
+                                                  menu_class.class,
+                                                  method,
+                                                  title_string);
+
+  /* Delete the unused title reference.  */
+
+  if (title_string)
+    ANDROID_DELETE_LOCAL_REF (title_string);
+
+  /* Push the second local frame for temporaries.  */
+  count1 = SPECPDL_INDEX ();
+  android_push_local_frame ();
+
+  /* Iterate over the menu.  */
+  i = 0, submenu_depth = 0;
+
+  while (i < menu_items_used)
+    {
+      if (NILP (AREF (menu_items, i)))
+       {
+         /* This is the start of a new submenu.  However, it can be
+            ignored here.  */
+         i += 1;
+         submenu_depth += 1;
+       }
+      else if (EQ (AREF (menu_items, i), Qlambda))
+       {
+         /* This is the end of a submenu.  Go back to the previous
+            context menu.  */
+         store = current_context_menu;
+         current_context_menu
+           = (*android_java_env)->CallObjectMethod (android_java_env,
+                                                    current_context_menu,
+                                                    menu_class.parent);
+         android_exception_check ();
+
+         if (store != context_menu)
+           ANDROID_DELETE_LOCAL_REF (store);
+         i += 1;
+         submenu_depth -= 1;
+
+         if (!current_context_menu || submenu_depth < 0)
+           {
+             __android_log_print (ANDROID_LOG_FATAL, __func__,
+                                  "unbalanced submenu pop in menu_items");
+             emacs_abort ();
+           }
+       }
+      else if (EQ (AREF (menu_items, i), Qt)
+              && submenu_depth != 0)
+       i += MENU_ITEMS_PANE_LENGTH;
+      else if (EQ (AREF (menu_items, i), Qquote))
+       i += 1;
+      else if (EQ (AREF (menu_items, i), Qt))
+       {
+         /* If the menu contains a single pane, then the pane is
+            actually TITLE.  Don't duplicate the text within the
+            context menu title.  */
+
+         if (menu_items_n_panes < 2)
+           goto next_item;
+
+         /* This is a new pane.  Switch back to the topmost context
+            menu.  */
+         if (current_context_menu != context_menu)
+           ANDROID_DELETE_LOCAL_REF (current_context_menu);
+         current_context_menu = context_menu;
+
+         /* Now figure out the title of this pane.  */
+         pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
+         prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+         pane_string = (NILP (pane_name)
+                        ? "" : SSDATA (pane_name));
+         if ((menuflags & MENU_KEYMAPS) && !NILP (prefix))
+           pane_string++;
+
+         /* Add the pane.  */
+         temp = (*android_java_env)->NewStringUTF (android_java_env,
+                                                   pane_string);
+         android_exception_check ();
+
+         (*android_java_env)->CallVoidMethod (android_java_env,
+                                              current_context_menu,
+                                              menu_class.add_pane,
+                                              temp);
+         android_exception_check ();
+         ANDROID_DELETE_LOCAL_REF (temp);
+
+       next_item:
+         i += MENU_ITEMS_PANE_LENGTH;
+       }
+      else
+       {
+         item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+         enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+         def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
+         type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
+         selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
+         help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
+
+         /* This is an actual menu item (or submenu).  Add it to the
+            menu.  */
+
+         if (i + MENU_ITEMS_ITEM_LENGTH < menu_items_used
+             && NILP (AREF (menu_items, i + MENU_ITEMS_ITEM_LENGTH)))
+           {
+             /* This is a submenu.  Add it.  */
+             title_string = (!NILP (item_name)
+                             ? android_build_string (item_name)
+                             : NULL);
+             help_string = NULL;
+
+             /* Menu items can have tool tips on Android 26 and
+                later.  In this case, set it to the help string.  */
+
+             if (android_get_current_api_level () >= 26
+                 && STRINGP (help))
+               help_string = android_build_string (help);
+
+             store = current_context_menu;
+             current_context_menu
+               = (*android_java_env)->CallObjectMethod (android_java_env,
+                                                        current_context_menu,
+                                                        menu_class.add_submenu,
+                                                        title_string,
+                                                        help_string);
+             android_exception_check ();
+
+             if (store != context_menu)
+               ANDROID_DELETE_LOCAL_REF (store);
+
+             if (title_string)
+               ANDROID_DELETE_LOCAL_REF (title_string);
+
+             if (help_string)
+               ANDROID_DELETE_LOCAL_REF (help_string);
+           }
+         else if (NILP (def) && menu_separator_name_p (SSDATA (item_name)))
+           /* Ignore this separator item.  */
+           ;
+         else
+           {
+             /* Compute the item ID.  This is the index of value.
+                Make sure it doesn't overflow.  */
+
+             if (!INT_ADD_OK (0, i + MENU_ITEMS_ITEM_VALUE, &item_id))
+               memory_full (i + MENU_ITEMS_ITEM_VALUE * sizeof (Lisp_Object));
+
+             /* Add this menu item with the appropriate state.  */
+
+             title_string = (!NILP (item_name)
+                             ? android_build_string (item_name)
+                             : NULL);
+             help_string = NULL;
+
+             /* Menu items can have tool tips on Android 26 and
+                later.  In this case, set it to the help string.  */
+
+             if (android_get_current_api_level () >= 26
+                 && STRINGP (help))
+               help_string = android_build_string (help);
+
+             /* Determine whether or not to display a check box.  */
+
+             checkmark = (EQ (type, QCtoggle)
+                          || EQ (type, QCradio));
+
+             (*android_java_env)->CallVoidMethod (android_java_env,
+                                                  current_context_menu,
+                                                  menu_class.add_item,
+                                                  (jint) item_id,
+                                                  title_string,
+                                                  (jboolean) !NILP (enable),
+                                                  (jboolean) checkmark,
+                                                  (jboolean) !NILP (selected),
+                                                  help_string,
+                                                  (jboolean) (EQ (type,
+                                                                  QCradio)));
+             android_exception_check ();
+
+             if (title_string)
+               ANDROID_DELETE_LOCAL_REF (title_string);
+
+             if (help_string)
+               ANDROID_DELETE_LOCAL_REF (help_string);
+           }
+
+         i += MENU_ITEMS_ITEM_LENGTH;
+       }
+    }
+
+  /* The menu has now been built.  Pop the second local frame.  */
+  unbind_to (count1, Qnil);
+
+  /* Now, display the context menu.  */
+  window = android_resolve_handle (FRAME_ANDROID_WINDOW (f),
+                                  ANDROID_HANDLE_WINDOW);
+  rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+                                              context_menu,
+                                              menu_class.display,
+                                              window, (jint) x,
+                                              (jint) y,
+                                              (jint) serial);
+  android_exception_check ();
+
+  if (!rc)
+    /* This means displaying the menu failed.  */
+    goto finish;
+
+  /* Make sure the context menu is always dismissed.  */
+  data.menu = context_menu;
+  data.window = window;
+  record_unwind_protect_ptr (android_dismiss_menu, &data);
+
+  /* Next, process events waiting for something to be selected.  */
+  popup_activated_flag = 1;
+  android_process_events_for_menu (&id);
+
+  if (!id)
+    /* This means no menu item was selected.  */
+    goto finish;
+
+  /* This means the id is invalid.  */
+  if (id >= ASIZE (menu_items))
+    goto finish;
+
+  /* Now return the menu item at that location.  */
+  tem = Qnil;
+  subprefix = NULL;
+  record_unwind_protect_ptr (android_free_subprefixes, &subprefix);
+
+  /* Find the selected item, and its pane, to return
+     the proper value.  */
+
+  prefix = entry = Qnil;
+  i = 0;
+  while (i < menu_items_used)
+    {
+      if (NILP (AREF (menu_items, i)))
+       {
+         temp_subprefix = xmalloc (sizeof *temp_subprefix);
+         temp_subprefix->last = subprefix;
+         subprefix = temp_subprefix;
+         subprefix->subprefix = prefix;
+
+         prefix = entry;
+         i++;
+       }
+      else if (EQ (AREF (menu_items, i), Qlambda))
+       {
+         prefix = subprefix->subprefix;
+         temp_subprefix = subprefix->last;
+         xfree (subprefix);
+         subprefix = temp_subprefix;
+
+         i++;
+       }
+      else if (EQ (AREF (menu_items, i), Qt))
+       {
+         prefix
+           = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+         i += MENU_ITEMS_PANE_LENGTH;
+       }
+      /* Ignore a nil in the item list.
+        It's meaningful only for dialog boxes.  */
+      else if (EQ (AREF (menu_items, i), Qquote))
+       i += 1;
+      else
+       {
+         entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
+
+         if (i + MENU_ITEMS_ITEM_VALUE == id)
+           {
+             if (menuflags & MENU_KEYMAPS)
+               {
+                 entry = list1 (entry);
+
+                 if (!NILP (prefix))
+                   entry = Fcons (prefix, entry);
+
+                 for (subprefix_1 = subprefix; subprefix_1;
+                      subprefix_1 = subprefix_1->last)
+                   if (!NILP (subprefix_1->subprefix))
+                     entry = Fcons (subprefix_1->subprefix, entry);
+               }
+
+             tem = entry;
+           }
+         i += MENU_ITEMS_ITEM_LENGTH;
+       }
+    }
+
+  unblock_input ();
+  return unbind_to (count, tem);
+
+ finish:
+  unblock_input ();
+  return unbind_to (count, Qnil);
+}
+
+
+
+/* Toolkit dialog implementation.  */
+
+/* Structure describing the EmacsDialog class.  */
+
+struct android_emacs_dialog
+{
+  jclass class;
+  jmethodID create_dialog;
+  jmethodID add_button;
+  jmethodID display;
+};
+
+/* Identifiers associated with the EmacsDialog class.  */
+static struct android_emacs_dialog dialog_class;
+
+static void
+android_init_emacs_dialog (void)
+{
+  jclass old;
+
+  dialog_class.class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "org/gnu/emacs/EmacsDialog");
+  eassert (dialog_class.class);
+
+  old = dialog_class.class;
+  dialog_class.class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 (jobject) old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!dialog_class.class)
+    emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature)                   \
+  dialog_class.c_name                                          \
+    = (*android_java_env)->GetMethodID (android_java_env,      \
+                                       dialog_class.class,     \
+                                       name, signature);       \
+  eassert (dialog_class.c_name);
+
+#define FIND_METHOD_STATIC(c_name, name, signature)                    \
+  dialog_class.c_name                                                  \
+    = (*android_java_env)->GetStaticMethodID (android_java_env,                
\
+                                             dialog_class.class,       \
+                                             name, signature);         \
+
+  FIND_METHOD_STATIC (create_dialog, "createDialog", "(Ljava/lang/String;"
+                     "Ljava/lang/String;I)Lorg/gnu/emacs/EmacsDialog;");
+  FIND_METHOD (add_button, "addButton", "(Ljava/lang/String;IZ)V");
+  FIND_METHOD (display, "display", "()Z");
+
+#undef FIND_METHOD
+#undef FIND_METHOD_STATIC
+}
+
+static Lisp_Object
+android_dialog_show (struct frame *f, Lisp_Object title,
+                    Lisp_Object header, const char **error_name)
+{
+  specpdl_ref count;
+  jobject dialog, java_header, java_title, temp;
+  size_t i;
+  Lisp_Object item_name, enable, entry;
+  bool rc;
+  int id;
+  jmethodID method;
+  unsigned int serial;
+
+  /* Generate a unique ID for events from this dialog box.  */
+  serial = ++current_menu_serial;
+
+  if (menu_items_n_panes > 1)
+    {
+      *error_name = "Multiple panes in dialog box";
+      return Qnil;
+    }
+
+  /* Do the initial setup.  */
+  count = SPECPDL_INDEX ();
+  *error_name = NULL;
+
+  android_push_local_frame ();
+
+  /* Figure out what header to use.  */
+  java_header = (!NILP (header)
+                ? android_build_jstring ("Information")
+                : android_build_jstring ("Question"));
+
+  /* And the title.  */
+  java_title = android_build_string (title);
+
+  /* Now create the dialog.  */
+  method = dialog_class.create_dialog;
+  dialog = (*android_java_env)->CallStaticObjectMethod (android_java_env,
+                                                       dialog_class.class,
+                                                       method, java_header,
+                                                       java_title,
+                                                       (jint) serial);
+  android_exception_check ();
+
+  /* Delete now unused local references.  */
+  if (java_header)
+    ANDROID_DELETE_LOCAL_REF (java_header);
+  ANDROID_DELETE_LOCAL_REF (java_title);
+
+  /* Create the buttons.  */
+  i = MENU_ITEMS_PANE_LENGTH;
+  while (i < menu_items_used)
+    {
+      item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+      enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+
+      /* Verify that there is no submenu here.  */
+
+      if (NILP (item_name))
+       {
+         *error_name = "Submenu in dialog items";
+         return unbind_to (count, Qnil);
+       }
+
+      /* Skip past boundaries between buttons on different sides.  The
+        Android toolkit is too silly to understand this
+        distinction.  */
+
+      if (EQ (item_name, Qquote))
+       ++i;
+      else
+       {
+         /* Make sure i is within bounds.  */
+         if (i > TYPE_MAXIMUM (jint))
+           {
+             *error_name = "Dialog box too big";
+             return unbind_to (count, Qnil);
+           }
+
+         /* Add the button.  */
+         temp = android_build_string (item_name);
+         (*android_java_env)->CallVoidMethod (android_java_env,
+                                              dialog,
+                                              dialog_class.add_button,
+                                              temp, (jint) i,
+                                              (jboolean) NILP (enable));
+         android_exception_check ();
+         ANDROID_DELETE_LOCAL_REF (temp);
+         i += MENU_ITEMS_ITEM_LENGTH;
+       }
+    }
+
+  /* The dialog is now built.  Run it.  */
+  rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+                                              dialog,
+                                              dialog_class.display);
+  android_exception_check ();
+
+  if (!rc)
+    quit ();
+
+  /* Wait for the menu ID to arrive.  */
+  android_process_events_for_menu (&id);
+
+  if (!id)
+    quit ();
+
+  /* Find the selected item, and its pane, to return
+     the proper value.  */
+  i = 0;
+  while (i < menu_items_used)
+    {
+      if (EQ (AREF (menu_items, i), Qt))
+       i += MENU_ITEMS_PANE_LENGTH;
+      else if (EQ (AREF (menu_items, i), Qquote))
+       /* This is the boundary between left-side elts and right-side
+          elts.  */
+       ++i;
+      else
+       {
+         entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
+
+         if (id == i)
+           return entry;
+
+         i += MENU_ITEMS_ITEM_LENGTH;
+       }
+    }
+
+  return Qnil;
+}
+
+Lisp_Object
+android_popup_dialog (struct frame *f, Lisp_Object header,
+                     Lisp_Object contents)
+{
+  Lisp_Object title;
+  const char *error_name;
+  Lisp_Object selection;
+  specpdl_ref specpdl_count = SPECPDL_INDEX ();
+
+  check_window_system (f);
+
+  /* Decode the dialog items from what was specified.  */
+  title = Fcar (contents);
+  CHECK_STRING (title);
+  record_unwind_protect_void (unuse_menu_items);
+
+  if (NILP (Fcar (Fcdr (contents))))
+    /* No buttons specified, add an "Ok" button so users can pop down
+       the dialog.  */
+    contents = list2 (title, Fcons (build_string ("Ok"), Qt));
+
+  list_of_panes (list1 (contents));
+
+  /* Display them in a dialog box.  */
+  block_input ();
+  selection = android_dialog_show (f, title, header, &error_name);
+  unblock_input ();
+
+  unbind_to (specpdl_count, Qnil);
+  discard_menu_items ();
+
+  if (error_name)
+    error ("%s", error_name);
+
+  return selection;
+}
+
+#else
+
+int
+popup_activated (void)
+{
+  return 0;
+}
+
+#endif
+
+DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p,
+       Smenu_or_popup_active_p, 0, 0, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (void)
+{
+  return (popup_activated ()) ? Qt : Qnil;
+}
+
+void
+init_androidmenu (void)
+{
+#ifndef ANDROID_STUBIFY
+  android_init_emacs_context_menu ();
+  android_init_emacs_dialog ();
+#endif
+}
+
+void
+syms_of_androidmenu (void)
+{
+  defsubr (&Smenu_or_popup_active_p);
+}
diff --git a/src/androidselect.c b/src/androidselect.c
new file mode 100644
index 00000000000..9910e7921de
--- /dev/null
+++ b/src/androidselect.c
@@ -0,0 +1,506 @@
+/* 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 <minmax.h>
+#include <unistd.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 and MIME data 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; for this reason, Emacs does not
+   support setting the clipboard contents to anything other than plain
+   text.  */
+
+
+
+/* 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;
+  jmethodID get_clipboard_targets;
+  jmethodID get_clipboard_data;
+};
+
+/* 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");
+  FIND_METHOD (get_clipboard_targets, "getClipboardTargets",
+              "()[[B");
+  FIND_METHOD (get_clipboard_data, "getClipboardData",
+              "([B)[J");
+
+  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;
+
+  if (!android_init_gui)
+    error ("Accessing clipboard without display connection");
+
+  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;
+
+  if (!android_init_gui)
+    error ("Accessing clipboard without display connection");
+
+  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_1 (bytes);
+
+  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;
+
+  if (!android_init_gui)
+    error ("No Android display connection!");
+
+  method = clipboard_class.get_clipboard;
+  bytes
+    = (*android_java_env)->CallObjectMethod (android_java_env,
+                                            clipboard,
+                                            method);
+  android_exception_check ();
+
+  if (!bytes)
+    return Qnil;
+
+  length = (*android_java_env)->GetArrayLength (android_java_env,
+                                               bytes);
+  data = (*android_java_env)->GetByteArrayElements (android_java_env,
+                                                   bytes, NULL);
+  android_exception_check_nonnull (data, bytes);
+
+  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, false);
+}
+
+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;
+
+  if (!android_init_gui)
+    error ("No Android display connection");
+
+  method = clipboard_class.clipboard_exists;
+  rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+                                              clipboard,
+                                              method);
+  android_exception_check ();
+
+  return rc ? Qt : Qnil;
+}
+
+DEFUN ("android-browse-url", Fandroid_browse_url,
+       Sandroid_browse_url, 1, 2, 0,
+       doc: /* Open URL in an external application.  URL should be a
+URL-encoded URL with a scheme specified unless SEND is non-nil.
+Signal an error upon failure.
+
+If SEND is nil, start a program that is able to display the URL, such
+as a web browser.  Otherwise, try to share URL using programs such as
+email clients.  */)
+  (Lisp_Object url, Lisp_Object send)
+{
+  Lisp_Object value;
+
+  if (!android_init_gui)
+    error ("No Android display connection!");
+
+  CHECK_STRING (url);
+  value = android_browse_url (url, send);
+
+  /* Signal an error upon failure.  */
+  if (!NILP (value))
+    signal_error ("Error browsing URL", value);
+
+  return Qnil;
+}
+
+
+
+/* MIME clipboard support.  This provides support for reading MIME
+   data (but not text) from the clipboard.  */
+
+DEFUN ("android-get-clipboard-targets", Fandroid_get_clipboard_targets,
+       Sandroid_get_clipboard_targets, 0, 0, 0,
+       doc: /* Return a list of data types in the clipboard.
+Value is a list of MIME types as strings, each defining a single extra
+data type available from the clipboard.  */)
+  (void)
+{
+  jarray bytes_array;
+  jbyteArray bytes;
+  jmethodID method;
+  size_t length, length1, i;
+  jbyte *data;
+  Lisp_Object targets, tem;
+
+  if (!android_init_gui)
+    error ("No Android display connection!");
+
+  targets = Qnil;
+  block_input ();
+  method = clipboard_class.get_clipboard_targets;
+  bytes_array = (*android_java_env)->CallObjectMethod (android_java_env,
+                                                      clipboard, method);
+  android_exception_check ();
+
+  if (!bytes_array)
+    goto fail;
+
+  length = (*android_java_env)->GetArrayLength (android_java_env,
+                                               bytes_array);
+  for (i = 0; i < length; ++i)
+    {
+      /* Retireve the MIME type.  */
+      bytes
+       = (*android_java_env)->GetObjectArrayElement (android_java_env,
+                                                     bytes_array, i);
+      android_exception_check_nonnull (bytes, bytes_array);
+
+      /* Cons it onto the list of targets.  */
+      length1 = (*android_java_env)->GetArrayLength (android_java_env,
+                                                    bytes);
+      data = (*android_java_env)->GetByteArrayElements (android_java_env,
+                                                       bytes, NULL);
+      android_exception_check_nonnull_1 (data, bytes, bytes_array);
+
+      /* Decode the string.  */
+      tem = make_unibyte_string ((char *) data, length1);
+      tem = code_convert_string_norecord (tem, Qutf_8, false);
+      targets = Fcons (tem, targets);
+
+      /* Delete the retrieved data.  */
+      (*android_java_env)->ReleaseByteArrayElements (android_java_env,
+                                                    bytes, data,
+                                                    JNI_ABORT);
+      ANDROID_DELETE_LOCAL_REF (bytes);
+    }
+  unblock_input ();
+
+  ANDROID_DELETE_LOCAL_REF (bytes_array);
+  return Fnreverse (targets);
+
+ fail:
+  unblock_input ();
+  return Qnil;
+}
+
+/* Free the memory inside PTR, a pointer to a char pointer.  */
+
+static void
+android_xfree_inside (void *ptr)
+{
+  xfree (*(char **) ptr);
+}
+
+DEFUN ("android-get-clipboard-data", Fandroid_get_clipboard_data,
+       Sandroid_get_clipboard_data, 1, 1, 0,
+       doc: /* Return the clipboard data of the given MIME TYPE.
+Value is a unibyte string containing the entire contents of the
+clipboard, after its owner has converted the data to the given
+MIME type.  Value is nil if the conversion fails, or if the data
+is not present.
+
+Value is also nil if the clipboard data consists of a single URL which
+does not have any corresponding data.  In that case, use
+`android-get-clipboard' instead.  */)
+  (Lisp_Object type)
+{
+  jlongArray array;
+  jbyteArray bytes;
+  jmethodID method;
+  int fd;
+  ptrdiff_t rc;
+  jlong offset, length, *longs;
+  specpdl_ref ref;
+  char *buffer, *start;
+
+  if (!android_init_gui)
+    error ("No Android display connection!");
+
+  /* Encode the string as UTF-8.  */
+  CHECK_STRING (type);
+  type = ENCODE_UTF_8 (type);
+
+  /* Then give it to the selection code.  */
+  block_input ();
+  bytes = (*android_java_env)->NewByteArray (android_java_env,
+                                            SBYTES (type));
+  (*android_java_env)->SetByteArrayRegion (android_java_env, bytes,
+                                          0, SBYTES (type),
+                                          (jbyte *) SDATA (type));
+  android_exception_check ();
+
+  method = clipboard_class.get_clipboard_data;
+  array = (*android_java_env)->CallObjectMethod (android_java_env,
+                                                clipboard, method,
+                                                bytes);
+  android_exception_check_1 (bytes);
+  ANDROID_DELETE_LOCAL_REF (bytes);
+
+  if (!array)
+    goto fail;
+
+  longs = (*android_java_env)->GetLongArrayElements (android_java_env,
+                                                    array, NULL);
+  android_exception_check_nonnull (longs, array);
+
+  /* longs[0] is the file descriptor.
+     longs[1] is an offset to apply to the file.
+     longs[2] is either -1, or the number of bytes to read from the
+     file.  */
+  fd = longs[0];
+  offset = longs[1];
+  length = longs[2];
+
+  (*android_java_env)->ReleaseLongArrayElements (android_java_env,
+                                                array, longs,
+                                                JNI_ABORT);
+  ANDROID_DELETE_LOCAL_REF (array);
+  unblock_input ();
+
+  /* Now begin reading from longs[0].  */
+  ref = SPECPDL_INDEX ();
+  record_unwind_protect_int (close_file_unwind, fd);
+
+  if (length != -1)
+    {
+      buffer = xmalloc (MIN (length, PTRDIFF_MAX));
+      record_unwind_protect_ptr (xfree, buffer);
+
+      rc = emacs_read_quit (fd, buffer,
+                           MIN (length, PTRDIFF_MAX));
+
+      /* Return nil upon an IO problem.  */
+      if (rc < 0)
+       return unbind_to (ref, Qnil);
+
+      /* Return the data as a unibyte string.  */
+      return unbind_to (ref, make_unibyte_string (buffer, rc));
+    }
+
+  /* Otherwise, read BUFSIZ bytes at a time.  */
+  buffer = xmalloc (BUFSIZ);
+  length = 0;
+  start = buffer;
+
+  record_unwind_protect_ptr (android_xfree_inside, &buffer);
+
+  /* Seek to the start of the data.  */
+
+  if (offset)
+    {
+      if (lseek (fd, offset, SEEK_SET) < 0)
+       return unbind_to (ref, Qnil);
+    }
+
+  while (true)
+    {
+      rc = emacs_read_quit (fd, start, BUFSIZ);
+
+      if (!INT_ADD_OK (rc, length, &length)
+         || PTRDIFF_MAX - length < BUFSIZ)
+       memory_full (PTRDIFF_MAX);
+
+      if (rc < 0)
+       return unbind_to (ref, Qnil);
+
+      if (rc < BUFSIZ)
+       break;
+
+      buffer = xrealloc (buffer, length + BUFSIZ);
+      start = buffer + length;
+    }
+
+  return unbind_to (ref, make_unibyte_string (buffer, rc));
+
+ fail:
+  unblock_input ();
+  return Qnil;
+}
+
+
+
+void
+init_androidselect (void)
+{
+  jobject tem;
+  jmethodID make_clipboard;
+
+  if (!android_init_gui)
+    return;
+
+  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);
+  defsubr (&Sandroid_browse_url);
+  defsubr (&Sandroid_get_clipboard_targets);
+  defsubr (&Sandroid_get_clipboard_data);
+}
diff --git a/src/androidterm.c b/src/androidterm.c
new file mode 100644
index 00000000000..f74463f88cd
--- /dev/null
+++ b/src/androidterm.c
@@ -0,0 +1,6523 @@
+/* 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 <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <semaphore.h>
+
+#include "lisp.h"
+#include "androidterm.h"
+#include "keyboard.h"
+#include "blockinput.h"
+#include "android.h"
+#include "buffer.h"
+#include "window.h"
+#include "textconv.h"
+#include "coding.h"
+#include "pdumper.h"
+
+/* This is a chain of structures for all the X displays currently in
+   use.  */
+
+struct android_display_info *x_display_list;
+
+
+
+/* Android terminal interface functions.  */
+
+#ifndef ANDROID_STUBIFY
+
+#include <android/log.h>
+
+/* Non-zero means that a HELP_EVENT has been generated since Emacs
+   start.  */
+
+static bool any_help_event_p;
+
+/* Counters for tallying up scroll wheel events if
+   mwheel_coalesce_scroll_events is true.  */
+
+static double wheel_event_x, wheel_event_y;
+
+enum
+  {
+    ANDROID_EVENT_NORMAL,
+    ANDROID_EVENT_GOTO_OUT,
+    ANDROID_EVENT_DROP,
+  };
+
+/* Find the frame whose window has the identifier WDESC.
+
+   This is like x_window_to_frame in xterm.c, except that DPYINFO may
+   be NULL, as there is only at most one Android display, and is only
+   specified in order to stay consistent with X.  */
+
+static struct frame *
+android_window_to_frame (struct android_display_info *dpyinfo,
+                        android_window wdesc)
+{
+  Lisp_Object tail, frame;
+  struct frame *f;
+
+  if (wdesc == ANDROID_NONE)
+    return NULL;
+
+  FOR_EACH_FRAME (tail, frame)
+    {
+      f = XFRAME (frame);
+
+      if (!FRAME_ANDROID_P (f))
+       continue;
+
+      if (FRAME_ANDROID_WINDOW (f) == wdesc)
+        return f;
+    }
+
+  return NULL;
+}
+
+static void
+android_clear_frame (struct frame *f)
+{
+  /* Clearing the frame will erase any cursor, so mark them all as no
+     longer visible.  */
+  mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
+  android_clear_window (FRAME_ANDROID_DRAWABLE (f));
+}
+
+static void
+android_show_hourglass (struct frame *f)
+{
+  struct android_output *x;
+
+  /* This isn't implemented like X because a window brings alongside
+     too many unneeded resources.  */
+
+  x = FRAME_ANDROID_OUTPUT (f);
+
+  /* If the hourglass window is mapped inside a popup menu, input
+     could be lost if the menu is popped down and the grab is
+     relinquished, but the hourglass window is still up.  Just
+     avoid displaying the hourglass at all while popups are
+     active.  */
+
+  if (popup_activated ())
+    return;
+
+  x->hourglass = true;
+
+  if (!f->pointer_invisible)
+    android_define_cursor (FRAME_ANDROID_WINDOW (f),
+                          x->hourglass_cursor);
+}
+
+static void
+android_hide_hourglass (struct frame *f)
+{
+  struct android_output *x;
+
+  x = FRAME_ANDROID_OUTPUT (f);
+  x->hourglass = false;
+
+  if (!f->pointer_invisible)
+    android_define_cursor (FRAME_ANDROID_WINDOW (f),
+                          x->current_cursor);
+}
+
+static void
+android_flash (struct frame *f)
+{
+  struct android_gc *gc;
+  struct android_gc_values values;
+  int rc;
+  fd_set fds;
+
+  block_input ();
+
+  values.function = ANDROID_GC_XOR;
+  values.foreground = (FRAME_FOREGROUND_PIXEL (f)
+                      ^ FRAME_BACKGROUND_PIXEL (f));
+
+  gc = android_create_gc ((ANDROID_GC_FUNCTION
+                          | ANDROID_GC_FOREGROUND),
+                         &values);
+
+  /* Get the height not including a menu bar widget.  */
+  int height = FRAME_PIXEL_HEIGHT (f);
+  /* Height of each line to flash.  */
+  int flash_height = FRAME_LINE_HEIGHT (f);
+  /* These will be the left and right margins of the rectangles.  */
+  int flash_left = FRAME_INTERNAL_BORDER_WIDTH (f);
+  int flash_right = FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f);
+  int width = flash_right - flash_left;
+
+  /* If window is tall, flash top and bottom line.  */
+  if (height > 3 * FRAME_LINE_HEIGHT (f))
+    {
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+                             flash_left,
+                             (FRAME_INTERNAL_BORDER_WIDTH (f)
+                              + FRAME_TOP_MARGIN_HEIGHT (f)),
+                             width, flash_height);
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+                             flash_left,
+                             (height - flash_height
+                              - FRAME_INTERNAL_BORDER_WIDTH (f)
+                              - FRAME_BOTTOM_MARGIN_HEIGHT (f)),
+                             width, flash_height);
+
+    }
+  else
+    /* If it is short, flash it all.  */
+    android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+                           flash_left, FRAME_INTERNAL_BORDER_WIDTH (f),
+                           width, (height - 2
+                                   * FRAME_INTERNAL_BORDER_WIDTH (f)));
+
+  flush_frame (f);
+
+  struct timespec delay = make_timespec (0, 150 * 1000 * 1000);
+  struct timespec wakeup = timespec_add (current_timespec (), delay);
+
+  /* Keep waiting until past the time wakeup or any input gets
+     available.  */
+  while (! detect_input_pending ())
+    {
+      struct timespec current = current_timespec ();
+      struct timespec timeout;
+
+      /* Break if result would not be positive.  */
+      if (timespec_cmp (wakeup, current) <= 0)
+       break;
+
+      /* How long `select' should wait.  */
+      timeout = make_timespec (0, 10 * 1000 * 1000);
+
+      /* Wait for some input to become available on the X
+        connection.  */
+      FD_ZERO (&fds);
+
+      /* Try to wait that long--but we might wake up sooner.  */
+      rc = pselect (0, &fds, NULL, NULL, &timeout, NULL);
+
+      /* Some input is available, exit the visible bell.  */
+      if (rc >= 0)
+       break;
+    }
+
+  /* If window is tall, flash top and bottom line.  */
+  if (height > 3 * FRAME_LINE_HEIGHT (f))
+    {
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+                             flash_left,
+                             (FRAME_INTERNAL_BORDER_WIDTH (f)
+                              + FRAME_TOP_MARGIN_HEIGHT (f)),
+                             width, flash_height);
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+                             flash_left,
+                             (height - flash_height
+                              - FRAME_INTERNAL_BORDER_WIDTH (f)
+                              - FRAME_BOTTOM_MARGIN_HEIGHT (f)),
+                             width, flash_height);
+    }
+  else
+    /* If it is short, flash it all.  */
+    android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+                           flash_left, FRAME_INTERNAL_BORDER_WIDTH (f),
+                           width, (height - 2
+                                   * FRAME_INTERNAL_BORDER_WIDTH (f)));
+
+  android_free_gc (gc);
+  flush_frame (f);
+
+  unblock_input ();
+}
+
+static void
+android_ring_bell (struct frame *f)
+{
+  if (visible_bell)
+    android_flash (f);
+  else
+    {
+      block_input ();
+      android_bell ();
+      unblock_input ();
+    }
+}
+
+static android_cursor
+make_invisible_cursor (struct android_display_info *dpyinfo)
+{
+  return android_create_font_cursor (ANDROID_XC_NULL);
+}
+
+static void
+android_toggle_visible_pointer (struct frame *f, bool invisible)
+{
+  struct android_display_info *dpyinfo;
+
+  dpyinfo = FRAME_DISPLAY_INFO (f);
+
+  if (!dpyinfo->invisible_cursor)
+    dpyinfo->invisible_cursor = make_invisible_cursor (dpyinfo);
+
+  if (invisible)
+    android_define_cursor (FRAME_ANDROID_WINDOW (f),
+                          dpyinfo->invisible_cursor);
+  else
+    android_define_cursor (FRAME_ANDROID_WINDOW (f),
+                          (FRAME_ANDROID_OUTPUT (f)->hourglass
+                           ? f->output_data.android->hourglass_cursor
+                           : f->output_data.android->current_cursor));
+
+  f->pointer_invisible = invisible;
+}
+
+static void
+android_toggle_invisible_pointer (struct frame *f, bool invisible)
+{
+  block_input ();
+  android_toggle_visible_pointer (f, invisible);
+  unblock_input ();
+}
+
+/* Start an update of frame F.  This function is installed as a hook
+   for update_begin, i.e. it is called when update_begin is called.
+   This function is called prior to calls to gui_update_window_begin
+   for each window being updated.  Currently, there is nothing to do
+   here because all interesting stuff is done on a window basis.  */
+
+static void
+android_update_begin (struct frame *f)
+{
+  /* The frame is no longer complete, as it is in the midst of an
+     update.  */
+  FRAME_ANDROID_COMPLETE_P (f) = false;
+}
+
+/* End update of frame F.  This function is installed as a hook in
+   update_end.  */
+
+static void
+android_update_end (struct frame *f)
+{
+  /* Mouse highlight may be displayed again.  */
+  MOUSE_HL_INFO (f)->mouse_face_defer = false;
+}
+
+static void
+show_back_buffer (struct frame *f)
+{
+  struct android_swap_info swap_info;
+
+  memset (&swap_info, 0, sizeof (swap_info));
+  swap_info.swap_window = FRAME_ANDROID_WINDOW (f);
+  swap_info.swap_action = ANDROID_COPIED;
+  android_swap_buffers (&swap_info, 1);
+
+  /* Now the back buffer no longer needs to be flipped.  */
+  FRAME_ANDROID_NEED_BUFFER_FLIP (f) = false;
+}
+
+/* Flip back buffers on F if it has undrawn content.  */
+
+static void
+android_flush_dirty_back_buffer_on (struct frame *f)
+{
+  if (FRAME_GARBAGED_P (f)
+      || buffer_flipping_blocked_p ()
+      /* If the frame is not already up to date, do not flush buffers
+        on input, as that will result in flicker.  */
+      || !FRAME_ANDROID_COMPLETE_P (f)
+      || !FRAME_ANDROID_NEED_BUFFER_FLIP (f))
+    return;
+
+  show_back_buffer (f);
+}
+
+/* Convert between the modifier bits Android uses and the modifier
+   bits Emacs uses.  */
+
+static int
+android_android_to_emacs_modifiers (struct android_display_info *dpyinfo,
+                                   int state)
+{
+  return (((state & ANDROID_CONTROL_MASK) ? ctrl_modifier  : 0)
+         | ((state & ANDROID_SHIFT_MASK) ? shift_modifier : 0)
+         | ((state & ANDROID_ALT_MASK)   ? meta_modifier  : 0)
+         | ((state & ANDROID_SUPER_MASK) ? super_modifier : 0)
+         | ((state & ANDROID_META_MASK)  ? alt_modifier   : 0));
+}
+
+static int
+android_emacs_to_android_modifiers (struct android_display_info *dpyinfo,
+                                   intmax_t state)
+{
+  return (((state & ctrl_modifier)    ? ANDROID_CONTROL_MASK : 0)
+         | ((state & shift_modifier) ? ANDROID_SHIFT_MASK   : 0)
+         | ((state & meta_modifier)  ? ANDROID_ALT_MASK     : 0)
+         | ((state & super_modifier) ? ANDROID_SUPER_MASK   : 0)
+         | ((state & alt_modifier)   ? ANDROID_META_MASK    : 0));
+}
+
+static void android_frame_rehighlight (struct android_display_info *);
+
+static void
+android_lower_frame (struct frame *f)
+{
+  android_lower_window (FRAME_ANDROID_WINDOW (f));
+}
+
+static void
+android_raise_frame (struct frame *f)
+{
+  android_raise_window (FRAME_ANDROID_WINDOW (f));
+}
+
+static void
+android_new_focus_frame (struct android_display_info *dpyinfo,
+                        struct frame *frame)
+{
+  struct frame *old_focus;
+
+  old_focus = dpyinfo->focus_frame;
+
+  if (frame != dpyinfo->focus_frame)
+    {
+      /* Set this before calling other routines, so that they see
+        the correct value of x_focus_frame.  */
+      dpyinfo->focus_frame = frame;
+
+      if (old_focus && old_focus->auto_lower)
+       android_lower_frame (old_focus);
+
+      if (dpyinfo->focus_frame && dpyinfo->focus_frame->auto_raise)
+       dpyinfo->pending_autoraise_frame = dpyinfo->focus_frame;
+      else
+       dpyinfo->pending_autoraise_frame = NULL;
+    }
+
+  android_frame_rehighlight (dpyinfo);
+}
+
+static void
+android_focus_changed (int type, int state,
+                      struct android_display_info *dpyinfo,
+                      struct frame *frame, struct input_event *bufp)
+{
+  if (type == ANDROID_FOCUS_IN)
+    {
+      if (dpyinfo->x_focus_event_frame != frame)
+        {
+          android_new_focus_frame (dpyinfo, frame);
+          dpyinfo->x_focus_event_frame = frame;
+          bufp->kind = FOCUS_IN_EVENT;
+          XSETFRAME (bufp->frame_or_window, frame);
+        }
+
+      frame->output_data.android->focus_state |= state;
+    }
+  else if (type == ANDROID_FOCUS_OUT)
+    {
+      frame->output_data.android->focus_state &= ~state;
+
+      if (dpyinfo->x_focus_event_frame == frame)
+        {
+          dpyinfo->x_focus_event_frame = 0;
+          android_new_focus_frame (dpyinfo, 0);
+
+          bufp->kind = FOCUS_OUT_EVENT;
+          XSETFRAME (bufp->frame_or_window, frame);
+        }
+
+      if (frame->pointer_invisible)
+        android_toggle_invisible_pointer (frame, false);
+    }
+}
+
+static void
+android_detect_focus_change (struct android_display_info *dpyinfo,
+                            struct frame *frame,
+                            union android_event *event,
+                            struct input_event *bufp)
+{
+  if (!frame)
+    return;
+
+  switch (event->type)
+    {
+    case ANDROID_FOCUS_IN:
+    case ANDROID_FOCUS_OUT:
+      android_focus_changed (event->type, FOCUS_EXPLICIT,
+                            dpyinfo, frame, bufp);
+      break;
+
+    default:
+      break;
+    }
+}
+
+static bool
+android_note_mouse_movement (struct frame *frame,
+                            struct android_motion_event *event)
+{
+  struct android_display_info *dpyinfo;
+  Emacs_Rectangle *r;
+
+  if (!FRAME_ANDROID_OUTPUT (frame))
+    return false;
+
+  dpyinfo = FRAME_DISPLAY_INFO (frame);
+  dpyinfo->last_mouse_motion_frame = frame;
+  dpyinfo->last_mouse_motion_x = event->x;
+  dpyinfo->last_mouse_motion_y = event->y;
+  dpyinfo->last_mouse_movement_time = event->time;
+
+  /* Has the mouse moved off the glyph it was on at the last sighting?  */
+  r = &dpyinfo->last_mouse_glyph;
+  if (frame != dpyinfo->last_mouse_glyph_frame
+      || event->x < r->x || event->x >= r->x + r->width
+      || event->y < r->y || event->y >= r->y + r->height)
+    {
+      frame->mouse_moved = true;
+      note_mouse_highlight (frame, event->x, event->y);
+      /* Remember which glyph we're now on.  */
+      remember_mouse_glyph (frame, event->x, event->y, r);
+      dpyinfo->last_mouse_glyph_frame = frame;
+      return true;
+    }
+
+  return false;
+}
+
+static struct frame *
+mouse_or_wdesc_frame (struct android_display_info *dpyinfo, int wdesc)
+{
+  struct frame *lm_f = (gui_mouse_grabbed (dpyinfo)
+                       ? dpyinfo->last_mouse_frame
+                       : NULL);
+
+  if (lm_f && !EQ (track_mouse, Qdropping)
+      && !EQ (track_mouse, Qdrag_source))
+    return lm_f;
+  else
+    {
+      struct frame *w_f = android_window_to_frame (dpyinfo, wdesc);
+
+      /* Do not return a tooltip frame.  */
+      if (!w_f || FRAME_TOOLTIP_P (w_f))
+       return EQ (track_mouse, Qdropping) ? lm_f : NULL;
+      else
+       /* When dropping it would be probably nice to raise w_f
+          here.  */
+       return w_f;
+    }
+}
+
+static Lisp_Object
+android_construct_mouse_click (struct input_event *result,
+                              struct android_button_event *event,
+                              struct frame *f)
+{
+  struct android_display_info *dpyinfo;
+  int x, y;
+
+  dpyinfo = FRAME_DISPLAY_INFO (f);
+  x = event->x;
+  y = event->y;
+
+  /* Make the event type NO_EVENT; we'll change that when we decide
+     otherwise.  */
+  result->kind = MOUSE_CLICK_EVENT;
+  result->code = event->button - 1;
+  result->timestamp = event->time;
+  result->modifiers = (android_android_to_emacs_modifiers (dpyinfo,
+                                                          event->state)
+                      | (event->type == ANDROID_BUTTON_RELEASE
+                         ? up_modifier : down_modifier));
+
+  XSETINT (result->x, x);
+  XSETINT (result->y, y);
+  XSETFRAME (result->frame_or_window, f);
+  result->arg = Qnil;
+  return Qnil;
+}
+
+/* Generate a TOUCHSCREEN_UPDATE_EVENT for all pressed tools in FRAME.
+   Return the event in IE.  Do not set IE->timestamp, as that is left
+   to the caller.  */
+
+static void
+android_update_tools (struct frame *f, struct input_event *ie)
+{
+  struct android_touch_point *touchpoint;
+
+  ie->kind = TOUCHSCREEN_UPDATE_EVENT;
+  XSETFRAME (ie->frame_or_window, f);
+  ie->arg = Qnil;
+
+  /* Build the list of active touches.  */
+  for (touchpoint = FRAME_OUTPUT_DATA (f)->touch_points;
+       touchpoint; touchpoint = touchpoint->next)
+    {
+      /* Skip touch points which originated on the tool bar.  */
+
+      if (touchpoint->tool_bar_p)
+       continue;
+
+      ie->arg = Fcons (list3i (touchpoint->x,
+                              touchpoint->y,
+                              touchpoint->tool_id),
+                      ie->arg);
+    }
+}
+
+/* Find and return an existing tool pressed against FRAME, identified
+   by POINTER_ID.  Return NULL if no tool by that ID was found.  */
+
+static struct android_touch_point *
+android_find_tool (struct frame *f, int pointer_id)
+{
+  struct android_touch_point *touchpoint;
+
+  for (touchpoint = FRAME_OUTPUT_DATA (f)->touch_points;
+       touchpoint; touchpoint = touchpoint->next)
+    {
+      if (touchpoint->tool_id == pointer_id)
+       return touchpoint;
+    }
+
+  return NULL;
+}
+
+/* Decode STRING, an array of N little endian UTF-16 characters, into
+   a Lisp string.  Return Qnil if the string is too large, and the
+   encoded string otherwise.  */
+
+static Lisp_Object
+android_decode_utf16 (unsigned short *utf16, size_t n)
+{
+  struct coding_system coding;
+  ptrdiff_t size;
+
+  if (INT_MULTIPLY_WRAPV (n, sizeof *utf16, &size))
+    return Qnil;
+
+  /* Set up the coding system.  Decoding a UTF-16 string (with no BOM)
+     should not signal.  */
+
+  memset (&coding, 0, sizeof coding);
+
+  setup_coding_system (Qutf_16le, &coding);
+  coding.source = (const unsigned char *) utf16;
+  decode_coding_object (&coding, Qnil, 0, 0, size,
+                       size, Qt);
+
+  return coding.dst_object;
+}
+
+/* Handle a cursor update request for F from the input method.
+   MODE specifies whether or not an update should be sent immediately,
+   and whether or not they are needed in the future.
+
+   If MODE & ANDROID_CURSOR_UPDATE_IMMEDIATE, report the position of
+   F's old selected window's phys cursor now.
+
+   If MODE & ANDROID_CURSOR_UPDATE_MONITOR, set
+   `need_cursor_updates'.  */
+
+static void
+android_request_cursor_updates (struct frame *f, int mode)
+{
+  struct window *w;
+
+  if (mode & ANDROID_CURSOR_UPDATE_IMMEDIATE
+      && WINDOWP (WINDOW_LIVE_P (f->old_selected_window)
+                 ? f->old_selected_window
+                 : f->selected_window))
+    {
+      /* Prefer the old selected window, as its selection is what was
+        reported to the IME previously.  */
+
+      w = XWINDOW (WINDOW_LIVE_P (f->old_selected_window)
+                  ? f->old_selected_window
+                  : f->selected_window);
+      android_set_preeditarea (w, w->cursor.x, w->cursor.y);
+    }
+
+  /* Now say whether or not updates are needed in the future.  */
+  FRAME_OUTPUT_DATA (f)->need_cursor_updates
+    = (mode & ANDROID_CURSOR_UPDATE_MONITOR);
+}
+
+/* Handle a single input method event EVENT, delivered to the frame
+   F.
+
+   Perform the text conversion action specified inside.  */
+
+static void
+android_handle_ime_event (union android_event *event, struct frame *f)
+{
+  Lisp_Object text UNINIT;
+  struct android_output *output;
+
+  /* First, decode the text if necessary.  */
+
+  switch (event->ime.operation)
+    {
+    case ANDROID_IME_COMMIT_TEXT:
+    case ANDROID_IME_SET_COMPOSING_TEXT:
+      text = android_decode_utf16 (event->ime.text,
+                                  event->ime.length);
+      xfree (event->ime.text);
+      break;
+
+    default:
+      break;
+    }
+
+  /* Finally, perform the appropriate conversion action.  */
+
+  switch (event->ime.operation)
+    {
+    case ANDROID_IME_COMMIT_TEXT:
+      commit_text (f, text, event->ime.position,
+                  event->ime.counter);
+      break;
+
+    case ANDROID_IME_DELETE_SURROUNDING_TEXT:
+      delete_surrounding_text (f, event->ime.start,
+                              event->ime.end,
+                              event->ime.counter);
+      break;
+
+    case ANDROID_IME_FINISH_COMPOSING_TEXT:
+
+      if (event->ime.length == 2)
+       {
+         output = FRAME_ANDROID_OUTPUT (f);
+
+         /* A new input method has connected to Emacs.  Stop
+            reporting changes that the previous input method has
+            asked to monitor.  */
+
+         output->extracted_text_flags = 0;
+         output->extracted_text_token = 0;
+         output->extracted_text_hint = 0;
+         output->need_cursor_updates = false;
+       }
+
+      finish_composing_text (f, event->ime.counter,
+                            event->ime.length == 1);
+
+      if (event->ime.length == 2)
+       {
+         /* Now cancel outstanding batch edits if a new input method
+            has connected.  */
+
+         f->conversion.batch_edit_flags = 0;
+         f->conversion.batch_edit_count = 0;
+       }
+
+      break;
+
+    case ANDROID_IME_SET_COMPOSING_TEXT:
+      set_composing_text (f, text, event->ime.position,
+                         event->ime.counter);
+      break;
+
+    case ANDROID_IME_SET_COMPOSING_REGION:
+      set_composing_region (f, event->ime.start,
+                           event->ime.end,
+                           event->ime.counter);
+      break;
+
+    case ANDROID_IME_SET_POINT:
+      textconv_set_point_and_mark (f, event->ime.start,
+                                  event->ime.end,
+                                  event->ime.counter);
+      break;
+
+    case ANDROID_IME_START_BATCH_EDIT:
+      start_batch_edit (f, event->ime.counter);
+      break;
+
+    case ANDROID_IME_END_BATCH_EDIT:
+      end_batch_edit (f, event->ime.counter);
+      break;
+
+    case ANDROID_IME_REQUEST_SELECTION_UPDATE:
+      request_point_update (f, event->ime.counter);
+      break;
+
+    case ANDROID_IME_REQUEST_CURSOR_UPDATES:
+      android_request_cursor_updates (f, event->ime.length);
+      break;
+    }
+}
+
+
+
+/* Forward declaration.  */
+static void android_notify_conversion (unsigned long);
+
+static int
+handle_one_android_event (struct android_display_info *dpyinfo,
+                         union android_event *event, int *finish,
+                         struct input_event *hold_quit)
+{
+  union android_event configureEvent;
+  struct frame *f, *any, *mouse_frame;
+  Mouse_HLInfo *hlinfo;
+  union buffered_input_event inev;
+  int modifiers, count, do_help;
+  struct android_touch_point *touchpoint, **last;
+  Lisp_Object window;
+  int scroll_height;
+  double scroll_unit;
+  int keysym;
+  ptrdiff_t nchars, i;
+  struct window *w;
+
+  /* It is okay for this to not resemble handle_one_xevent so much.
+     Differences in event handling code are much less nasty than
+     stuble differences in the graphics code.  */
+
+  do_help = count = 0;
+  hlinfo = &dpyinfo->mouse_highlight;
+  *finish = ANDROID_EVENT_NORMAL;
+  any = android_window_to_frame (dpyinfo, event->xany.window);
+  nchars = 0;
+
+  if (any && any->wait_event_type == event->type)
+    any->wait_event_type = 0; /* Indicates we got it.  */
+
+  EVENT_INIT (inev.ie);
+
+  switch (event->type)
+    {
+    case ANDROID_CONFIGURE_NOTIFY:
+      configureEvent = *event;
+
+      f = android_window_to_frame (dpyinfo,
+                                  configureEvent.xconfigure.window);
+
+      if (!f)
+       goto OTHER;
+
+      if (FRAME_TOOLTIP_P (f))
+       {
+         if (FRAME_PIXEL_HEIGHT (f) != configureEvent.xconfigure.height
+             || FRAME_PIXEL_WIDTH (f) != configureEvent.xconfigure.width)
+           SET_FRAME_GARBAGED (f);
+
+         FRAME_PIXEL_HEIGHT (f) = configureEvent.xconfigure.height;
+         FRAME_PIXEL_WIDTH (f) = configureEvent.xconfigure.width;
+       }
+
+      int width = configureEvent.xconfigure.width;
+      int height = configureEvent.xconfigure.height;
+
+      if (CONSP (frame_size_history))
+       frame_size_history_extra (f, build_string ("ConfigureNotify"),
+                                 FRAME_PIXEL_WIDTH (f),
+                                 FRAME_PIXEL_HEIGHT (f),
+                                 width, height, f->new_width,
+                                 f->new_height);
+
+      /* Even if the number of character rows and columns has
+        not changed, the font size may have changed, so we need
+        to check the pixel dimensions as well.  */
+
+      if (width != FRAME_PIXEL_WIDTH (f)
+         || height != FRAME_PIXEL_HEIGHT (f)
+         || (f->new_size_p
+             && ((f->new_width >= 0 && width != f->new_width)
+                 || (f->new_height >= 0 && height != f->new_height))))
+       {
+         change_frame_size (f, width, height, false, true, false);
+         android_clear_under_internal_border (f);
+         SET_FRAME_GARBAGED (f);
+         cancel_mouse_face (f);
+       }
+
+      /* Now change the left and top position of this window.  */
+
+      {
+       int old_left = f->left_pos;
+       int old_top = f->top_pos;
+       Lisp_Object frame;
+
+       XSETFRAME (frame, f);
+
+       {
+         android_window root;
+         unsigned int dummy_uint;
+
+         android_get_geometry (FRAME_ANDROID_WINDOW (f),
+                               &root, &f->left_pos, &f->top_pos,
+                               &dummy_uint, &dummy_uint,
+                               &dummy_uint);
+       }
+
+       if (!FRAME_TOOLTIP_P (f)
+           && (old_left != f->left_pos || old_top != f->top_pos))
+         {
+           inev.ie.kind = MOVE_FRAME_EVENT;
+           XSETFRAME (inev.ie.frame_or_window, f);
+         }
+
+      if (f && FRAME_OUTPUT_DATA (f)->need_cursor_updates)
+       {
+         w = XWINDOW (f->selected_window);
+         android_set_preeditarea (w, w->cursor.x, w->cursor.y);
+       }
+      }
+
+      goto OTHER;
+
+    case ANDROID_KEY_PRESS:
+
+      /* Set f to any.  There are no ``outer windows'' on Android.  */
+      f = any;
+
+      /* If mouse-highlight is an integer, input clears out
+        mouse highlighting.  */
+      if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight)
+         && (any == 0
+             || !EQ (any->tool_bar_window, hlinfo->mouse_face_window)
+             || !EQ (any->tab_bar_window, hlinfo->mouse_face_window)))
+        {
+         mouse_frame = hlinfo->mouse_face_mouse_frame;
+
+         clear_mouse_face (hlinfo);
+         hlinfo->mouse_face_hidden = true;
+
+         if (mouse_frame)
+           android_flush_dirty_back_buffer_on (mouse_frame);
+       }
+
+      if (!f)
+       goto OTHER;
+
+      if (event->xkey.counter)
+       /* This event was generated by `performEditorAction'.  Make
+          sure it is processed before any subsequent edits.  */
+       textconv_barrier (f, event->xkey.counter);
+
+      wchar_t copy_buffer[129];
+      wchar_t *copy_bufptr = copy_buffer;
+      int copy_bufsiz = 128 * sizeof (wchar_t);
+
+      event->xkey.state
+       |= android_emacs_to_android_modifiers (dpyinfo,
+                                              extra_keyboard_modifiers);
+      modifiers = event->xkey.state;
+
+      /* Common for all keysym input events.  */
+      XSETFRAME (inev.ie.frame_or_window, any);
+      inev.ie.modifiers
+       = android_android_to_emacs_modifiers (dpyinfo, modifiers);
+      inev.ie.timestamp = event->xkey.time;
+
+      keysym = event->xkey.keycode;
+
+      {
+       enum android_lookup_status status_return;
+
+       nchars = android_wc_lookup_string (&event->xkey, copy_bufptr,
+                                          copy_bufsiz, &keysym,
+                                          &status_return);
+
+       /* android_lookup_string can't be called twice, so there's no
+          way to recover from buffer overflow.  */
+       if (status_return == ANDROID_BUFFER_OVERFLOW)
+         goto done_keysym;
+       else if (status_return == ANDROID_LOOKUP_NONE)
+         {
+           /* Don't skip preedit text events.  */
+           if (event->xkey.keycode != (uint32_t) -1)
+             goto done_keysym;
+         }
+       else if (status_return == ANDROID_LOOKUP_CHARS)
+         keysym = ANDROID_NO_SYMBOL;
+       else if (status_return != ANDROID_LOOKUP_KEYSYM
+                && status_return != ANDROID_LOOKUP_BOTH)
+         emacs_abort ();
+
+       /* Deal with pre-edit text events.  On Android, these are
+          simply encoded as events with associated strings and a
+          keycode set to ``-1''.  */
+
+       if (event->xkey.keycode == (uint32_t) -1)
+         {
+           inev.ie.kind = PREEDIT_TEXT_EVENT;
+           inev.ie.arg = Qnil;
+
+           /* If text was looked up, decode it and make it the
+              preedit text.  */
+
+           if (status_return == ANDROID_LOOKUP_CHARS && nchars)
+             {
+               copy_bufptr[nchars] = 0;
+               inev.ie.arg = from_unicode_buffer (copy_bufptr);
+             }
+
+           goto done_keysym;
+         }
+      }
+
+      if (nchars == 1 && copy_bufptr[0] >= 32)
+       {
+         /* Deal with characters.  */
+
+         if (copy_bufptr[0] < 128)
+           inev.ie.kind = ASCII_KEYSTROKE_EVENT;
+         else
+           inev.ie.kind = MULTIBYTE_CHAR_KEYSTROKE_EVENT;
+
+         inev.ie.code = copy_bufptr[0];
+       }
+      else if (nchars < 2 && keysym)
+       {
+         /* If the key is a modifier key, just return.  */
+         if (ANDROID_IS_MODIFIER_KEY (keysym))
+           goto done_keysym;
+
+         /* Next, deal with special ``characters'' by giving the
+            keycode to keyboard.c.  */
+         inev.ie.kind = NON_ASCII_KEYSTROKE_EVENT;
+         inev.ie.code = keysym;
+       }
+      else
+       {
+         /* Finally, deal with strings.  */
+
+         for (i = 0; i < nchars; ++i)
+           {
+             inev.ie.kind = (SINGLE_BYTE_CHAR_P (copy_bufptr[i])
+                             ? ASCII_KEYSTROKE_EVENT
+                             : MULTIBYTE_CHAR_KEYSTROKE_EVENT);
+             inev.ie.code = copy_bufptr[i];
+
+             /* If the character is actually '\n', then change this
+                to RET.  */
+
+             if (copy_bufptr[i] == '\n')
+               {
+                 inev.ie.kind = NON_ASCII_KEYSTROKE_EVENT;
+                 inev.ie.code = 66;
+               }
+
+             kbd_buffer_store_buffered_event (&inev, hold_quit);
+           }
+
+         count += nchars;
+         inev.ie.kind = NO_EVENT;  /* Already stored above.  */
+       }
+
+      goto done_keysym;
+
+    done_keysym:
+
+      /* Now proceed to tell the input method the current position of
+        the cursor, if required.  */
+
+      if (f && FRAME_OUTPUT_DATA (f)->need_cursor_updates)
+       {
+         w = XWINDOW (f->selected_window);
+         android_set_preeditarea (w, w->cursor.x, w->cursor.y);
+       }
+
+      goto OTHER;
+
+    case ANDROID_FOCUS_IN:
+    case ANDROID_FOCUS_OUT:
+      android_detect_focus_change (dpyinfo, any, event, &inev.ie);
+      goto OTHER;
+
+    case ANDROID_WINDOW_ACTION:
+
+      /* This is a special event sent by android_run_in_emacs_thread
+        used to make Android run stuff.  */
+
+      if (!event->xaction.window && !event->xaction.action)
+       /* Don't run queries here, as it may run inside editor
+          commands, which can expose an inconsistent view of buffer
+          contents to the input method during command execution.
+
+          Instead, wait for Emacs to return to `android_select'.  */
+       goto OTHER;
+
+      f = any;
+
+      if (event->xaction.action == 0)
+       {
+         /* Action 0 either means that a window has been destroyed
+            and its associated frame should be as well.  */
+
+         if (event->xaction.window)
+           {
+             if (!f)
+               goto OTHER;
+
+             inev.ie.kind = DELETE_WINDOW_EVENT;
+             XSETFRAME (inev.ie.frame_or_window, f);
+           }
+       }
+
+    case ANDROID_ENTER_NOTIFY:
+      f = any;
+
+      if (f)
+       android_note_mouse_movement (f, &event->xmotion);
+      goto OTHER;
+
+    case ANDROID_MOTION_NOTIFY:
+
+      previous_help_echo_string = help_echo_string;
+      help_echo_string = Qnil;
+
+      if (hlinfo->mouse_face_hidden)
+       {
+         hlinfo->mouse_face_hidden = false;
+         clear_mouse_face (hlinfo);
+       }
+
+      f = any;
+
+      if (f)
+       {
+         /* Maybe generate a SELECT_WINDOW_EVENT for
+            `mouse-autoselect-window' but don't let popup menus
+            interfere with this (Bug#1261).  */
+         if (!NILP (Vmouse_autoselect_window)
+             && !popup_activated ()
+             /* Don't switch if we're currently in the minibuffer.
+                This tries to work around problems where the
+                minibuffer gets unselected unexpectedly, and where
+                you then have to move your mouse all the way down to
+                the minibuffer to select it.  */
+             && !MINI_WINDOW_P (XWINDOW (selected_window))
+             /* With `focus-follows-mouse' non-nil create an event
+                also when the target window is on another frame.  */
+             && (f == XFRAME (selected_frame)
+                 || !NILP (focus_follows_mouse)))
+           {
+             static Lisp_Object last_mouse_window;
+             Lisp_Object window
+               = window_from_coordinates (f, event->xmotion.x,
+                                          event->xmotion.y, 0,
+                                          false, false);
+
+             /* A window will be autoselected only when it is not
+                selected now and the last mouse movement event was
+                not in it.  The remainder of the code is a bit vague
+                wrt what a "window" is.  For immediate autoselection,
+                the window is usually the entire window but for GTK
+                where the scroll bars don't count.  For delayed
+                autoselection the window is usually the window's text
+                area including the margins.  */
+             if (WINDOWP (window)
+                 && !EQ (window, last_mouse_window)
+                 && !EQ (window, selected_window))
+               {
+                 inev.ie.kind = SELECT_WINDOW_EVENT;
+                 inev.ie.frame_or_window = window;
+               }
+
+             /* Remember the last window where we saw the mouse.  */
+             last_mouse_window = window;
+           }
+
+         if (!android_note_mouse_movement (f, &event->xmotion))
+           help_echo_string = previous_help_echo_string;
+       }
+
+      /* If the contents of the global variable help_echo_string
+        has changed, generate a HELP_EVENT.  */
+      if (!NILP (help_echo_string)
+         || !NILP (previous_help_echo_string))
+       do_help = 1;
+
+      if (f)
+       android_flush_dirty_back_buffer_on (f);
+
+      goto OTHER;
+
+    case ANDROID_LEAVE_NOTIFY:
+      f = any;
+
+      if (f)
+        {
+         /* Now clear dpyinfo->last_mouse_motion_frame, or
+            gui_redo_mouse_highlight will end up highlighting the
+            last known position of the mouse if a tooltip frame is
+            later unmapped.  */
+
+         if (f == dpyinfo->last_mouse_motion_frame)
+           dpyinfo->last_mouse_motion_frame = NULL;
+
+         /* Something similar applies to
+            dpyinfo->last_mouse_glyph_frame.  */
+         if (f == dpyinfo->last_mouse_glyph_frame)
+           dpyinfo->last_mouse_glyph_frame = NULL;
+
+          if (f == hlinfo->mouse_face_mouse_frame)
+            {
+              /* If we move outside the frame, then we're
+                 certainly no longer on any text in the frame.  */
+              clear_mouse_face (hlinfo);
+              hlinfo->mouse_face_mouse_frame = 0;
+             android_flush_dirty_back_buffer_on (f);
+            }
+
+          /* Generate a nil HELP_EVENT to cancel a help-echo.
+             Do it only if there's something to cancel.
+             Otherwise, the startup message is cleared when
+             the mouse leaves the frame.  */
+          if (any_help_event_p
+             /* But never if `mouse-drag-and-drop-region' is in
+                progress, since that results in the tooltip being
+                dismissed when the mouse moves on top.  */
+             && !((EQ (track_mouse, Qdrag_source)
+                   || EQ (track_mouse, Qdropping))
+                  && gui_mouse_grabbed (dpyinfo)))
+           do_help = -1;
+        }
+
+      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
+        by the rest of Emacs, we put it here.  */
+
+      f = any;
+
+      Lisp_Object tab_bar_arg = Qnil;
+      bool tab_bar_p = false;
+      bool tool_bar_p = false;
+
+      dpyinfo->last_mouse_glyph_frame = NULL;
+
+      f = mouse_or_wdesc_frame (dpyinfo, event->xbutton.window);
+
+      if (f && event->xbutton.type == ANDROID_BUTTON_PRESS
+         && !popup_activated ()
+         /* && !x_window_to_scroll_bar (event->xbutton.display, */
+         /*                          event->xbutton.window, 2) */
+         && !FRAME_NO_ACCEPT_FOCUS (f))
+       {
+         /* When clicking into a child frame or when clicking
+            into a parent frame with the child frame selected and
+            `no-accept-focus' is not set, select the clicked
+            frame.  */
+         struct frame *hf = dpyinfo->highlight_frame;
+
+         if (FRAME_PARENT_FRAME (f) || (hf && frame_ancestor_p (f, hf)))
+           {
+             android_set_input_focus (FRAME_ANDROID_WINDOW (f),
+                                      event->xbutton.time);
+
+             if (FRAME_PARENT_FRAME (f))
+               android_raise_window (FRAME_ANDROID_WINDOW (f));
+           }
+       }
+
+      if (f)
+       {
+         /* Is this in the tab-bar?  */
+         if (WINDOWP (f->tab_bar_window)
+             && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)))
+           {
+             Lisp_Object window;
+             int x = event->xbutton.x;
+             int y = event->xbutton.y;
+
+             window = window_from_coordinates (f, x, y, 0, true, true);
+             tab_bar_p = EQ (window, f->tab_bar_window);
+
+             if (tab_bar_p)
+               {
+                 tab_bar_arg = handle_tab_bar_click
+                   (f, x, y, (event->xbutton.type
+                              == ANDROID_BUTTON_PRESS),
+                    android_android_to_emacs_modifiers (dpyinfo,
+                                                        event->xbutton.state));
+                 android_flush_dirty_back_buffer_on (f);
+               }
+           }
+
+         /* Is this in the tool-bar?  */
+         if (WINDOWP (f->tool_bar_window)
+             && WINDOW_TOTAL_LINES (XWINDOW (f->tool_bar_window)))
+           {
+             Lisp_Object window;
+             int x = event->xbutton.x;
+             int y = event->xbutton.y;
+
+             window = window_from_coordinates (f, x, y, 0, true, true);
+             tool_bar_p = (EQ (window, f->tool_bar_window)
+                           && ((event->xbutton.type
+                                != ANDROID_BUTTON_RELEASE)
+                               || f->last_tool_bar_item != -1));
+
+             if (tool_bar_p && event->xbutton.button < 4)
+               {
+                 handle_tool_bar_click
+                   (f, x, y, (event->xbutton.type
+                              == ANDROID_BUTTON_PRESS),
+                    android_android_to_emacs_modifiers (dpyinfo,
+                                                        event->xbutton.state));
+                 android_flush_dirty_back_buffer_on (f);
+               }
+           }
+
+         if (!(tab_bar_p && NILP (tab_bar_arg)) && !tool_bar_p)
+           if (! popup_activated ())
+             {
+               android_construct_mouse_click (&inev.ie, &event->xbutton, f);
+
+               if (!NILP (tab_bar_arg))
+                 inev.ie.arg = tab_bar_arg;
+             }
+       }
+
+      if (event->type == ANDROID_BUTTON_PRESS)
+       {
+         dpyinfo->grabbed |= (1 << event->xbutton.button);
+         dpyinfo->last_mouse_frame = f;
+         if (f && !tab_bar_p)
+           f->last_tab_bar_item = -1;
+         if (f && !tool_bar_p)
+           f->last_tool_bar_item = -1;
+       }
+      else
+       dpyinfo->grabbed &= ~(1 << event->xbutton.button);
+
+      /* Ignore any mouse motion that happened before this event;
+        any subsequent mouse-movement Emacs events should reflect
+        only motion after the ButtonPress/Release.  */
+      if (f != 0)
+       f->mouse_moved = false;
+
+      goto OTHER;
+
+      /* Touch events.  The events here don't parallel X so much.  */
+    case ANDROID_TOUCH_DOWN:
+
+      if (!any)
+       goto OTHER;
+
+      /* This event is sent when a tool is put on the screen.  X and Y
+        are the location of the finger, and pointer_id identifies the
+        tool for as long as it is still held down.  First, see if the
+        touch point already exists and can be reused (this shouldn't
+        happen, but be safe.)  */
+
+      touchpoint = android_find_tool (any, event->touch.pointer_id);
+
+      if (touchpoint)
+       {
+         /* Simply update the tool position and send an update.  */
+         touchpoint->x = event->touch.x;
+         touchpoint->y = event->touch.x;
+         android_update_tools (any, &inev.ie);
+         inev.ie.timestamp = event->touch.time;
+
+         goto OTHER;
+       }
+
+      /* Otherwise, link a new touchpoint onto the output's list of
+        pressed tools.  */
+
+      touchpoint = xmalloc (sizeof *touchpoint);
+      touchpoint->tool_id = event->touch.pointer_id;
+      touchpoint->x = event->touch.x;
+      touchpoint->y = event->touch.x;
+      touchpoint->next = FRAME_OUTPUT_DATA (any)->touch_points;
+      touchpoint->tool_bar_p = false;
+      FRAME_OUTPUT_DATA (any)->touch_points = touchpoint;
+
+      /* Figure out whether or not the tool was pressed on the tool
+        bar.  Note that the code which runs when it was is more or
+        less an abuse of the mouse highlight machinery, but it works
+        well enough in practice.  */
+
+      if (WINDOWP (any->tool_bar_window)
+         && WINDOW_TOTAL_LINES (XWINDOW (any->tool_bar_window)))
+       {
+         Lisp_Object window;
+         int x = event->touch.x;
+         int y = event->touch.y;
+
+         window = window_from_coordinates (any, x, y, 0, true,
+                                           true);
+
+         /* If this touch has started in the tool bar, do not
+            send it to Lisp.  Instead, simulate a tool bar
+            click, releasing it once it goes away.  */
+
+         if (EQ (window, any->tool_bar_window))
+           {
+             /* Call note_mouse_highlight on the tool bar
+                item.  Otherwise, get_tool_bar_item will
+                return 1.
+
+                This is not necessary when mouse-highlight is
+                nil.  */
+
+             if (!NILP (Vmouse_highlight))
+               {
+                 /* Clear the pointer invisible flag to always make
+                    note_mouse_highlight do its thing.  */
+                 any->pointer_invisible = false;
+                 note_mouse_highlight (any, x, y);
+
+                 /* Always allow future mouse motion to
+                    update the mouse highlight, no matter
+                    where it is.  */
+                 memset (&dpyinfo->last_mouse_glyph, 0,
+                         sizeof dpyinfo->last_mouse_glyph);
+                 dpyinfo->last_mouse_glyph_frame = any;
+               }
+
+             handle_tool_bar_click (any, x, y, true, 0);
+
+             /* Flush any changes made by that to the front
+                buffer.  */
+             android_flush_dirty_back_buffer_on (any);
+
+             /* Mark the touch point as being grabbed by the tool
+                bar.  */
+             touchpoint->tool_bar_p = true;
+             goto OTHER;
+           }
+       }
+
+      /* Now generate the Emacs event.  */
+      inev.ie.kind = TOUCHSCREEN_BEGIN_EVENT;
+      inev.ie.timestamp = event->touch.time;
+      XSETFRAME (inev.ie.frame_or_window, any);
+      XSETINT (inev.ie.x, event->touch.x);
+      XSETINT (inev.ie.y, event->touch.y);
+      XSETINT (inev.ie.arg, event->touch.pointer_id);
+
+      goto OTHER;
+
+    case ANDROID_TOUCH_MOVE:
+
+      if (!any)
+       goto OTHER;
+
+      /* Look for the tool that moved.  */
+
+      touchpoint = android_find_tool (any, event->touch.pointer_id);
+
+      /* If it doesn't exist or has been grabbed by the tool bar, skip
+        processing this event.  */
+
+      if (!touchpoint || touchpoint->tool_bar_p)
+       goto OTHER;
+
+      /* Otherwise, update the position and send the update event.  */
+
+      touchpoint->x = event->touch.x;
+      touchpoint->y = event->touch.y;
+      android_update_tools (any, &inev.ie);
+      inev.ie.timestamp = event->touch.time;
+
+      goto OTHER;
+
+    case ANDROID_TOUCH_UP:
+
+      if (!any)
+       goto OTHER;
+
+      /* Now find and unlink the tool in question.  */
+
+      last = &FRAME_OUTPUT_DATA (any)->touch_points;
+      while ((touchpoint = *last))
+       {
+         if (touchpoint->tool_id == event->touch.pointer_id)
+           {
+             *last = touchpoint->next;
+
+             if (touchpoint->tool_bar_p)
+               {
+                 xfree (touchpoint);
+
+                 /* Do what is necessary to release the tool bar and
+                    possibly trigger a click.  */
+
+                 if (any->last_tool_bar_item != -1)
+                   handle_tool_bar_click (any, event->touch.x,
+                                          event->touch.y, false,
+                                          0);
+
+                 /* Cancel any outstanding mouse highlight.  */
+                 note_mouse_highlight (any, -1, -1);
+                 android_flush_dirty_back_buffer_on (any);
+
+                 goto OTHER;
+               }
+
+             /* The tool was unlinked.  Free it and generate the
+                appropriate Emacs event (assuming that it was not
+                grabbed by the tool bar).  */
+             xfree (touchpoint);
+
+             inev.ie.kind = TOUCHSCREEN_END_EVENT;
+             inev.ie.timestamp = event->touch.time;
+
+             /* Report whether the sequence has been canceled.  */
+
+             if (event->touch.flags & ANDROID_TOUCH_SEQUENCE_CANCELED)
+               inev.ie.modifiers = 1;
+
+             XSETFRAME (inev.ie.frame_or_window, any);
+             XSETINT (inev.ie.x, event->touch.x);
+             XSETINT (inev.ie.y, event->touch.y);
+             XSETINT (inev.ie.arg, event->touch.pointer_id);
+
+             /* Break out of the loop.  */
+             goto OTHER;
+           }
+         else
+           last = &touchpoint->next;
+       }
+
+      /* No touch point was found.  This shouldn't happen.  */
+      goto OTHER;
+
+      /* Wheel motion.  The events here don't parallel X because
+        Android doesn't have scroll valuators.  */
+
+    case ANDROID_WHEEL:
+
+      if (!any)
+       goto OTHER;
+
+      if (fabs (event->wheel.x_delta) > 0
+         || fabs (event->wheel.y_delta) > 0)
+       {
+         if (mwheel_coalesce_scroll_events)
+           {
+             if (signbit (event->wheel.x_delta)
+                 != signbit (wheel_event_x))
+               wheel_event_x = 0.0;
+
+             if (signbit (event->wheel.y_delta)
+                 != signbit (wheel_event_y))
+               wheel_event_y = 0.0;
+
+             /* Tally up deltas until one of them exceeds 1.0.  */
+             wheel_event_x += event->wheel.x_delta;
+             wheel_event_y += event->wheel.y_delta;
+
+             if (fabs (wheel_event_x) < 1.0
+                 && fabs (wheel_event_y) < 1.0)
+               goto OTHER;
+           }
+         else
+           {
+             /* Use the deltas in the event.  */
+             wheel_event_x = event->wheel.x_delta;
+             wheel_event_y = event->wheel.y_delta;
+           }
+
+         /* Determine what kind of event to send.  */
+         inev.ie.kind = ((fabs (wheel_event_y)
+                          >= fabs (wheel_event_x))
+                         ? WHEEL_EVENT : HORIZ_WHEEL_EVENT);
+         inev.ie.timestamp = event->wheel.time;
+
+         /* Set the event coordinates.  */
+         XSETINT (inev.ie.x, event->wheel.x);
+         XSETINT (inev.ie.y, event->wheel.y);
+
+         /* Set the frame.  */
+         XSETFRAME (inev.ie.frame_or_window, any);
+
+         /* Figure out the scroll direction.  */
+         inev.ie.modifiers = (signbit ((fabs (wheel_event_x)
+                                        >= fabs (wheel_event_y))
+                                       ? wheel_event_x
+                                       : wheel_event_y)
+                              ? down_modifier : up_modifier);
+
+         /* Figure out how much to scale the deltas by.  */
+         window = window_from_coordinates (any, event->wheel.x,
+                                           event->wheel.y, NULL,
+                                           false, false);
+
+         if (WINDOWP (window))
+           scroll_height = XWINDOW (window)->pixel_height;
+         else
+           /* EVENT_X and EVENT_Y can be outside the
+              frame if F holds the input grab, so fall
+              back to the height of the frame instead.  */
+           scroll_height = FRAME_PIXEL_HEIGHT (any);
+
+         scroll_unit = pow (scroll_height, 2.0 / 3.0);
+
+         /* Add the keyboard modifiers.  */
+         inev.ie.modifiers
+           |= android_android_to_emacs_modifiers (dpyinfo,
+                                                  event->wheel.state);
+
+         /* Finally include the scroll deltas.  */
+         inev.ie.arg = list3 (Qnil,
+                              make_float (wheel_event_x
+                                          * scroll_unit),
+                              make_float (wheel_event_y
+                                          * scroll_unit));
+
+         wheel_event_x = 0.0;
+         wheel_event_y = 0.0;
+       }
+
+      goto OTHER;
+
+      /* Iconification.  This is vastly simpler than on X.  */
+    case ANDROID_ICONIFIED:
+
+      if (!any)
+       goto OTHER;
+
+      if (FRAME_ICONIFIED_P (any))
+       goto OTHER;
+
+      SET_FRAME_VISIBLE (any, false);
+      SET_FRAME_ICONIFIED (any, true);
+
+      inev.ie.kind = ICONIFY_EVENT;
+      XSETFRAME (inev.ie.frame_or_window, any);
+      goto OTHER;
+
+    case ANDROID_DEICONIFIED:
+
+      if (!any)
+       goto OTHER;
+
+      if (!FRAME_ICONIFIED_P (any))
+       goto OTHER;
+
+      SET_FRAME_VISIBLE (any, true);
+      SET_FRAME_ICONIFIED (any, false);
+
+      inev.ie.kind = DEICONIFY_EVENT;
+      XSETFRAME (inev.ie.frame_or_window, any);
+      goto OTHER;
+
+      /* Context menu handling.  */
+    case ANDROID_CONTEXT_MENU:
+
+      if (dpyinfo->menu_event_id == -1
+         /* Previously displayed popup menus might generate events
+            after dismissal, which might interfere.
+            `current_menu_serial' is always set to an identifier
+            identifying the last context menu to be displayed.  */
+         && event->menu.menu_event_serial == current_menu_serial)
+       dpyinfo->menu_event_id = event->menu.menu_event_id;
+
+      goto OTHER;
+
+      /* Input method events.  textconv.c functions are called here to
+        queue events, which are then executed in a safe context
+        inside keyboard.c.  */
+    case ANDROID_INPUT_METHOD:
+
+      if (!any)
+       {
+         /* Free any text allocated for this event.  */
+         xfree (event->ime.text);
+
+         /* If edits associated with this event haven't been
+            processed yet, signal their completion to avoid delays
+            the next time a call to `android_sync_edit' is made.
+
+            If events for a deleted frame are interleaved with events
+            for another frame, the edit counter may be prematurely
+            incremented before edits associated with the other frames
+            are processed.  This is not a problem in practice.  */
+
+         android_notify_conversion (event->ime.counter);
+       }
+      else
+       android_handle_ime_event (event, any);
+
+      goto OTHER;
+
+    default:
+      goto OTHER;
+    }
+
+ OTHER:
+  if (inev.ie.kind != NO_EVENT)
+    {
+      kbd_buffer_store_buffered_event (&inev, hold_quit);
+      count++;
+    }
+
+  if (do_help
+      && !(hold_quit && hold_quit->kind != NO_EVENT))
+    {
+      Lisp_Object frame;
+
+      if (f)
+       XSETFRAME (frame, f);
+      else
+       frame = Qnil;
+
+      if (do_help > 0)
+       {
+         any_help_event_p = true;
+         gen_help_event (help_echo_string, frame, help_echo_window,
+                         help_echo_object, help_echo_pos);
+       }
+      else
+       {
+         help_echo_string = Qnil;
+         gen_help_event (Qnil, frame, Qnil, Qnil, 0);
+       }
+      count++;
+    }
+
+  return count;
+}
+
+static int
+android_read_socket (struct terminal *terminal,
+                    struct input_event *hold_quit)
+{
+  int count = 0;
+  struct android_display_info *dpyinfo;
+
+  dpyinfo = terminal->display_info.android;
+
+  block_input ();
+  while (android_pending ())
+    {
+      int finish;
+      union android_event event;
+
+      android_next_event (&event);
+      count += handle_one_android_event (dpyinfo, &event, &finish,
+                                        hold_quit);
+
+      if (finish == ANDROID_EVENT_GOTO_OUT)
+       break;
+    }
+  unblock_input ();
+
+  /* If the focus was just given to an auto-raising frame, raise it
+     now.  */
+  if (dpyinfo->pending_autoraise_frame)
+    {
+      android_raise_frame (dpyinfo->pending_autoraise_frame);
+      dpyinfo->pending_autoraise_frame = NULL;
+    }
+
+  return count;
+}
+
+static void
+android_frame_up_to_date (struct frame *f)
+{
+  eassert (FRAME_ANDROID_P (f));
+  block_input ();
+  FRAME_MOUSE_UPDATE (f);
+
+  if (!buffer_flipping_blocked_p ()
+      && FRAME_ANDROID_NEED_BUFFER_FLIP (f))
+    show_back_buffer (f);
+
+  /* The frame is now complete, as its contents have been drawn.  */
+  FRAME_ANDROID_COMPLETE_P (f) = true;
+
+  /* Shrink the scanline buffer used by the font backend.  */
+  sfntfont_android_shrink_scanline_buffer ();
+  unblock_input ();
+}
+
+static void
+android_buffer_flipping_unblocked_hook (struct frame *f)
+{
+  block_input ();
+
+  if (FRAME_ANDROID_NEED_BUFFER_FLIP (f))
+    show_back_buffer (f);
+
+  unblock_input ();
+}
+
+static void
+android_query_frame_background_color (struct frame *f, Emacs_Color *bgcolor)
+{
+  unsigned long background;
+
+  background = FRAME_BACKGROUND_PIXEL (f);
+  bgcolor->pixel = background;
+
+  android_query_colors (f, bgcolor, 1);
+}
+
+int
+android_parse_color (struct frame *f, const char *color_name,
+                    Emacs_Color *color)
+{
+  unsigned short r, g, b;
+  Lisp_Object tem, tem1;
+  unsigned long lisp_color;
+
+  if (parse_color_spec (color_name, &r, &g, &b))
+    {
+      color->red = r;
+      color->green = g;
+      color->blue = b;
+
+      return 1;
+    }
+
+  tem = x_display_list->color_map;
+  for (; CONSP (tem); tem = XCDR (tem))
+    {
+      tem1 = XCAR (tem);
+
+      if (CONSP (tem1)
+         && !xstrcasecmp (SSDATA (XCAR (tem1)), color_name))
+       {
+         lisp_color = XFIXNUM (XCDR (tem1));
+         color->red = RED_FROM_ULONG (lisp_color) * 257;
+         color->green = GREEN_FROM_ULONG (lisp_color) * 257;
+         color->blue = BLUE_FROM_ULONG (lisp_color) * 257;
+         return 1;
+       }
+    }
+
+  return 0;
+}
+
+bool
+android_alloc_nearest_color (struct frame *f, Emacs_Color *color)
+{
+  gamma_correct (f, color);
+  color->pixel = RGB_TO_ULONG (color->red / 256,
+                              color->green / 256,
+                              color->blue / 256);
+
+  return true;
+}
+
+void
+android_query_colors (struct frame *f, Emacs_Color *colors, int ncolors)
+{
+  int i;
+
+  for (i = 0; i < ncolors; ++i)
+    {
+      colors[i].red = RED_FROM_ULONG (colors[i].pixel) * 257;
+      colors[i].green = RED_FROM_ULONG (colors[i].pixel) * 257;
+      colors[i].blue = RED_FROM_ULONG (colors[i].pixel) * 257;
+    }
+}
+
+static void
+android_mouse_position (struct frame **fp, int insist,
+                       Lisp_Object *bar_window,
+                       enum scroll_bar_part *part, Lisp_Object *x,
+                       Lisp_Object *y, Time *timestamp)
+{
+  Lisp_Object tail, frame;
+  struct android_display_info *dpyinfo;
+
+  dpyinfo = FRAME_DISPLAY_INFO (*fp);
+
+  /* This is the best implementation possible on Android, where the
+     system doesn't let Emacs obtain any information about the mouse
+     pointer at all.  */
+
+  if (dpyinfo->last_mouse_motion_frame)
+    {
+      *fp = dpyinfo->last_mouse_motion_frame;
+      *timestamp = dpyinfo->last_mouse_movement_time;
+      *x = make_fixnum (dpyinfo->last_mouse_motion_x);
+      *y = make_fixnum (dpyinfo->last_mouse_motion_y);
+      *bar_window = Qnil;
+      *part = scroll_bar_nowhere;
+
+      FOR_EACH_FRAME (tail, frame)
+       {
+         if (FRAME_ANDROID_P (XFRAME (frame)))
+           XFRAME (frame)->mouse_moved = false;
+       }
+
+      dpyinfo->last_mouse_motion_frame->mouse_moved = false;
+    }
+}
+
+static Lisp_Object
+android_get_focus_frame (struct frame *f)
+{
+  Lisp_Object lisp_focus;
+  struct frame *focus;
+
+  focus = FRAME_DISPLAY_INFO (f)->focus_frame;
+
+  if (!focus)
+    return Qnil;
+
+  XSETFRAME (lisp_focus, focus);
+  return lisp_focus;
+}
+
+static void
+android_focus_frame (struct frame *f, bool noactivate)
+{
+  /* Set the input focus to the frame's window.  The system only lets
+     this work on child frames.  */
+  android_set_input_focus (FRAME_ANDROID_WINDOW (f),
+                          ANDROID_CURRENT_TIME);
+}
+
+/* The two procedures below only have to update the cursor on Android,
+   as there are no window borders there.  */
+
+static void
+android_frame_highlight (struct frame *f)
+{
+  gui_update_cursor (f, true);
+}
+
+static void
+android_frame_unhighlight (struct frame *f)
+{
+  gui_update_cursor (f, true);
+}
+
+static void
+android_frame_rehighlight (struct android_display_info *dpyinfo)
+{
+  struct frame *old_highlight;
+
+  old_highlight = dpyinfo->highlight_frame;
+
+  if (dpyinfo->focus_frame)
+    {
+      dpyinfo->highlight_frame
+       = ((FRAMEP (FRAME_FOCUS_FRAME (dpyinfo->focus_frame)))
+          ? XFRAME (FRAME_FOCUS_FRAME (dpyinfo->focus_frame))
+          : dpyinfo->focus_frame);
+      if (!FRAME_LIVE_P (dpyinfo->highlight_frame))
+       {
+         fset_focus_frame (dpyinfo->focus_frame, Qnil);
+         dpyinfo->highlight_frame = dpyinfo->focus_frame;
+       }
+    }
+  else
+    dpyinfo->highlight_frame = 0;
+
+  if (dpyinfo->highlight_frame != old_highlight)
+    {
+      /* This is not yet required on Android.  */
+      if (old_highlight)
+       android_frame_unhighlight (old_highlight);
+      if (dpyinfo->highlight_frame)
+       android_frame_highlight (dpyinfo->highlight_frame);
+    }
+}
+
+static void
+android_frame_rehighlight_hook (struct frame *f)
+{
+  android_frame_rehighlight (FRAME_DISPLAY_INFO (f));
+}
+
+static void
+android_frame_raise_lower (struct frame *f, bool raise_flag)
+{
+  if (raise_flag)
+    android_raise_frame (f);
+  else
+    android_lower_frame (f);
+}
+
+void
+android_make_frame_visible (struct frame *f)
+{
+  android_map_window (FRAME_ANDROID_WINDOW (f));
+
+  SET_FRAME_VISIBLE (f, true);
+  SET_FRAME_ICONIFIED (f, false);
+}
+
+void
+android_make_frame_invisible (struct frame *f)
+{
+  /* Don't keep the highlight on an invisible frame.  */
+  if (FRAME_DISPLAY_INFO (f)->highlight_frame == f)
+    FRAME_DISPLAY_INFO (f)->highlight_frame = 0;
+
+  android_unmap_window (FRAME_ANDROID_WINDOW (f));
+
+  SET_FRAME_VISIBLE (f, false);
+  SET_FRAME_ICONIFIED (f, false);
+}
+
+static void
+android_make_frame_visible_invisible (struct frame *f, bool visible)
+{
+  if (visible)
+    android_make_frame_visible (f);
+  else
+    android_make_frame_invisible (f);
+}
+
+static void
+android_fullscreen_hook (struct frame *f)
+{
+  Lisp_Object wanted;
+
+  if (!FRAME_PARENT_FRAME (f))
+    {
+      /* Explicitly setting fullscreen is not supported on older
+        Android versions.  */
+
+      wanted = (f->want_fullscreen == FULLSCREEN_BOTH
+               ? Qfullscreen : Qmaximized);
+
+      if (android_set_fullscreen (FRAME_ANDROID_WINDOW (f),
+                                 EQ (wanted, Qfullscreen)))
+       store_frame_param (f, Qfullscreen, Qmaximized);
+      else
+        store_frame_param (f, Qfullscreen, wanted);
+    }
+  else
+    {
+      store_frame_param (f, Qfullscreen, Qnil);
+
+      /* If this is a child frame, don't keep it fullscreen
+        anymore.  */
+      android_set_fullscreen (FRAME_ANDROID_WINDOW (f), false);
+    }
+}
+
+void
+android_iconify_frame (struct frame *f)
+{
+  /* This really doesn't work on Android.  */
+  error ("Can't notify window manager of iconification");
+}
+
+static void
+android_wait_for_event (struct frame *f, int eventtype)
+{
+  if (!FLOATP (Vandroid_wait_for_event_timeout))
+    return;
+
+  int level = interrupt_input_blocked;
+  struct timespec tmo, tmo_at, time_now;
+
+  f->wait_event_type = eventtype;
+
+  /* Default timeout is 0.1 second.  Hopefully not noticeable.  */
+  double timeout = XFLOAT_DATA (Vandroid_wait_for_event_timeout);
+  time_t timeout_seconds = (time_t) timeout;
+  tmo = make_timespec (timeout_seconds,
+                      (long int) ((timeout - timeout_seconds)
+                                  * 1000 * 1000 * 1000));
+  tmo_at = timespec_add (current_timespec (), tmo);
+
+  while (f->wait_event_type)
+    {
+      pending_signals = true;
+      totally_unblock_input ();
+      /* XTread_socket is called after unblock.  */
+      block_input ();
+      interrupt_input_blocked = level;
+
+      time_now = current_timespec ();
+      if (timespec_cmp (tmo_at, time_now) < 0)
+       break;
+
+      tmo = timespec_sub (tmo_at, time_now);
+      if (android_select (0, NULL, NULL, NULL, &tmo) == 0)
+        break; /* Timeout */
+    }
+
+  f->wait_event_type = 0;
+}
+
+static void
+android_set_window_size_1 (struct frame *f, bool change_gravity,
+                          int width, int height)
+{
+  if (change_gravity)
+    f->win_gravity = NorthWestGravity;
+
+  android_resize_window (FRAME_ANDROID_WINDOW (f), width,
+                        height);
+
+  SET_FRAME_GARBAGED (f);
+
+  if (FRAME_VISIBLE_P (f))
+    {
+      android_wait_for_event (f, ANDROID_CONFIGURE_NOTIFY);
+
+      if (CONSP (frame_size_history))
+       frame_size_history_extra (f, build_string ("set_window_size_1 visible"),
+                                 FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
+                                 width, height, f->new_width, f->new_height);
+    }
+  else
+    {
+      if (CONSP (frame_size_history))
+       frame_size_history_extra (f, build_string ("set_window_size_1 "
+                                                  "invisible"),
+                                 FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
+                                 width, height, f->new_width, f->new_height);
+
+      adjust_frame_size (f, FRAME_PIXEL_TO_TEXT_WIDTH (f, width),
+                        FRAME_PIXEL_TO_TEXT_HEIGHT (f, height),
+                        5, 0, Qx_set_window_size_1);
+    }
+}
+
+void
+android_set_window_size (struct frame *f, bool change_gravity,
+                        int width, int height)
+{
+  block_input ();
+
+  android_set_window_size_1 (f, change_gravity, width, height);
+  android_clear_under_internal_border (f);
+
+  /* If cursor was outside the new size, mark it as off.  */
+  mark_window_cursors_off (XWINDOW (f->root_window));
+
+  /* Clear out any recollection of where the mouse highlighting was,
+     since it might be in a place that's outside the new frame size.
+     Actually checking whether it is outside is a pain in the neck,
+     so don't try--just let the highlighting be done afresh with new size.  */
+  cancel_mouse_face (f);
+
+  unblock_input ();
+
+  do_pending_window_change (false);
+}
+
+static void
+android_set_offset (struct frame *f, int xoff, int yoff,
+                   int change_gravity)
+{
+  if (change_gravity > 0)
+    {
+      f->top_pos = yoff;
+      f->left_pos = xoff;
+      f->size_hint_flags &= ~ (XNegative | YNegative);
+      if (xoff < 0)
+       f->size_hint_flags |= XNegative;
+      if (yoff < 0)
+       f->size_hint_flags |= YNegative;
+      f->win_gravity = NorthWestGravity;
+    }
+
+  android_move_window (FRAME_ANDROID_WINDOW (f), xoff, yoff);
+}
+
+static void
+android_set_alpha (struct frame *f)
+{
+  /* Not supported on Android.  */
+}
+
+static Lisp_Object
+android_new_font (struct frame *f, Lisp_Object font_object, int fontset)
+{
+  struct font *font = XFONT_OBJECT (font_object);
+  int unit, font_ascent, font_descent;
+
+  if (fontset < 0)
+    fontset = fontset_from_font (font_object);
+  FRAME_FONTSET (f) = fontset;
+  if (FRAME_FONT (f) == font)
+    /* This font is already set in frame F.  There's nothing more to
+       do.  */
+    return font_object;
+
+  FRAME_FONT (f) = font;
+  FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
+  FRAME_COLUMN_WIDTH (f) = font->average_width;
+  get_font_ascent_descent (font, &font_ascent, &font_descent);
+  FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
+
+  /* We could use a more elaborate calculation here.  */
+  FRAME_TAB_BAR_HEIGHT (f) = FRAME_TAB_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);
+
+  /* Compute character columns occupied by scrollbar.
+
+     Don't do things differently for non-toolkit scrollbars
+     (Bug#17163).  */
+  unit = FRAME_COLUMN_WIDTH (f);
+  if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)
+    FRAME_CONFIG_SCROLL_BAR_COLS (f)
+      = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + unit - 1) / unit;
+  else
+    FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + unit - 1) / unit;
+
+
+  /* Don't change the size of a tip frame; there's no point in doing it
+     because it's done in Fx_show_tip, and it leads to problems because
+     the tip frame has no widget.  */
+  if (FRAME_ANDROID_WINDOW (f) != 0 && !FRAME_TOOLTIP_P (f))
+    adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
+                      FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
+                      false, Qfont);
+
+  return font_object;
+}
+
+static bool
+android_bitmap_icon (struct frame *f, Lisp_Object file)
+{
+  return false;
+}
+
+static void
+android_free_pixmap_hook (struct frame *f, Emacs_Pixmap pixmap)
+{
+  android_free_pixmap (pixmap);
+}
+
+void
+android_free_frame_resources (struct frame *f)
+{
+  struct android_display_info *dpyinfo;
+  Mouse_HLInfo *hlinfo;
+  struct android_touch_point *last, *next;
+
+  dpyinfo = FRAME_DISPLAY_INFO (f);
+  hlinfo = &dpyinfo->mouse_highlight;
+
+  block_input ();
+  free_frame_faces (f);
+
+  /* FRAME_ANDROID_WINDOW can be 0 if frame creation failed.  */
+  if (FRAME_ANDROID_WINDOW (f))
+    android_destroy_window (FRAME_ANDROID_WINDOW (f));
+
+  android_free_gcs (f);
+
+  /* Free cursors.  */
+  if (f->output_data.android->text_cursor)
+    android_free_cursor (f->output_data.android->text_cursor);
+  if (f->output_data.android->nontext_cursor)
+    android_free_cursor (f->output_data.android->nontext_cursor);
+  if (f->output_data.android->modeline_cursor)
+    android_free_cursor (f->output_data.android->modeline_cursor);
+  if (f->output_data.android->hand_cursor)
+    android_free_cursor (f->output_data.android->hand_cursor);
+  if (f->output_data.android->hourglass_cursor)
+    android_free_cursor (f->output_data.android->hourglass_cursor);
+  if (f->output_data.android->horizontal_drag_cursor)
+    android_free_cursor (f->output_data.android->horizontal_drag_cursor);
+  if (f->output_data.android->vertical_drag_cursor)
+    android_free_cursor (f->output_data.android->vertical_drag_cursor);
+  if (f->output_data.android->left_edge_cursor)
+    android_free_cursor (f->output_data.android->left_edge_cursor);
+  if (f->output_data.android->top_left_corner_cursor)
+    android_free_cursor (f->output_data.android->top_left_corner_cursor);
+  if (f->output_data.android->top_edge_cursor)
+    android_free_cursor (f->output_data.android->top_edge_cursor);
+  if (f->output_data.android->top_right_corner_cursor)
+    android_free_cursor (f->output_data.android->top_right_corner_cursor);
+  if (f->output_data.android->right_edge_cursor)
+    android_free_cursor (f->output_data.android->right_edge_cursor);
+  if (f->output_data.android->bottom_right_corner_cursor)
+    android_free_cursor (f->output_data.android->bottom_right_corner_cursor);
+  if (f->output_data.android->bottom_edge_cursor)
+    android_free_cursor (f->output_data.android->bottom_edge_cursor);
+  if (f->output_data.android->bottom_left_corner_cursor)
+    android_free_cursor (f->output_data.android->bottom_left_corner_cursor);
+
+  /* Free extra GCs allocated by android_setup_relief_colors.  */
+  if (f->output_data.android->white_relief.gc)
+    {
+      android_free_gc (f->output_data.android->white_relief.gc);
+      f->output_data.android->white_relief.gc = 0;
+    }
+  if (f->output_data.android->black_relief.gc)
+    {
+      android_free_gc (f->output_data.android->black_relief.gc);
+      f->output_data.android->black_relief.gc = 0;
+    }
+
+  if (f == dpyinfo->focus_frame)
+    dpyinfo->focus_frame = 0;
+  if (f == dpyinfo->x_focus_event_frame)
+    dpyinfo->x_focus_event_frame = 0;
+  if (f == dpyinfo->highlight_frame)
+    dpyinfo->highlight_frame = 0;
+  if (f == hlinfo->mouse_face_mouse_frame)
+    reset_mouse_highlight (hlinfo);
+
+  /* These two need to be freed now that they are used to compute the
+     mouse position, I think.  */
+  if (f == dpyinfo->last_mouse_motion_frame)
+    dpyinfo->last_mouse_motion_frame = NULL;
+  if (f == dpyinfo->last_mouse_frame)
+    dpyinfo->last_mouse_frame = NULL;
+
+  /* Free all tool presses currently active on this frame.  */
+  next = FRAME_OUTPUT_DATA (f)->touch_points;
+  while (next)
+    {
+      last = next;
+      next = next->next;
+      xfree (last);
+    }
+
+  /* Clear this in case unblock_input reads events.  */
+  FRAME_OUTPUT_DATA (f)->touch_points = NULL;
+
+  unblock_input ();
+}
+
+static void
+android_delete_frame (struct frame *f)
+{
+  android_free_frame_resources (f);
+  xfree (f->output_data.android);
+  f->output_data.android = NULL;
+}
+
+static void
+android_delete_terminal (struct terminal *terminal)
+{
+  error ("Cannot terminate connection to Android display server");
+}
+
+
+
+/* RIF functions.  */
+
+static void
+android_scroll_run (struct window *w, struct run *run)
+{
+  struct frame *f = XFRAME (w->frame);
+  int x, y, width, height, from_y, to_y, bottom_y;
+
+  /* Get frame-relative bounding box of the text display area of W,
+     without mode lines.  Include in this box the left and right
+     fringe of W.  */
+  window_box (w, ANY_AREA, &x, &y, &width, &height);
+
+  from_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->current_y);
+  to_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->desired_y);
+  bottom_y = y + height;
+
+  if (to_y < from_y)
+    {
+      /* Scrolling up.  Make sure we don't copy part of the mode
+        line at the bottom.  */
+      if (from_y + run->height > bottom_y)
+       height = bottom_y - from_y;
+      else
+       height = run->height;
+    }
+  else
+    {
+      /* Scrolling down.  Make sure we don't copy over the mode line.
+        at the bottom.  */
+      if (to_y + run->height > bottom_y)
+       height = bottom_y - to_y;
+      else
+       height = run->height;
+    }
+
+  block_input ();
+
+  /* Cursor off.  Will be switched on again in gui_update_window_end.  */
+  gui_clear_cursor (w);
+
+  /* To avoid sequence point problems, make sure to only call
+     FRAME_ANDROID_DRAWABLE once.  */
+  android_copy_area (FRAME_ANDROID_DRAWABLE (f),
+                    FRAME_ANDROID_WINDOW (f),
+                    f->output_data.android->normal_gc,
+                    x, from_y, width, height, x, to_y);
+
+  unblock_input ();
+}
+
+static void
+android_after_update_window_line (struct window *w, struct glyph_row 
*desired_row)
+{
+  eassert (w);
+
+  if (!desired_row->mode_line_p && !w->pseudo_window_p)
+    desired_row->redraw_fringe_bitmaps_p = true;
+}
+
+static void
+android_flip_and_flush (struct frame *f)
+{
+  block_input ();
+
+  if (FRAME_ANDROID_NEED_BUFFER_FLIP (f))
+    show_back_buffer (f);
+
+  /* The frame is complete again as its contents were just
+     flushed.  */
+  FRAME_ANDROID_COMPLETE_P (f) = true;
+  unblock_input ();
+}
+
+static void
+android_clear_rectangle (struct frame *f, struct android_gc *gc, int x,
+                        int y, int width, int height)
+{
+  struct android_gc_values xgcv;
+
+  android_get_gc_values (gc, (ANDROID_GC_BACKGROUND
+                             | ANDROID_GC_FOREGROUND),
+                        &xgcv);
+  android_set_foreground (gc, xgcv.background);
+  android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+                         x, y, width, height);
+  android_set_foreground (gc, xgcv.foreground);
+}
+
+static void
+android_reset_clip_rectangles (struct frame *f, struct android_gc *gc)
+{
+  android_set_clip_mask (gc, ANDROID_NONE);
+}
+
+static void
+android_clip_to_row (struct window *w, struct glyph_row *row,
+                    enum glyph_row_area area, struct android_gc *gc)
+{
+  struct android_rectangle clip_rect;
+  int window_x, window_y, window_width;
+
+  window_box (w, area, &window_x, &window_y, &window_width, 0);
+
+  clip_rect.x = window_x;
+  clip_rect.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y));
+  clip_rect.y = max (clip_rect.y, window_y);
+  clip_rect.width = window_width;
+  clip_rect.height = row->visible_height;
+
+  android_set_clip_rectangles (gc, 0, 0, &clip_rect, 1);
+}
+
+static void
+android_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
+                           struct draw_fringe_bitmap_params *p)
+{
+  struct frame *f = XFRAME (WINDOW_FRAME (w));
+  struct android_gc *gc = f->output_data.android->normal_gc;
+  struct face *face = p->face;
+
+  /* Must clip because of partially visible lines.  */
+  android_clip_to_row (w, row, ANY_AREA, gc);
+
+  if (p->bx >= 0 && !p->overlay_p)
+    {
+      /* In case the same realized face is used for fringes and for
+        something displayed in the text (e.g. face `region' on
+        mono-displays, the fill style may have been changed to
+        ANDROID_FILL_SOLID in
+        android_draw_glyph_string_background.  */
+      if (face->stipple)
+       {
+         android_set_fill_style (face->gc, ANDROID_FILL_OPAQUE_STIPPLED);
+         android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), face->gc,
+                                 p->bx, p->by, p->nx, p->ny);
+         android_set_fill_style (face->gc, ANDROID_FILL_SOLID);
+
+         row->stipple_p = true;
+       }
+      else
+       {
+         android_set_background (face->gc, face->background);
+         android_clear_rectangle (f, face->gc, p->bx, p->by, p->nx, p->ny);
+         android_set_foreground (face->gc, face->foreground);
+       }
+    }
+
+  if (p->which)
+    {
+      android_drawable drawable;
+      char *bits;
+      android_pixmap pixmap, clipmask;
+      struct android_gc_values gcv;
+      unsigned long background, cursor_pixel;
+      int depth;
+
+      drawable = FRAME_ANDROID_DRAWABLE (f);
+      clipmask = ANDROID_NONE;
+      background = face->background;
+      cursor_pixel = f->output_data.android->cursor_pixel;
+      depth = FRAME_DISPLAY_INFO (f)->n_planes;
+
+      if (p->wd > 8)
+       bits = (char *) (p->bits + p->dh);
+      else
+       bits = (char *) p->bits + p->dh;
+
+      pixmap = android_create_pixmap_from_bitmap_data (bits, p->wd, p->h,
+                                                      (p->cursor_p
+                                                       ? (p->overlay_p
+                                                          ? face->background
+                                                          : cursor_pixel)
+                                                       : face->foreground),
+                                                      background, depth);
+
+      if (p->overlay_p)
+       {
+         clipmask = android_create_pixmap_from_bitmap_data (bits, p->wd, p->h,
+                                                            1, 0, 1);
+
+         gcv.clip_mask = clipmask;
+         gcv.clip_x_origin = p->x;
+         gcv.clip_y_origin = p->y;
+         android_change_gc (gc, (ANDROID_GC_CLIP_MASK
+                                 | ANDROID_GC_CLIP_X_ORIGIN
+                                 | ANDROID_GC_CLIP_Y_ORIGIN),
+                            &gcv);
+       }
+
+      android_copy_area (pixmap, drawable, gc, 0, 0, p->wd, p->h,
+                        p->x, p->y);
+      android_free_pixmap (pixmap);
+
+      if (p->overlay_p)
+       {
+         gcv.clip_mask = ANDROID_NONE;
+         android_change_gc (gc, ANDROID_GC_CLIP_MASK, &gcv);
+         android_free_pixmap (clipmask);
+       }
+    }
+
+  android_reset_clip_rectangles (f, gc);
+}
+
+/* Set S->gc to a suitable GC for drawing glyph string S in cursor
+   face.  */
+
+static void
+android_set_cursor_gc (struct glyph_string *s)
+{
+  if (s->font == FRAME_FONT (s->f)
+      && s->face->background == FRAME_BACKGROUND_PIXEL (s->f)
+      && s->face->foreground == FRAME_FOREGROUND_PIXEL (s->f)
+      && !s->cmp)
+    s->gc = s->f->output_data.android->cursor_gc;
+  else
+    {
+      /* Cursor on non-default face: must merge.  */
+      struct android_gc_values xgcv;
+      unsigned long mask;
+
+      xgcv.background = s->f->output_data.android->cursor_pixel;
+      xgcv.foreground = s->face->background;
+
+      /* If the glyph would be invisible, try a different foreground.  */
+      if (xgcv.foreground == xgcv.background)
+       xgcv.foreground = s->face->foreground;
+      if (xgcv.foreground == xgcv.background)
+       xgcv.foreground = s->f->output_data.android->cursor_foreground_pixel;
+      if (xgcv.foreground == xgcv.background)
+       xgcv.foreground = s->face->foreground;
+
+      /* Make sure the cursor is distinct from text in this face.  */
+      if (xgcv.background == s->face->background
+         && xgcv.foreground == s->face->foreground)
+       {
+         xgcv.background = s->face->foreground;
+         xgcv.foreground = s->face->background;
+       }
+
+      mask = (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND);
+
+      if (FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc)
+       android_change_gc (FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc,
+                          mask, &xgcv);
+      else
+       FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc
+          = android_create_gc (mask, &xgcv);
+
+      s->gc = FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc;
+    }
+}
+
+
+/* Set up S->gc of glyph string S for drawing text in mouse face.  */
+
+static void
+android_set_mouse_face_gc (struct glyph_string *s)
+{
+  if (s->font == s->face->font)
+    s->gc = s->face->gc;
+  else
+    {
+      /* Otherwise construct scratch_cursor_gc with values from FACE
+        except for FONT.  */
+      struct android_gc_values xgcv;
+      unsigned long mask;
+
+      xgcv.background = s->face->background;
+      xgcv.foreground = s->face->foreground;
+
+      mask = (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND);
+
+      if (FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc)
+       android_change_gc (FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc,
+                          mask, &xgcv);
+      else
+       FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc
+          = android_create_gc (mask, &xgcv);
+
+      s->gc = FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc;
+    }
+
+  eassert (s->gc != 0);
+}
+
+
+/* Set S->gc of glyph string S to a GC suitable for drawing a mode line.
+   Faces to use in the mode line have already been computed when the
+   matrix was built, so there isn't much to do, here.  */
+
+static void
+android_set_mode_line_face_gc (struct glyph_string *s)
+{
+  s->gc = s->face->gc;
+}
+
+/* Set S->gc of glyph string S for drawing that glyph string.  Set
+   S->stippled_p to a non-zero value if the face of S has a stipple
+   pattern.  */
+
+static void
+android_set_glyph_string_gc (struct glyph_string *s)
+{
+  prepare_face_for_display (s->f, s->face);
+
+  if (s->hl == DRAW_NORMAL_TEXT)
+    {
+      s->gc = s->face->gc;
+      s->stippled_p = s->face->stipple != 0;
+    }
+  else if (s->hl == DRAW_INVERSE_VIDEO)
+    {
+      android_set_mode_line_face_gc (s);
+      s->stippled_p = s->face->stipple != 0;
+    }
+  else if (s->hl == DRAW_CURSOR)
+    {
+      android_set_cursor_gc (s);
+      s->stippled_p = false;
+    }
+  else if (s->hl == DRAW_MOUSE_FACE)
+    {
+      android_set_mouse_face_gc (s);
+      s->stippled_p = s->face->stipple != 0;
+    }
+  else if (s->hl == DRAW_IMAGE_RAISED
+          || s->hl == DRAW_IMAGE_SUNKEN)
+    {
+      s->gc = s->face->gc;
+      s->stippled_p = s->face->stipple != 0;
+    }
+  else
+    emacs_abort ();
+
+  /* GC must have been set.  */
+  eassert (s->gc != 0);
+}
+
+
+/* Set clipping for output of glyph string S.  S may be part of a mode
+   line or menu if we don't have X toolkit support.  */
+
+static void
+android_set_glyph_string_clipping (struct glyph_string *s)
+{
+  struct android_rectangle *r = s->clip;
+  int n = get_glyph_string_clip_rects (s, r, 2);
+
+  if (n > 0)
+    android_set_clip_rectangles (s->gc, 0, 0, r, n);
+  s->num_clips = n;
+}
+
+
+/* Set SRC's clipping for output of glyph string DST.  This is called
+   when we are drawing DST's left_overhang or right_overhang only in
+   the area of SRC.  */
+
+static void
+android_set_glyph_string_clipping_exactly (struct glyph_string *src,
+                                          struct glyph_string *dst)
+{
+  struct android_rectangle r;
+
+  r.x = src->x;
+  r.width = src->width;
+  r.y = src->y;
+  r.height = src->height;
+  dst->clip[0] = r;
+  dst->num_clips = 1;
+  android_set_clip_rectangles (dst->gc, 0, 0, &r, 1);
+}
+
+static void
+android_compute_glyph_string_overhangs (struct glyph_string *s)
+{
+  if (s->cmp == NULL
+      && (s->first_glyph->type == CHAR_GLYPH
+         || s->first_glyph->type == COMPOSITE_GLYPH))
+    {
+      struct font_metrics metrics;
+
+      if (s->first_glyph->type == CHAR_GLYPH)
+       {
+         struct font *font = s->font;
+         font->driver->text_extents (font, s->char2b, s->nchars, &metrics);
+       }
+      else
+       {
+         Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
+
+         composition_gstring_width (gstring, s->cmp_from, s->cmp_to, &metrics);
+       }
+      s->right_overhang = (metrics.rbearing > metrics.width
+                          ? metrics.rbearing - metrics.width : 0);
+      s->left_overhang = metrics.lbearing < 0 ? - metrics.lbearing : 0;
+    }
+  else if (s->cmp)
+    {
+      s->right_overhang = s->cmp->rbearing - s->cmp->pixel_width;
+      s->left_overhang = - s->cmp->lbearing;
+    }
+}
+
+static void
+android_clear_glyph_string_rect (struct glyph_string *s, int x, int y,
+                                int w, int h)
+{
+  android_clear_rectangle (s->f, s->gc, x, y, w, h);
+}
+
+static void
+android_draw_glyph_string_background (struct glyph_string *s, bool force_p)
+{
+  /* Nothing to do if background has already been drawn or if it
+     shouldn't be drawn in the first place.  */
+  if (!s->background_filled_p)
+    {
+      int box_line_width = max (s->face->box_horizontal_line_width, 0);
+
+      if (s->stippled_p)
+       {
+         /* Fill background with a stipple pattern.  */
+         android_set_fill_style (s->gc, ANDROID_FILL_OPAQUE_STIPPLED);
+         android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+                                 s->x, s->y + box_line_width,
+                                 s->background_width,
+                                 s->height - 2 * box_line_width);
+         android_set_fill_style (s->gc, ANDROID_FILL_SOLID);
+         s->background_filled_p = true;
+       }
+      else if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
+              /* When xdisp.c ignores FONT_HEIGHT, we cannot trust
+                 font dimensions, since the actual glyphs might be
+                 much smaller.  So in that case we always clear the
+                 rectangle with background color.  */
+              || FONT_TOO_HIGH (s->font)
+              || s->font_not_found_p
+              || s->extends_to_end_of_line_p
+              || force_p)
+       {
+         android_clear_glyph_string_rect (s, s->x, s->y + box_line_width,
+                                          s->background_width,
+                                          s->height - 2 * box_line_width);
+         s->background_filled_p = true;
+       }
+    }
+}
+
+static void
+android_fill_triangle (struct frame *f, struct android_gc *gc,
+                      struct android_point point1,
+                      struct android_point point2,
+                      struct android_point point3)
+{
+  struct android_point abc[3];
+
+  abc[0] = point1;
+  abc[1] = point2;
+  abc[2] = point3;
+
+  android_fill_polygon (FRAME_ANDROID_DRAWABLE (f),
+                       gc, abc, 3, ANDROID_CONVEX,
+                       ANDROID_COORD_MODE_ORIGIN);
+}
+
+static struct android_point
+android_make_point (int x, int y)
+{
+  struct android_point pt;
+
+  pt.x = x;
+  pt.y = y;
+
+  return pt;
+}
+
+static bool
+android_inside_rect_p (struct android_rectangle *rects, int nrects, int x,
+                      int y)
+{
+  int i;
+
+  for (i = 0; i < nrects; ++i)
+    {
+      if (x >= rects[i].x && y >= rects[i].y
+         && x < rects[i].x + rects[i].width
+         && y < rects[i].y + rects[i].height)
+       return true;
+    }
+
+  return false;
+}
+
+static void
+android_clear_point (struct frame *f, struct android_gc *gc,
+                    int x, int y)
+{
+  struct android_gc_values xgcv;
+
+  android_get_gc_values (gc, ANDROID_GC_BACKGROUND | ANDROID_GC_FOREGROUND,
+                        &xgcv);
+  android_set_foreground (gc, xgcv.background);
+  android_draw_point (FRAME_ANDROID_DRAWABLE (f), gc, x, y);
+  android_set_foreground (gc, xgcv.foreground);
+}
+
+static void
+android_draw_relief_rect (struct frame *f, int left_x, int top_y, int right_x,
+                         int bottom_y, int hwidth, int vwidth, bool raised_p,
+                         bool top_p, bool bot_p, bool left_p, bool right_p,
+                         struct android_rectangle *clip_rect)
+{
+  struct android_gc *gc, *white_gc, *black_gc, *normal_gc;
+  android_drawable drawable;
+
+  /* This code is more complicated than it has to be, because of two
+     minor hacks to make the boxes look nicer: (i) if width > 1, draw
+     the outermost line using the black relief.  (ii) Omit the four
+     corner pixels.  */
+
+  white_gc = f->output_data.android->white_relief.gc;
+  black_gc = f->output_data.android->black_relief.gc;
+  normal_gc = f->output_data.android->normal_gc;
+
+  drawable = FRAME_ANDROID_DRAWABLE (f);
+
+  android_set_clip_rectangles (white_gc, 0, 0, clip_rect, 1);
+  android_set_clip_rectangles (black_gc, 0, 0, clip_rect, 1);
+
+  if (raised_p)
+    gc = white_gc;
+  else
+    gc = black_gc;
+
+  /* Draw lines.  */
+
+  if (top_p)
+    android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x, top_y,
+                           right_x - left_x + 1, hwidth);
+
+  if (left_p)
+    android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x, top_y,
+                           vwidth, bottom_y - top_y + 1);
+
+  if (raised_p)
+    gc = black_gc;
+  else
+    gc = white_gc;
+
+  if (bot_p)
+    android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x,
+                           bottom_y - hwidth + 1,
+                           right_x - left_x + 1, hwidth);
+
+  if (right_p)
+    android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+                           right_x - vwidth + 1,
+                           top_y, vwidth, bottom_y - top_y + 1);
+
+  /* Draw corners.  */
+
+  if (bot_p && left_p)
+    android_fill_triangle (f, raised_p ? white_gc : black_gc,
+                          android_make_point (left_x, bottom_y - hwidth),
+                          android_make_point (left_x + vwidth,
+                                              bottom_y - hwidth),
+                          android_make_point (left_x, bottom_y));
+
+  if (top_p && right_p)
+    android_fill_triangle (f, raised_p ? white_gc : black_gc,
+                          android_make_point (right_x - vwidth, top_y),
+                          android_make_point (right_x, top_y),
+                          android_make_point (right_x - vwidth,
+                                              top_y + hwidth));
+
+  /* Draw outer line.  */
+
+  if (top_p && left_p && bot_p && right_p
+      && hwidth > 1 && vwidth > 1)
+    android_draw_rectangle (FRAME_ANDROID_DRAWABLE (f),
+                           black_gc, left_x, top_y,
+                           right_x - left_x, bottom_y - top_y);
+  else
+    {
+      if (top_p && hwidth > 1)
+       android_draw_line (drawable, black_gc, left_x, top_y,
+                          right_x + 1, top_y);
+
+      if (bot_p && hwidth > 1)
+       android_draw_line (drawable, black_gc, left_x, bottom_y,
+                          right_x + 1, bottom_y);
+
+      if (left_p && vwidth > 1)
+       android_draw_line (drawable, black_gc, left_x, top_y,
+                          left_x, bottom_y + 1);
+
+      if (right_p && vwidth > 1)
+       android_draw_line (drawable, black_gc, right_x, top_y,
+                          right_x, bottom_y + 1);
+    }
+
+  /* Erase corners.  */
+
+  if (hwidth > 1 && vwidth > 1)
+    {
+      if (left_p && top_p && android_inside_rect_p (clip_rect, 1,
+                                                   left_x, top_y))
+       android_clear_point (f, normal_gc, left_x, top_y);
+
+      if (left_p && bot_p && android_inside_rect_p (clip_rect, 1,
+                                                   left_x, bottom_y))
+       android_clear_point (f, normal_gc, left_x, bottom_y);
+
+      if (right_p && top_p && android_inside_rect_p (clip_rect, 1,
+                                                    right_x, top_y))
+       android_clear_point (f, normal_gc, right_x, top_y);
+
+      if (right_p && bot_p && android_inside_rect_p (clip_rect, 1,
+                                                    right_x, bottom_y))
+       android_clear_point (f, normal_gc, right_x, bottom_y);
+    }
+
+  android_reset_clip_rectangles (f, white_gc);
+  android_reset_clip_rectangles (f, black_gc);
+}
+
+static void
+android_draw_box_rect (struct glyph_string *s,
+                      int left_x, int top_y, int right_x, int bottom_y,
+                      int hwidth, int vwidth, bool left_p, bool right_p,
+                      struct android_rectangle *clip_rect)
+{
+  struct android_gc_values xgcv;
+
+  android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
+  android_set_foreground (s->gc, s->face->box_color);
+  android_set_clip_rectangles (s->gc, 0, 0, clip_rect, 1);
+
+  /* Top.  */
+  android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x,
+                         top_y, right_x - left_x + 1, hwidth);
+
+  /* Left.  */
+  if (left_p)
+    android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x,
+                           top_y, vwidth, bottom_y - top_y + 1);
+
+  /* Bottom.  */
+  android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x,
+                         bottom_y - hwidth + 1, right_x - left_x + 1,
+                         hwidth);
+
+  /* Right.  */
+  if (right_p)
+    android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+                           right_x - vwidth + 1, top_y, vwidth,
+                           bottom_y - top_y + 1);
+
+  android_set_foreground (s->gc, xgcv.foreground);
+  android_reset_clip_rectangles (s->f, s->gc);
+}
+
+#define HIGHLIGHT_COLOR_DARK_BOOST_LIMIT 48000
+
+static bool
+android_alloc_lighter_color (struct frame *f, unsigned long *pixel,
+                            double factor, int delta)
+{
+  Emacs_Color color, new;
+  long bright;
+  bool success_p;
+
+  /* Get RGB color values.  */
+  color.pixel = *pixel;
+  android_query_colors (f, &color, 1);
+
+  /* Change RGB values by specified FACTOR.  Avoid overflow!  */
+  eassert (factor >= 0);
+  new.red = min (0xffff, factor * color.red);
+  new.green = min (0xffff, factor * color.green);
+  new.blue = min (0xffff, factor * color.blue);
+
+  /* Calculate brightness of COLOR.  */
+  bright = (2 * color.red + 3 * color.green + color.blue) / 6;
+
+  /* We only boost colors that are darker than
+     HIGHLIGHT_COLOR_DARK_BOOST_LIMIT.  */
+  if (bright < HIGHLIGHT_COLOR_DARK_BOOST_LIMIT)
+    /* Make an additive adjustment to NEW, because it's dark enough so
+       that scaling by FACTOR alone isn't enough.  */
+    {
+      /* How far below the limit this color is (0 - 1, 1 being darker).  */
+      double dimness = 1 - (double) bright / HIGHLIGHT_COLOR_DARK_BOOST_LIMIT;
+      /* The additive adjustment.  */
+      int min_delta = delta * dimness * factor / 2;
+
+      if (factor < 1)
+       {
+         new.red =   max (0, new.red -   min_delta);
+         new.green = max (0, new.green - min_delta);
+         new.blue =  max (0, new.blue -  min_delta);
+       }
+      else
+       {
+         new.red =   min (0xffff, min_delta + new.red);
+         new.green = min (0xffff, min_delta + new.green);
+         new.blue =  min (0xffff, min_delta + new.blue);
+       }
+    }
+
+  /* Try to allocate the color.  */
+  success_p = android_alloc_nearest_color (f, &new);
+
+  if (success_p)
+    {
+      if (new.pixel == *pixel)
+       {
+         /* If we end up with the same color as before, try adding
+            delta to the RGB values.  */
+         new.red = min (0xffff, delta + color.red);
+         new.green = min (0xffff, delta + color.green);
+         new.blue = min (0xffff, delta + color.blue);
+         success_p = android_alloc_nearest_color (f, &new);
+       }
+      else
+       success_p = true;
+
+      *pixel = new.pixel;
+    }
+
+  return success_p;
+}
+
+/* Set up the foreground color for drawing relief lines of glyph
+   string S.  RELIEF is a pointer to a struct relief containing the GC
+   with which lines will be drawn.  Use a color that is FACTOR or
+   DELTA lighter or darker than the relief's background which is found
+   in S->f->output_data.android->relief_background.  If such a color
+   cannot be allocated, use DEFAULT_PIXEL, instead.  */
+
+static void
+android_setup_relief_color (struct frame *f, struct relief *relief,
+                           double factor, int delta,
+                           unsigned long default_pixel)
+{
+  struct android_gc_values xgcv;
+  struct android_output *di = f->output_data.android;
+  unsigned long mask = ANDROID_GC_FOREGROUND;
+  unsigned long pixel;
+  unsigned long background = di->relief_background;
+  struct android_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+
+  if (relief->gc && relief->pixel != -1)
+    relief->pixel = -1;
+
+  /* Allocate new color.  */
+  xgcv.foreground = default_pixel;
+  pixel = background;
+
+  if (dpyinfo->n_planes != 1
+      && android_alloc_lighter_color (f, &pixel, factor, delta))
+    xgcv.foreground = relief->pixel = pixel;
+
+  if (relief->gc == 0)
+    relief->gc = android_create_gc (mask, &xgcv);
+  else
+    android_change_gc (relief->gc, mask, &xgcv);
+}
+
+/* Set up colors for the relief lines around glyph string S.  */
+
+static void
+android_setup_relief_colors (struct glyph_string *s)
+{
+  struct android_output *di;
+  unsigned long color;
+
+  di = s->f->output_data.android;
+
+  if (s->face->use_box_color_for_shadows_p)
+    color = s->face->box_color;
+  else if (s->first_glyph->type == IMAGE_GLYPH
+          && s->img->pixmap
+          && !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0))
+    color = IMAGE_BACKGROUND (s->img, s->f, 0);
+  else
+    {
+      struct android_gc_values xgcv;
+
+      /* Get the background color of the face.  */
+      android_get_gc_values (s->gc, ANDROID_GC_BACKGROUND, &xgcv);
+      color = xgcv.background;
+    }
+
+  if (di->white_relief.gc == 0
+      || color != di->relief_background)
+    {
+      di->relief_background = color;
+      android_setup_relief_color (s->f, &di->white_relief, 1.2, 0x8000,
+                                 WHITE_PIX_DEFAULT (s->f));
+      android_setup_relief_color (s->f, &di->black_relief, 0.6, 0x4000,
+                                 BLACK_PIX_DEFAULT (s->f));
+    }
+}
+
+static void
+android_draw_glyph_string_box (struct glyph_string *s)
+{
+  int hwidth, vwidth, left_x, right_x, top_y, bottom_y, last_x;
+  bool raised_p, left_p, right_p;
+  struct glyph *last_glyph;
+  struct android_rectangle clip_rect;
+
+  last_x = ((s->row->full_width_p && !s->w->pseudo_window_p)
+           ? WINDOW_RIGHT_EDGE_X (s->w)
+           : window_box_right (s->w, s->area));
+
+  /* The glyph that may have a right box line.  For static
+     compositions and images, the right-box flag is on the first glyph
+     of the glyph string; for other types it's on the last glyph.  */
+  if (s->cmp || s->img)
+    last_glyph = s->first_glyph;
+  else if (s->first_glyph->type == COMPOSITE_GLYPH
+          && s->first_glyph->u.cmp.automatic)
+    {
+      /* For automatic compositions, we need to look up the last glyph
+        in the composition.  */
+        struct glyph *end = s->row->glyphs[s->area] + s->row->used[s->area];
+       struct glyph *g = s->first_glyph;
+       for (last_glyph = g++;
+            g < end && g->u.cmp.automatic && g->u.cmp.id == s->cmp_id
+              && g->slice.cmp.to < s->cmp_to;
+            last_glyph = g++)
+         ;
+    }
+  else
+    last_glyph = s->first_glyph + s->nchars - 1;
+
+  vwidth = eabs (s->face->box_vertical_line_width);
+  hwidth = eabs (s->face->box_horizontal_line_width);
+  raised_p = s->face->box == FACE_RAISED_BOX;
+  left_x = s->x;
+  right_x = (s->row->full_width_p && s->extends_to_end_of_line_p
+            ? last_x - 1
+            : min (last_x, s->x + s->background_width) - 1);
+  top_y = s->y;
+  bottom_y = top_y + s->height - 1;
+
+  left_p = (s->first_glyph->left_box_line_p
+           || (s->hl == DRAW_MOUSE_FACE
+               && (s->prev == NULL
+                   || s->prev->hl != s->hl)));
+  right_p = (last_glyph->right_box_line_p
+            || (s->hl == DRAW_MOUSE_FACE
+                && (s->next == NULL
+                    || s->next->hl != s->hl)));
+
+  get_glyph_string_clip_rect (s, &clip_rect);
+
+  if (s->face->box == FACE_SIMPLE_BOX)
+    android_draw_box_rect (s, left_x, top_y, right_x, bottom_y, hwidth,
+                          vwidth, left_p, right_p, &clip_rect);
+  else
+    {
+      android_setup_relief_colors (s);
+      android_draw_relief_rect (s->f, left_x, top_y, right_x, bottom_y, hwidth,
+                               vwidth, raised_p, true, true, left_p, right_p,
+                               &clip_rect);
+    }
+}
+
+static void
+android_draw_glyph_string_bg_rect (struct glyph_string *s, int x, int y,
+                                  int w, int h)
+{
+  if (s->stippled_p)
+    {
+      /* Fill background with a stipple pattern.  */
+      android_set_fill_style (s->gc, ANDROID_FILL_OPAQUE_STIPPLED);
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, x,
+                             y, w, h);
+      android_set_fill_style (s->gc, ANDROID_FILL_SOLID);
+    }
+  else
+    android_clear_glyph_string_rect (s, x, y, w, h);
+}
+
+static void
+android_draw_image_relief (struct glyph_string *s)
+{
+  int x1, y1, thick;
+  bool raised_p, top_p, bot_p, left_p, right_p;
+  int extra_x, extra_y;
+  struct android_rectangle r;
+  int x = s->x;
+  int y = s->ybase - image_ascent (s->img, s->face, &s->slice);
+
+  /* If first glyph of S has a left box line, start drawing it to the
+     right of that line.  */
+  if (s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p
+      && s->slice.x == 0)
+    x += max (s->face->box_vertical_line_width, 0);
+
+  /* If there is a margin around the image, adjust x- and y-position
+     by that margin.  */
+  if (s->slice.x == 0)
+    x += s->img->hmargin;
+  if (s->slice.y == 0)
+    y += s->img->vmargin;
+
+  if (s->hl == DRAW_IMAGE_SUNKEN
+      || s->hl == DRAW_IMAGE_RAISED)
+    {
+      if (s->face->id == TAB_BAR_FACE_ID)
+       thick = (tab_bar_button_relief < 0
+                ? DEFAULT_TAB_BAR_BUTTON_RELIEF
+                : min (tab_bar_button_relief, 1000000));
+      else
+       thick = (tool_bar_button_relief < 0
+                ? DEFAULT_TOOL_BAR_BUTTON_RELIEF
+                : min (tool_bar_button_relief, 1000000));
+      raised_p = s->hl == DRAW_IMAGE_RAISED;
+    }
+  else
+    {
+      thick = eabs (s->img->relief);
+      raised_p = s->img->relief > 0;
+    }
+
+  x1 = x + s->slice.width - 1;
+  y1 = y + s->slice.height - 1;
+
+  extra_x = extra_y = 0;
+  if (s->face->id == TAB_BAR_FACE_ID)
+    {
+      if (CONSP (Vtab_bar_button_margin)
+         && FIXNUMP (XCAR (Vtab_bar_button_margin))
+         && FIXNUMP (XCDR (Vtab_bar_button_margin)))
+       {
+         extra_x = XFIXNUM (XCAR (Vtab_bar_button_margin)) - thick;
+         extra_y = XFIXNUM (XCDR (Vtab_bar_button_margin)) - thick;
+       }
+      else if (FIXNUMP (Vtab_bar_button_margin))
+       extra_x = extra_y = XFIXNUM (Vtab_bar_button_margin) - thick;
+    }
+
+  if (s->face->id == TOOL_BAR_FACE_ID)
+    {
+      if (CONSP (Vtool_bar_button_margin)
+         && FIXNUMP (XCAR (Vtool_bar_button_margin))
+         && FIXNUMP (XCDR (Vtool_bar_button_margin)))
+       {
+         extra_x = XFIXNUM (XCAR (Vtool_bar_button_margin));
+         extra_y = XFIXNUM (XCDR (Vtool_bar_button_margin));
+       }
+      else if (FIXNUMP (Vtool_bar_button_margin))
+       extra_x = extra_y = XFIXNUM (Vtool_bar_button_margin);
+    }
+
+  top_p = bot_p = left_p = right_p = false;
+
+  if (s->slice.x == 0)
+    x -= thick + extra_x, left_p = true;
+  if (s->slice.y == 0)
+    y -= thick + extra_y, top_p = true;
+  if (s->slice.x + s->slice.width == s->img->width)
+    x1 += thick + extra_x, right_p = true;
+  if (s->slice.y + s->slice.height == s->img->height)
+    y1 += thick + extra_y, bot_p = true;
+
+  android_setup_relief_colors (s);
+  get_glyph_string_clip_rect (s, &r);
+  android_draw_relief_rect (s->f, x, y, x1, y1, thick, thick, raised_p,
+                           top_p, bot_p, left_p, right_p, &r);
+}
+
+static void
+android_draw_image_foreground (struct glyph_string *s)
+{
+  int x = s->x;
+  int y = s->ybase - image_ascent (s->img, s->face, &s->slice);
+
+  /* If first glyph of S has a left box line, start drawing it to the
+     right of that line.  */
+  if (s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p
+      && s->slice.x == 0)
+    x += max (s->face->box_vertical_line_width, 0);
+
+  /* If there is a margin around the image, adjust x- and y-position
+     by that margin.  */
+  if (s->slice.x == 0)
+    x += s->img->hmargin;
+  if (s->slice.y == 0)
+    y += s->img->vmargin;
+
+  if (s->img->pixmap)
+    {
+      unsigned long mask = (ANDROID_GC_CLIP_MASK
+                           | ANDROID_GC_CLIP_X_ORIGIN
+                           | ANDROID_GC_CLIP_Y_ORIGIN
+                           | ANDROID_GC_FUNCTION);
+      struct android_gc_values xgcv;
+      struct android_rectangle clip_rect, image_rect, r;
+
+      xgcv.clip_mask = s->img->mask;
+      xgcv.clip_x_origin = x - s->slice.x;
+      xgcv.clip_y_origin = y - s->slice.y;
+      xgcv.function = ANDROID_GC_COPY;
+      android_change_gc (s->gc, mask, &xgcv);
+
+      get_glyph_string_clip_rect (s, &clip_rect);
+      image_rect.x = x;
+      image_rect.y = y;
+      image_rect.width = s->slice.width;
+      image_rect.height = s->slice.height;
+
+      if (gui_intersect_rectangles (&clip_rect, &image_rect, &r))
+       android_copy_area (s->img->pixmap,
+                          FRAME_ANDROID_DRAWABLE (s->f),
+                          s->gc, s->slice.x + r.x - x,
+                          s->slice.y + r.y - y,
+                          r.width, r.height, r.x, r.y);
+
+      /* When the image has a mask, we can expect that at least part
+        of a mouse highlight or a block cursor will be visible.  If
+        the image doesn't have a mask, make a block cursor visible by
+        drawing a rectangle around the image.  I believe it's looking
+        better if we do nothing here for mouse-face.  */
+      if (s->hl == DRAW_CURSOR && !s->img->mask)
+       {
+         int relief = eabs (s->img->relief);
+         android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+                                 x - relief, y - relief,
+                                 s->slice.width + relief*2 - 1,
+                                 s->slice.height + relief*2 - 1);
+       }
+
+      android_set_clip_mask (s->gc, ANDROID_NONE);
+    }
+  else
+    /* Draw a rectangle if image could not be loaded.  */
+    android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, x, y,
+                           s->slice.width - 1, s->slice.height - 1);
+}
+
+static void
+android_draw_image_glyph_string (struct glyph_string *s)
+{
+  int box_line_hwidth = max (s->face->box_vertical_line_width, 0);
+  int box_line_vwidth = max (s->face->box_horizontal_line_width, 0);
+  int height;
+
+  height = s->height;
+  if (s->slice.y == 0)
+    height -= box_line_vwidth;
+  if (s->slice.y + s->slice.height >= s->img->height)
+    height -= box_line_vwidth;
+
+  /* Fill background with face under the image.  Do it only if row is
+     taller than image or if image has a clip mask to reduce
+     flickering.  */
+  s->stippled_p = s->face->stipple != 0;
+  if (height > s->slice.height
+      || s->img->hmargin
+      || s->img->vmargin
+      || s->img->mask
+      || s->img->pixmap == 0
+      || s->width != s->background_width)
+    {
+      if (s->stippled_p)
+       s->row->stipple_p = true;
+
+      int x = s->x;
+      int y = s->y;
+      int width = s->background_width;
+
+      if (s->first_glyph->left_box_line_p
+         && s->slice.x == 0)
+       {
+         x += box_line_hwidth;
+         width -= box_line_hwidth;
+       }
+
+      if (s->slice.y == 0)
+       y += box_line_vwidth;
+
+      android_draw_glyph_string_bg_rect (s, x, y, width, height);
+
+      s->background_filled_p = true;
+    }
+
+  /* Draw the foreground.  */
+  android_draw_image_foreground (s);
+  android_set_glyph_string_clipping (s);
+
+  /* If we must draw a relief around the image, do it.  */
+  if (s->img->relief
+      || s->hl == DRAW_IMAGE_RAISED
+      || s->hl == DRAW_IMAGE_SUNKEN)
+    android_draw_image_relief (s);
+}
+
+static void
+android_draw_stretch_glyph_string (struct glyph_string *s)
+{
+  eassert (s->first_glyph->type == STRETCH_GLYPH);
+
+  if (s->hl == DRAW_CURSOR && !x_stretch_cursor_p)
+    {
+      /* If `x-stretch-cursor' is nil, don't draw a block cursor as
+        wide as the stretch glyph.  */
+      int width, background_width = s->background_width;
+      int x = s->x;
+
+      if (!s->row->reversed_p)
+       {
+         int left_x = window_box_left_offset (s->w, TEXT_AREA);
+
+         if (x < left_x)
+           {
+             background_width -= left_x - x;
+             x = left_x;
+           }
+       }
+      else
+       {
+         /* In R2L rows, draw the cursor on the right edge of the
+            stretch glyph.  */
+         int right_x = window_box_right (s->w, TEXT_AREA);
+
+         if (x + background_width > right_x)
+           background_width -= x - right_x;
+         x += background_width;
+       }
+      width = min (FRAME_COLUMN_WIDTH (s->f), background_width);
+      if (s->row->reversed_p)
+       x -= width;
+
+      /* Draw cursor.  */
+      android_draw_glyph_string_bg_rect (s, x, s->y, width, s->height);
+
+      /* Clear rest using the GC of the original non-cursor face.  */
+      if (width < background_width)
+       {
+         int y = s->y;
+         int w = background_width - width, h = s->height;
+         struct android_rectangle r;
+         struct android_gc *gc;
+
+         if (!s->row->reversed_p)
+           x += width;
+         else
+           x = s->x;
+         if (s->row->mouse_face_p
+             && cursor_in_mouse_face_p (s->w))
+           {
+             android_set_mouse_face_gc (s);
+             gc = s->gc;
+           }
+         else
+           gc = s->face->gc;
+
+         get_glyph_string_clip_rect (s, &r);
+         android_set_clip_rectangles (gc, 0, 0, &r, 1);
+
+         if (s->face->stipple)
+           {
+             /* Fill background with a stipple pattern.  */
+             android_set_fill_style (gc, ANDROID_FILL_OPAQUE_STIPPLED);
+             android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
+                                     gc, x, y, w, h);
+             android_set_fill_style (gc, ANDROID_FILL_SOLID);
+
+             s->row->stipple_p = true;
+           }
+         else
+           {
+             struct android_gc_values xgcv;
+             android_get_gc_values (gc, (ANDROID_GC_FOREGROUND
+                                         | ANDROID_GC_BACKGROUND),
+                                    &xgcv);
+             android_set_foreground (gc, xgcv.background);
+             android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
+                                     gc, x, y, w, h);
+             android_set_foreground (gc, xgcv.foreground);
+           }
+
+         android_reset_clip_rectangles (s->f, gc);
+       }
+    }
+  else if (!s->background_filled_p)
+    {
+      int background_width = s->background_width;
+      int x = s->x, text_left_x = window_box_left (s->w, TEXT_AREA);
+
+      /* Don't draw into left fringe or scrollbar area except for
+         header line and mode line.  */
+      if (s->area == TEXT_AREA
+         && x < text_left_x && !s->row->mode_line_p)
+       {
+         background_width -= text_left_x - x;
+         x = text_left_x;
+       }
+
+      if (!s->row->stipple_p)
+       s->row->stipple_p = s->stippled_p;
+
+      if (background_width > 0)
+       android_draw_glyph_string_bg_rect (s, x, s->y,
+                                          background_width,
+                                          s->height);
+    }
+
+  s->background_filled_p = true;
+}
+
+static void
+android_get_scale_factor (int *scale_x, int *scale_y)
+{
+  /* This is 96 everywhere else, but 160 on Android.  */
+  const int base_res = 160;
+  struct android_display_info *dpyinfo;
+
+  dpyinfo = x_display_list;
+  *scale_x = *scale_y = 1;
+
+  if (dpyinfo)
+    {
+      if (dpyinfo->resx > base_res)
+       *scale_x = floor (dpyinfo->resx / base_res);
+      if (dpyinfo->resy > base_res)
+       *scale_y = floor (dpyinfo->resy / base_res);
+    }
+}
+
+static void
+android_draw_underwave (struct glyph_string *s, int decoration_width)
+{
+  int scale_x, scale_y;
+
+  android_get_scale_factor (&scale_x, &scale_y);
+
+  int wave_height = 3 * scale_y, wave_length = 2 * scale_x;
+
+  int dx, dy, x0, y0, width, x1, y1, x2, y2, xmax;
+  bool odd;
+  struct android_rectangle wave_clip, string_clip, final_clip;
+
+  dx = wave_length;
+  dy = wave_height - 1;
+  x0 = s->x;
+  y0 = s->ybase + wave_height / 2;
+  width = decoration_width;
+  xmax = x0 + width;
+
+  /* Find and set clipping rectangle */
+
+  wave_clip.x = x0;
+  wave_clip.y = y0;
+  wave_clip.width = width;
+  wave_clip.height = wave_height;
+  get_glyph_string_clip_rect (s, &string_clip);
+
+  if (!gui_intersect_rectangles (&wave_clip, &string_clip, &final_clip))
+    return;
+
+  android_set_clip_rectangles (s->gc, 0, 0, &final_clip, 1);
+
+  /* Draw the waves */
+
+  x1 = x0 - (x0 % dx);
+  x2 = x1 + dx;
+  odd = (x1 / dx) & 1;
+  y1 = y2 = y0;
+
+  if (odd)
+    y1 += dy;
+  else
+    y2 += dy;
+
+  if (INT_MAX - dx < xmax)
+    emacs_abort ();
+
+  while (x1 <= xmax)
+    {
+      android_draw_line (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+                        x1, y1, x2, y2);
+      x1  = x2, y1 = y2;
+      x2 += dx, y2 = y0 + odd*dy;
+      odd = !odd;
+    }
+
+  /* Restore previous clipping rectangle(s) */
+  android_set_clip_rectangles (s->gc, 0, 0, s->clip, s->num_clips);
+}
+
+static void
+android_draw_glyph_string_foreground (struct glyph_string *s)
+{
+  int i, x;
+
+  /* If first glyph of S has a left box line, start drawing the text
+     of S to the right of that box line.  */
+  if (s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p)
+    x = s->x + max (s->face->box_vertical_line_width, 0);
+  else
+    x = s->x;
+
+  /* Draw characters of S as rectangles if S's font could not be
+     loaded.  */
+  if (s->font_not_found_p)
+    {
+      for (i = 0; i < s->nchars; ++i)
+       {
+         struct glyph *g = s->first_glyph + i;
+         android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
+                                 s->gc, x, s->y,
+                                 g->pixel_width - 1,
+                                 s->height - 1);
+         x += g->pixel_width;
+       }
+    }
+  else
+    {
+      struct font *font = s->font;
+      int boff = font->baseline_offset;
+      int y;
+
+      if (font->vertical_centering)
+       boff = VCENTER_BASELINE_OFFSET (font, s->f) - boff;
+
+      y = s->ybase - boff;
+      if (s->for_overlaps
+         || (s->background_filled_p && s->hl != DRAW_CURSOR))
+       font->driver->draw (s, 0, s->nchars, x, y, false);
+      else
+       font->driver->draw (s, 0, s->nchars, x, y, true);
+      if (s->face->overstrike)
+       font->driver->draw (s, 0, s->nchars, x + 1, y, false);
+    }
+}
+
+static void
+android_draw_composite_glyph_string_foreground (struct glyph_string *s)
+{
+  int i, j, x;
+  struct font *font = s->font;
+
+  /* If first glyph of S has a left box line, start drawing the text
+     of S to the right of that box line.  */
+  if (s->face && s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p)
+    x = s->x + max (s->face->box_vertical_line_width, 0);
+  else
+    x = s->x;
+
+  /* S is a glyph string for a composition.  S->cmp_from is the index
+     of the first character drawn for glyphs of this composition.
+     S->cmp_from == 0 means we are drawing the very first character of
+     this composition.  */
+
+  /* Draw a rectangle for the composition if the font for the very
+     first character of the composition could not be loaded.  */
+  if (s->font_not_found_p)
+    {
+      if (s->cmp_from == 0)
+       android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
+                               s->gc, x, s->y,
+                               s->width - 1, s->height - 1);
+    }
+  else if (! s->first_glyph->u.cmp.automatic)
+    {
+      int y = s->ybase;
+
+      for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++)
+       /* TAB in a composition means display glyphs with
+          padding space on the left or right.  */
+       if (COMPOSITION_GLYPH (s->cmp, j) != '\t')
+         {
+           int xx = x + s->cmp->offsets[j * 2];
+           int yy = y - s->cmp->offsets[j * 2 + 1];
+
+           font->driver->draw (s, j, j + 1, xx, yy, false);
+           if (s->face->overstrike)
+             font->driver->draw (s, j, j + 1, xx + 1, yy, false);
+         }
+    }
+  else
+    {
+      Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
+      Lisp_Object glyph;
+      int y = s->ybase;
+      int width = 0;
+
+      for (i = j = s->cmp_from; i < s->cmp_to; i++)
+       {
+         glyph = LGSTRING_GLYPH (gstring, i);
+         if (NILP (LGLYPH_ADJUSTMENT (glyph)))
+           width += LGLYPH_WIDTH (glyph);
+         else
+           {
+             int xoff, yoff, wadjust;
+
+             if (j < i)
+               {
+                 font->driver->draw (s, j, i, x, y, false);
+                 if (s->face->overstrike)
+                   font->driver->draw (s, j, i, x + 1, y, false);
+                 x += width;
+               }
+             xoff = LGLYPH_XOFF (glyph);
+             yoff = LGLYPH_YOFF (glyph);
+             wadjust = LGLYPH_WADJUST (glyph);
+             font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false);
+             if (s->face->overstrike)
+               font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff,
+                                   false);
+             x += wadjust;
+             j = i + 1;
+             width = 0;
+           }
+       }
+      if (j < i)
+       {
+         font->driver->draw (s, j, i, x, y, false);
+         if (s->face->overstrike)
+           font->driver->draw (s, j, i, x + 1, y, false);
+       }
+    }
+}
+
+static void
+android_draw_glyphless_glyph_string_foreground (struct glyph_string *s)
+{
+  struct glyph *glyph = s->first_glyph;
+  unsigned char2b[8];
+  int x, i, j;
+
+  /* If first glyph of S has a left box line, start drawing the text
+     of S to the right of that box line.  */
+  if (s->face && s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p)
+    x = s->x + max (s->face->box_vertical_line_width, 0);
+  else
+    x = s->x;
+
+  s->char2b = char2b;
+
+  for (i = 0; i < s->nchars; i++, glyph++)
+    {
+#ifdef GCC_LINT
+      enum { PACIFY_GCC_BUG_81401 = 1 };
+#else
+      enum { PACIFY_GCC_BUG_81401 = 0 };
+#endif
+      char buf[7 + PACIFY_GCC_BUG_81401];
+      char *str = NULL;
+      int len = glyph->u.glyphless.len;
+
+      if (glyph->u.glyphless.method == GLYPHLESS_DISPLAY_ACRONYM)
+       {
+         if (len > 0
+             && CHAR_TABLE_P (Vglyphless_char_display)
+             && (CHAR_TABLE_EXTRA_SLOTS (XCHAR_TABLE (Vglyphless_char_display))
+                 >= 1))
+           {
+             Lisp_Object acronym
+               = (! glyph->u.glyphless.for_no_font
+                  ? CHAR_TABLE_REF (Vglyphless_char_display,
+                                    glyph->u.glyphless.ch)
+                  : XCHAR_TABLE (Vglyphless_char_display)->extras[0]);
+             if (CONSP (acronym))
+               acronym = XCAR (acronym);
+             if (STRINGP (acronym))
+               str = SSDATA (acronym);
+           }
+       }
+      else if (glyph->u.glyphless.method == GLYPHLESS_DISPLAY_HEX_CODE)
+       {
+         unsigned int ch = glyph->u.glyphless.ch;
+         eassume (ch <= MAX_CHAR);
+         sprintf (buf, "%0*X", ch < 0x10000 ? 4 : 6, ch);
+         str = buf;
+       }
+
+      if (str)
+       {
+         int upper_len = (len + 1) / 2;
+
+         /* It is assured that all LEN characters in STR is ASCII.  */
+         for (j = 0; j < len; j++)
+            char2b[j] = s->font->driver->encode_char (s->font, str[j]) & 
0xFFFF;
+         s->font->driver->draw (s, 0, upper_len,
+                                x + glyph->slice.glyphless.upper_xoff,
+                                s->ybase + glyph->slice.glyphless.upper_yoff,
+                                false);
+         s->font->driver->draw (s, upper_len, len,
+                                x + glyph->slice.glyphless.lower_xoff,
+                                s->ybase + glyph->slice.glyphless.lower_yoff,
+                                false);
+       }
+      if (glyph->u.glyphless.method != GLYPHLESS_DISPLAY_THIN_SPACE)
+       android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+                               x, s->ybase - glyph->ascent,
+                               glyph->pixel_width - 1,
+                               glyph->ascent + glyph->descent - 1);
+      x += glyph->pixel_width;
+   }
+
+  /* Defend against hypothetical bad code elsewhere that uses
+     s->char2b after this function returns.  */
+  s->char2b = NULL;
+}
+
+static void
+android_draw_glyph_string (struct glyph_string *s)
+{
+  bool relief_drawn_p = false;
+
+  /* If S draws into the background of its successors, draw the
+     background of the successors first so that S can draw into it.
+     This makes S->next use XDrawString instead of XDrawImageString.  */
+  if (s->next && s->right_overhang && !s->for_overlaps)
+    {
+      int width;
+      struct glyph_string *next;
+
+      for (width = 0, next = s->next;
+          next && width < s->right_overhang;
+          width += next->width, next = next->next)
+       if (next->first_glyph->type != IMAGE_GLYPH)
+         {
+           android_set_glyph_string_gc (next);
+           android_set_glyph_string_clipping (next);
+           if (next->first_glyph->type == STRETCH_GLYPH)
+             android_draw_stretch_glyph_string (next);
+           else
+             android_draw_glyph_string_background (next, true);
+           next->num_clips = 0;
+         }
+    }
+
+  /* Set up S->gc, set clipping and draw S.  */
+  android_set_glyph_string_gc (s);
+
+  /* Draw relief (if any) in advance for char/composition so that the
+     glyph string can be drawn over it.  */
+  if (!s->for_overlaps
+      && s->face->box != FACE_NO_BOX
+      && (s->first_glyph->type == CHAR_GLYPH
+         || s->first_glyph->type == COMPOSITE_GLYPH))
+
+    {
+      android_set_glyph_string_clipping (s);
+      android_draw_glyph_string_background (s, true);
+      android_draw_glyph_string_box (s);
+      android_set_glyph_string_clipping (s);
+      relief_drawn_p = true;
+    }
+  else if (!s->clip_head /* draw_glyphs didn't specify a clip mask. */
+          && !s->clip_tail
+          && ((s->prev && s->prev->hl != s->hl && s->left_overhang)
+              || (s->next && s->next->hl != s->hl && s->right_overhang)))
+    /* We must clip just this glyph.  left_overhang part has already
+       drawn when s->prev was drawn, and right_overhang part will be
+       drawn later when s->next is drawn. */
+    android_set_glyph_string_clipping_exactly (s, s);
+  else
+    android_set_glyph_string_clipping (s);
+
+  switch (s->first_glyph->type)
+    {
+    case IMAGE_GLYPH:
+      android_draw_image_glyph_string (s);
+      break;
+
+    case XWIDGET_GLYPH:
+      emacs_abort ();
+      break;
+
+    case STRETCH_GLYPH:
+      android_draw_stretch_glyph_string (s);
+      break;
+
+    case CHAR_GLYPH:
+      if (s->for_overlaps)
+       s->background_filled_p = true;
+      else
+       android_draw_glyph_string_background (s, false);
+      android_draw_glyph_string_foreground (s);
+      break;
+
+    case COMPOSITE_GLYPH:
+      if (s->for_overlaps || (s->cmp_from > 0
+                             && ! s->first_glyph->u.cmp.automatic))
+       s->background_filled_p = true;
+      else
+       android_draw_glyph_string_background (s, true);
+      android_draw_composite_glyph_string_foreground (s);
+      break;
+
+    case GLYPHLESS_GLYPH:
+      if (s->for_overlaps)
+       s->background_filled_p = true;
+      else
+       android_draw_glyph_string_background (s, true);
+      android_draw_glyphless_glyph_string_foreground (s);
+      break;
+
+    default:
+      emacs_abort ();
+    }
+
+  if (!s->for_overlaps)
+    {
+      int area_x, area_y, area_width, area_height;
+      int area_max_x, decoration_width;
+
+      /* Prevent the underline from overwriting surrounding areas
+        and the fringe.  */
+      window_box (s->w, s->area, &area_x, &area_y,
+                 &area_width, &area_height);
+      area_max_x = area_x + area_width - 1;
+
+      decoration_width = s->width;
+      if (!s->row->mode_line_p
+         && !s->row->tab_line_p
+         && area_max_x < (s->x + decoration_width - 1))
+       decoration_width -= (s->x + decoration_width - 1) - area_max_x;
+
+      /* Draw relief if not yet drawn.  */
+      if (!relief_drawn_p && s->face->box != FACE_NO_BOX)
+       android_draw_glyph_string_box (s);
+
+      /* Draw underline.  */
+      if (s->face->underline)
+        {
+          if (s->face->underline == FACE_UNDER_WAVE)
+            {
+              if (s->face->underline_defaulted_p)
+                android_draw_underwave (s, decoration_width);
+              else
+                {
+                  struct android_gc_values xgcv;
+                  android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
+                  android_set_foreground (s->gc, s->face->underline_color);
+                  android_draw_underwave (s, decoration_width);
+                  android_set_foreground (s->gc, xgcv.foreground);
+                }
+            }
+          else if (s->face->underline == FACE_UNDER_LINE)
+            {
+              unsigned long thickness, position;
+              int y;
+
+              if (s->prev
+                 && s->prev->face->underline == FACE_UNDER_LINE
+                 && (s->prev->face->underline_at_descent_line_p
+                     == s->face->underline_at_descent_line_p)
+                 && (s->prev->face->underline_pixels_above_descent_line
+                     == s->face->underline_pixels_above_descent_line))
+                {
+                  /* We use the same underline style as the previous one.  */
+                  thickness = s->prev->underline_thickness;
+                  position = s->prev->underline_position;
+                }
+              else
+                {
+                 struct font *font = font_for_underline_metrics (s);
+                 unsigned long minimum_offset;
+                 bool underline_at_descent_line;
+                 bool use_underline_position_properties;
+                 Lisp_Object val = (WINDOW_BUFFER_LOCAL_VALUE
+                                    (Qunderline_minimum_offset, s->w));
+
+                 if (FIXNUMP (val))
+                   minimum_offset = max (0, XFIXNUM (val));
+                 else
+                   minimum_offset = 1;
+
+                 val = (WINDOW_BUFFER_LOCAL_VALUE
+                        (Qx_underline_at_descent_line, s->w));
+                 underline_at_descent_line
+                   = (!(NILP (val) || BASE_EQ (val, Qunbound))
+                      || s->face->underline_at_descent_line_p);
+
+                 val = (WINDOW_BUFFER_LOCAL_VALUE
+                        (Qx_use_underline_position_properties, s->w));
+                 use_underline_position_properties
+                   = !(NILP (val) || BASE_EQ (val, Qunbound));
+
+                  /* Get the underline thickness.  Default is 1 pixel.  */
+                  if (font && font->underline_thickness > 0)
+                    thickness = font->underline_thickness;
+                  else
+                    thickness = 1;
+                  if (underline_at_descent_line)
+                   position = ((s->height - thickness)
+                               - (s->ybase - s->y)
+                               - s->face->underline_pixels_above_descent_line);
+                  else
+                    {
+                      /* Get the underline position.  This is the
+                         recommended vertical offset in pixels from
+                         the baseline to the top of the underline.
+                         This is a signed value according to the
+                         specs, and its default is
+
+                         ROUND ((maximum descent) / 2), with
+                         ROUND(x) = floor (x + 0.5)  */
+
+                      if (use_underline_position_properties
+                          && font && font->underline_position >= 0)
+                        position = font->underline_position;
+                      else if (font)
+                        position = (font->descent + 1) / 2;
+                      else
+                        position = minimum_offset;
+                    }
+
+                 /* Ignore minimum_offset if the amount of pixels was
+                    explicitly specified.  */
+                 if (!s->face->underline_pixels_above_descent_line)
+                   position = max (position, minimum_offset);
+                }
+              /* Check the sanity of thickness and position.  We should
+                 avoid drawing underline out of the current line area.  */
+             if (s->y + s->height <= s->ybase + position)
+               position = (s->height - 1) - (s->ybase - s->y);
+              if (s->y + s->height < s->ybase + position + thickness)
+                thickness = (s->y + s->height) - (s->ybase + position);
+              s->underline_thickness = thickness;
+              s->underline_position = position;
+              y = s->ybase + position;
+              if (s->face->underline_defaulted_p)
+                android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+                                       s->x, y, decoration_width, thickness);
+              else
+                {
+                  struct android_gc_values xgcv;
+                  android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
+                  android_set_foreground (s->gc, s->face->underline_color);
+                  android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+                                         s->x, y, decoration_width, thickness);
+                  android_set_foreground (s->gc, xgcv.foreground);
+                }
+            }
+        }
+      /* Draw overline.  */
+      if (s->face->overline_p)
+       {
+         unsigned long dy = 0, h = 1;
+
+         if (s->face->overline_color_defaulted_p)
+           android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
+                                   s->gc, s->x, s->y + dy,
+                                   decoration_width, h);
+         else
+           {
+             struct android_gc_values xgcv;
+             android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
+             android_set_foreground (s->gc, s->face->overline_color);
+             android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+                                     s->x, s->y + dy, decoration_width, h);
+             android_set_foreground (s->gc, xgcv.foreground);
+           }
+       }
+
+      /* Draw strike-through.  */
+      if (s->face->strike_through_p)
+       {
+         /* Y-coordinate and height of the glyph string's first
+            glyph.  We cannot use s->y and s->height because those
+            could be larger if there are taller display elements
+            (e.g., characters displayed with a larger font) in the
+            same glyph row.  */
+         int glyph_y = s->ybase - s->first_glyph->ascent;
+         int glyph_height = s->first_glyph->ascent + s->first_glyph->descent;
+         /* Strike-through width and offset from the glyph string's
+            top edge.  */
+          unsigned long h = 1;
+          unsigned long dy = (glyph_height - h) / 2;
+
+         if (s->face->strike_through_color_defaulted_p)
+           android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f),
+                                   s->gc, s->x, glyph_y + dy,
+                                   s->width, h);
+         else
+           {
+             struct android_gc_values xgcv;
+             android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
+             android_set_foreground (s->gc, s->face->strike_through_color);
+             android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+                                     s->x, glyph_y + dy, decoration_width,
+                                     h);
+             android_set_foreground (s->gc, xgcv.foreground);
+           }
+       }
+
+      if (s->prev)
+       {
+         struct glyph_string *prev;
+
+         for (prev = s->prev; prev; prev = prev->prev)
+           if (prev->hl != s->hl
+               && prev->x + prev->width + prev->right_overhang > s->x)
+             {
+               /* As prev was drawn while clipped to its own area, we
+                  must draw the right_overhang part using s->hl now.  */
+               enum draw_glyphs_face save = prev->hl;
+
+               prev->hl = s->hl;
+               android_set_glyph_string_gc (prev);
+               android_set_glyph_string_clipping_exactly (s, prev);
+               if (prev->first_glyph->type == CHAR_GLYPH)
+                 android_draw_glyph_string_foreground (prev);
+               else
+                 android_draw_composite_glyph_string_foreground (prev);
+               android_reset_clip_rectangles (prev->f, prev->gc);
+               prev->hl = save;
+               prev->num_clips = 0;
+             }
+       }
+
+      if (s->next)
+       {
+         struct glyph_string *next;
+
+         for (next = s->next; next; next = next->next)
+           if (next->hl != s->hl
+               && next->x - next->left_overhang < s->x + s->width)
+             {
+               /* As next will be drawn while clipped to its own area,
+                  we must draw the left_overhang part using s->hl now.  */
+               enum draw_glyphs_face save = next->hl;
+
+               next->hl = s->hl;
+               android_set_glyph_string_gc (next);
+               android_set_glyph_string_clipping_exactly (s, next);
+               if (next->first_glyph->type == CHAR_GLYPH)
+                 android_draw_glyph_string_foreground (next);
+               else
+                 android_draw_composite_glyph_string_foreground (next);
+               android_reset_clip_rectangles (next->f, next->gc);
+               next->hl = save;
+               next->num_clips = 0;
+               next->clip_head = s->next;
+             }
+       }
+    }
+
+  /* Reset clipping.  */
+  android_reset_clip_rectangles (s->f, s->gc);
+  s->num_clips = 0;
+
+  /* Set the stippled flag that tells redisplay whether or not a
+     stipple was actually draw.  */
+
+  if (s->first_glyph->type != STRETCH_GLYPH
+      && s->first_glyph->type != IMAGE_GLYPH
+      && !s->row->stipple_p)
+    s->row->stipple_p = s->stippled_p;
+}
+
+static void
+android_define_frame_cursor (struct frame *f, Emacs_Cursor cursor)
+{
+  if (!f->pointer_invisible
+      && !FRAME_ANDROID_OUTPUT (f)->hourglass
+      && f->output_data.android->current_cursor != cursor)
+    android_define_cursor (FRAME_ANDROID_WINDOW (f), cursor);
+
+  f->output_data.android->current_cursor = cursor;
+}
+
+static void
+android_clear_frame_area (struct frame *f, int x, int y,
+                         int width, int height)
+{
+  android_clear_area (FRAME_ANDROID_DRAWABLE (f),
+                     x, y, width, height);
+}
+
+void
+android_clear_under_internal_border (struct frame *f)
+{
+  if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0)
+    {
+      int border = FRAME_INTERNAL_BORDER_WIDTH (f);
+      int width = FRAME_PIXEL_WIDTH (f);
+      int height = FRAME_PIXEL_HEIGHT (f);
+      int margin = FRAME_TOP_MARGIN_HEIGHT (f);
+      int bottom_margin = FRAME_BOTTOM_MARGIN_HEIGHT (f);
+      int face_id = (FRAME_PARENT_FRAME (f)
+                    ? (!NILP (Vface_remapping_alist)
+                       ? lookup_basic_face (NULL, f,
+                                            CHILD_FRAME_BORDER_FACE_ID)
+                       : CHILD_FRAME_BORDER_FACE_ID)
+                    : (!NILP (Vface_remapping_alist)
+                       ? lookup_basic_face (NULL, f,
+                                            INTERNAL_BORDER_FACE_ID)
+                       : INTERNAL_BORDER_FACE_ID));
+      struct face *face = FACE_FROM_ID_OR_NULL (f, face_id);
+
+      if (face)
+       {
+         unsigned long color = face->background;
+         struct android_gc *gc = f->output_data.android->normal_gc;
+
+         android_set_foreground (gc, color);
+         android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0, margin,
+                                 width, border);
+         android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0, 0,
+                                 border, height);
+         android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, width - 
border,
+                                 0, border, height);
+         android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0,
+                                 height - bottom_margin - border,
+                                 width, border);
+         android_set_foreground (gc, FRAME_FOREGROUND_PIXEL (f));
+       }
+      else
+       {
+         android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0, 0,
+                             border, height);
+         android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0,
+                             margin, width, border);
+         android_clear_area (FRAME_ANDROID_DRAWABLE (f), width - border,
+                             0, border, height);
+         android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0,
+                             height - bottom_margin - border,
+                             width, border);
+       }
+    }
+}
+
+static void
+android_draw_hollow_cursor (struct window *w, struct glyph_row *row)
+{
+  struct frame *f = XFRAME (WINDOW_FRAME (w));
+  struct android_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+  int x, y, wd, h;
+  struct android_gc_values xgcv;
+  struct glyph *cursor_glyph;
+  struct android_gc *gc;
+
+  /* Get the glyph the cursor is on.  If we can't tell because
+     the current matrix is invalid or such, give up.  */
+  cursor_glyph = get_phys_cursor_glyph (w);
+  if (cursor_glyph == NULL)
+    return;
+
+  /* Compute frame-relative coordinates for phys cursor.  */
+  get_phys_cursor_geometry (w, row, cursor_glyph, &x, &y, &h);
+  wd = w->phys_cursor_width - 1;
+
+  /* The foreground of cursor_gc is typically the same as the normal
+     background color, which can cause the cursor box to be invisible.  */
+  xgcv.foreground = f->output_data.android->cursor_pixel;
+  if (dpyinfo->scratch_cursor_gc)
+    android_change_gc (dpyinfo->scratch_cursor_gc,
+                      ANDROID_GC_FOREGROUND, &xgcv);
+  else
+    dpyinfo->scratch_cursor_gc
+      =  android_create_gc (ANDROID_GC_FOREGROUND, &xgcv);
+  gc = dpyinfo->scratch_cursor_gc;
+
+  /* When on R2L character, show cursor at the right edge of the
+     glyph, unless the cursor box is as wide as the glyph or wider
+     (the latter happens when x-stretch-cursor is non-nil).  */
+  if ((cursor_glyph->resolved_level & 1) != 0
+      && cursor_glyph->pixel_width > wd)
+    {
+      x += cursor_glyph->pixel_width - wd;
+      if (wd > 0)
+       wd -= 1;
+    }
+  /* Set clipping, draw the rectangle, and reset clipping again.  */
+  android_clip_to_row (w, row, TEXT_AREA, gc);
+  android_draw_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x, y, wd, h - 1);
+  android_reset_clip_rectangles (f, gc);
+}
+
+static void
+android_draw_bar_cursor (struct window *w, struct glyph_row *row, int width,
+                        enum text_cursor_kinds kind)
+{
+  struct frame *f = XFRAME (w->frame);
+  struct glyph *cursor_glyph;
+  int cursor_start_y;
+
+  /* If cursor is out of bounds, don't draw garbage.  This can happen
+     in mini-buffer windows when switching between echo area glyphs
+     and mini-buffer.  */
+  cursor_glyph = get_phys_cursor_glyph (w);
+  if (cursor_glyph == NULL)
+    return;
+
+  /* Experimental avoidance of cursor on xwidget.  */
+  if (cursor_glyph->type == XWIDGET_GLYPH)
+    return;
+
+  /* If on an image, draw like a normal cursor.  That's usually better
+     visible than drawing a bar, esp. if the image is large so that
+     the bar might not be in the window.  */
+  if (cursor_glyph->type == IMAGE_GLYPH)
+    {
+      struct glyph_row *r;
+      r = MATRIX_ROW (w->current_matrix, w->phys_cursor.vpos);
+      draw_phys_cursor_glyph (w, r, DRAW_CURSOR);
+    }
+  else
+    {
+      struct android_gc *gc = FRAME_DISPLAY_INFO (f)->scratch_cursor_gc;
+      unsigned long mask = ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND;
+      struct face *face = FACE_FROM_ID (f, cursor_glyph->face_id);
+      struct android_gc_values xgcv;
+
+      /* If the glyph's background equals the color we normally draw
+        the bars cursor in, the bar cursor in its normal color is
+        invisible.  Use the glyph's foreground color instead in this
+        case, on the assumption that the glyph's colors are chosen so
+        that the glyph is legible.  */
+      if (face->background == f->output_data.android->cursor_pixel)
+       xgcv.background = xgcv.foreground = face->foreground;
+      else
+       xgcv.background = xgcv.foreground = 
f->output_data.android->cursor_pixel;
+
+      if (gc)
+       android_change_gc (gc, mask, &xgcv);
+      else
+       {
+          gc = android_create_gc (mask, &xgcv);
+         FRAME_DISPLAY_INFO (f)->scratch_cursor_gc = gc;
+       }
+
+      android_clip_to_row (w, row, TEXT_AREA, gc);
+
+      if (kind == BAR_CURSOR)
+       {
+         int x = WINDOW_TEXT_TO_FRAME_PIXEL_X (w, w->phys_cursor.x);
+
+         if (width < 0)
+           width = FRAME_CURSOR_WIDTH (f);
+         width = min (cursor_glyph->pixel_width, width);
+
+         w->phys_cursor_width = width;
+
+         /* If the character under cursor is R2L, draw the bar cursor
+            on the right of its glyph, rather than on the left.  */
+         if ((cursor_glyph->resolved_level & 1) != 0)
+           x += cursor_glyph->pixel_width - width;
+
+         android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x,
+                                 WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y),
+                                 width, row->height);
+       }
+      else /* HBAR_CURSOR */
+       {
+         int dummy_x, dummy_y, dummy_h;
+         int x = WINDOW_TEXT_TO_FRAME_PIXEL_X (w, w->phys_cursor.x);
+
+         if (width < 0)
+           width = row->height;
+
+         width = min (row->height, width);
+
+         get_phys_cursor_geometry (w, row, cursor_glyph, &dummy_x,
+                                   &dummy_y, &dummy_h);
+
+         cursor_start_y = WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y
+                                                   + row->height - width);
+
+         if ((cursor_glyph->resolved_level & 1) != 0
+             && cursor_glyph->pixel_width > w->phys_cursor_width - 1)
+           x += cursor_glyph->pixel_width - w->phys_cursor_width + 1;
+         android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x,
+                                 cursor_start_y,
+                                 w->phys_cursor_width - 1, width);
+       }
+
+      android_reset_clip_rectangles (f, gc);
+    }
+}
+
+static void
+android_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
+                           int x, int y, enum text_cursor_kinds cursor_type,
+                           int cursor_width, bool on_p, bool active_p)
+{
+  struct frame *f;
+
+  f = WINDOW_XFRAME (w);
+
+  if (on_p)
+    {
+      w->phys_cursor_type = cursor_type;
+      w->phys_cursor_on_p = true;
+
+      if (glyph_row->exact_window_width_line_p
+         && (glyph_row->reversed_p
+             ? (w->phys_cursor.hpos < 0)
+             : (w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])))
+       {
+         glyph_row->cursor_in_fringe_p = true;
+         draw_fringe_bitmap (w, glyph_row, glyph_row->reversed_p);
+       }
+      else
+       {
+         switch (cursor_type)
+           {
+           case HOLLOW_BOX_CURSOR:
+             android_draw_hollow_cursor (w, glyph_row);
+             break;
+
+           case FILLED_BOX_CURSOR:
+             draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
+             break;
+
+           case BAR_CURSOR:
+             android_draw_bar_cursor (w, glyph_row, cursor_width, BAR_CURSOR);
+             break;
+
+           case HBAR_CURSOR:
+             android_draw_bar_cursor (w, glyph_row, cursor_width, HBAR_CURSOR);
+             break;
+
+           case NO_CURSOR:
+             w->phys_cursor_width = 0;
+             break;
+
+           default:
+             emacs_abort ();
+           }
+       }
+
+      /* Now proceed to tell the input method the current position of
+        the cursor, if required.  */
+
+      if (FRAME_OUTPUT_DATA (f)->need_cursor_updates
+         && w == XWINDOW (f->selected_window))
+       android_set_preeditarea (w, x, y);
+    }
+}
+
+static void
+android_draw_vertical_window_border (struct window *w, int x, int y0, int y1)
+{
+  struct frame *f = XFRAME (WINDOW_FRAME (w));
+  struct face *face;
+
+  face = FACE_FROM_ID_OR_NULL (f, VERTICAL_BORDER_FACE_ID);
+  if (face)
+    android_set_foreground (f->output_data.android->normal_gc,
+                           face->foreground);
+
+  android_draw_line (FRAME_ANDROID_DRAWABLE (f),
+                    f->output_data.android->normal_gc,
+                    x, y0, x, y1);
+}
+
+static void
+android_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
+{
+  struct frame *f = XFRAME (WINDOW_FRAME (w));
+  struct face *face = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FACE_ID);
+  struct face *face_first
+    = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID);
+  struct face *face_last
+    = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_LAST_PIXEL_FACE_ID);
+  unsigned long color = face ? face->foreground : FRAME_FOREGROUND_PIXEL (f);
+  unsigned long color_first = (face_first
+                              ? face_first->foreground
+                              : FRAME_FOREGROUND_PIXEL (f));
+  unsigned long color_last = (face_last
+                             ? face_last->foreground
+                             : FRAME_FOREGROUND_PIXEL (f));
+
+  if ((y1 - y0 > x1 - x0) && (x1 - x0 >= 3))
+    /* A vertical divider, at least three pixels wide: Draw first and
+       last pixels differently.  */
+    {
+      android_set_foreground (f->output_data.android->normal_gc,
+                             color_first);
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+                             f->output_data.android->normal_gc,
+                             x0, y0, 1, y1 - y0);
+      android_set_foreground (f->output_data.android->normal_gc,
+                             color);
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+                             f->output_data.android->normal_gc,
+                             x0 + 1, y0, x1 - x0 - 2, y1 - y0);
+      android_set_foreground (f->output_data.android->normal_gc,
+                             color_last);
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+                             f->output_data.android->normal_gc,
+                             x1 - 1, y0, 1, y1 - y0);
+    }
+  else if ((x1 - x0 > y1 - y0) && (y1 - y0 >= 3))
+    /* A horizontal divider, at least three pixels high: Draw first
+       and last pixels differently.  */
+    {
+      android_set_foreground (f->output_data.android->normal_gc,
+                             color_first);
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+                             f->output_data.android->normal_gc,
+                             x0, y0, x1 - x0, 1);
+      android_set_foreground (f->output_data.android->normal_gc, color);
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+                             f->output_data.android->normal_gc,
+                             x0, y0 + 1, x1 - x0, y1 - y0 - 2);
+      android_set_foreground (f->output_data.android->normal_gc,
+                             color_last);
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+                             f->output_data.android->normal_gc,
+                             x0, y1 - 1, x1 - x0, 1);
+    }
+  else
+    {
+      /* In any other case do not draw the first and last pixels
+        differently.  */
+      android_set_foreground (f->output_data.android->normal_gc, color);
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+                             f->output_data.android->normal_gc,
+                             x0, y0, x1 - x0, y1 - y0);
+    }
+}
+
+
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#else
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+#endif
+
+/* Input method related functions.  Some of these are called from Java
+   within the UI thread.  */
+
+/* A counter used to decide when an editing request completes.  */
+static unsigned long edit_counter;
+
+/* The last counter known to have completed.  */
+static unsigned long last_edit_counter;
+
+/* Semaphore posted every time the counter increases.  */
+static sem_t edit_sem;
+
+/* Try to synchronize with the UI thread, waiting a certain amount of
+   time for outstanding editing requests to complete.
+
+   Every time one of the text retrieval functions is called and an
+   editing request is made, Emacs gives the main thread approximately
+   100 ms to process it, in order to mostly keep the input method in
+   sync with the buffer contents.  */
+
+static void
+android_sync_edit (void)
+{
+  struct timespec start, end, rem;
+  unsigned long counter;
+
+  counter = __atomic_load_n (&last_edit_counter,
+                            __ATOMIC_SEQ_CST);
+
+  if (counter == edit_counter)
+    return;
+
+  start = current_timespec ();
+  end = timespec_add (start, make_timespec (0, 100000000));
+
+  while (true)
+    {
+      rem = timespec_sub (end, current_timespec ());
+
+      /* Timeout.  */
+      if (timespec_sign (rem) < 0)
+       break;
+
+      if (__atomic_load_n (&last_edit_counter,
+                          __ATOMIC_SEQ_CST)
+         == edit_counter)
+       break;
+
+      sem_timedwait (&edit_sem, &end);
+    }
+}
+
+/* Return a copy of the specified Java string and its length in
+   *LENGTH.  Use the JNI environment ENV.  Value is NULL if copying
+   *the string fails.  */
+
+static unsigned short *
+android_copy_java_string (JNIEnv *env, jstring string, size_t *length)
+{
+  jsize size, i;
+  const jchar *java;
+  unsigned short *buffer;
+
+  size = (*env)->GetStringLength (env, string);
+  buffer = malloc (size * sizeof *buffer);
+
+  if (!buffer)
+    return NULL;
+
+  java = (*env)->GetStringChars (env, string, NULL);
+
+  if (!java)
+    {
+      free (buffer);
+      return NULL;
+    }
+
+  for (i = 0; i < size; ++i)
+    buffer[i] = java[i];
+
+  *length = size;
+  (*env)->ReleaseStringChars (env, string, java);
+  return buffer;
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (beginBatchEdit) (JNIEnv *env, jobject object, jshort window)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.ime.type = ANDROID_INPUT_METHOD;
+  event.ime.serial = ++event_serial;
+  event.ime.window = window;
+  event.ime.operation = ANDROID_IME_START_BATCH_EDIT;
+  event.ime.start = 0;
+  event.ime.end = 0;
+  event.ime.length = 0;
+  event.ime.position = 0;
+  event.ime.text = NULL;
+  event.ime.counter = ++edit_counter;
+
+  android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (endBatchEdit) (JNIEnv *env, jobject object, jshort window)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.ime.type = ANDROID_INPUT_METHOD;
+  event.ime.serial = ++event_serial;
+  event.ime.window = window;
+  event.ime.operation = ANDROID_IME_END_BATCH_EDIT;
+  event.ime.start = 0;
+  event.ime.end = 0;
+  event.ime.length = 0;
+  event.ime.position = 0;
+  event.ime.text = NULL;
+  event.ime.counter = ++edit_counter;
+
+  android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (commitCompletion) (JNIEnv *env, jobject object, jshort window,
+                               jstring completion_text, jint position)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+  unsigned short *text;
+  size_t length;
+
+  /* First, obtain a copy of the Java string.  */
+  text = android_copy_java_string (env, completion_text, &length);
+
+  if (!text)
+    return;
+
+  /* Next, populate the event.  Events will always eventually be
+     delivered on Android, so handle_one_android_event can be relied
+     on to free text.  */
+
+  event.ime.type = ANDROID_INPUT_METHOD;
+  event.ime.serial = ++event_serial;
+  event.ime.window = window;
+  event.ime.operation = ANDROID_IME_COMMIT_TEXT;
+  event.ime.start = 0;
+  event.ime.end = 0;
+  event.ime.length = min (length, PTRDIFF_MAX);
+  event.ime.position = position;
+  event.ime.text = text;
+  event.ime.counter = ++edit_counter;
+
+  android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (commitText) (JNIEnv *env, jobject object, jshort window,
+                         jstring commit_text, jint position)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+  unsigned short *text;
+  size_t length;
+
+  /* First, obtain a copy of the Java string.  */
+  text = android_copy_java_string (env, commit_text, &length);
+
+  if (!text)
+    return;
+
+  /* Next, populate the event.  Events will always eventually be
+     delivered on Android, so handle_one_android_event can be relied
+     on to free text.  */
+
+  event.ime.type = ANDROID_INPUT_METHOD;
+  event.ime.serial = ++event_serial;
+  event.ime.window = window;
+  event.ime.operation = ANDROID_IME_COMMIT_TEXT;
+  event.ime.start = 0;
+  event.ime.end = 0;
+  event.ime.length = min (length, PTRDIFF_MAX);
+  event.ime.position = position;
+  event.ime.text = text;
+  event.ime.counter = ++edit_counter;
+
+  android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (deleteSurroundingText) (JNIEnv *env, jobject object,
+                                    jshort window, jint left_length,
+                                    jint right_length)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.ime.type = ANDROID_INPUT_METHOD;
+  event.ime.serial = ++event_serial;
+  event.ime.window = window;
+  event.ime.operation = ANDROID_IME_DELETE_SURROUNDING_TEXT;
+  event.ime.start = left_length;
+  event.ime.end = right_length;
+  event.ime.length = 0;
+  event.ime.position = 0;
+  event.ime.text = NULL;
+  event.ime.counter = ++edit_counter;
+
+  android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (finishComposingText) (JNIEnv *env, jobject object,
+                                  jshort window)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.ime.type = ANDROID_INPUT_METHOD;
+  event.ime.serial = ++event_serial;
+  event.ime.window = window;
+  event.ime.operation = ANDROID_IME_FINISH_COMPOSING_TEXT;
+  event.ime.start = 0;
+  event.ime.end = 0;
+  event.ime.length = 0;
+  event.ime.position = 0;
+  event.ime.text = NULL;
+  event.ime.counter = ++edit_counter;
+
+  android_write_event (&event);
+}
+
+/* Structure describing the context used for a text query.  */
+
+struct android_conversion_query_context
+{
+  /* The conversion request.  */
+  struct textconv_callback_struct query;
+
+  /* The window the request is being made on.  */
+  android_window window;
+
+  /* Whether or not the request was successful.  */
+  bool success;
+};
+
+/* Obtain the text from the frame whose window is that specified in
+   DATA using the text conversion query specified there.
+
+   Set ((struct android_conversion_query_context *) DATA)->success on
+   success.  */
+
+static void
+android_perform_conversion_query (void *data)
+{
+  struct android_conversion_query_context *context;
+  struct frame *f;
+
+  context = data;
+
+  /* Find the frame associated with the window.  */
+  f = android_window_to_frame (NULL, context->window);
+
+  if (!f)
+    return;
+
+  textconv_query (f, &context->query, 0);
+
+  /* context->query.text will have been set even if textconv_query
+     returns 1.  */
+
+  context->success = true;
+}
+
+/* Convert a string BUFFERS containing N characters in Emacs's
+   internal multibyte encoding to a Java string utilizing the
+   specified JNI environment.
+
+   If N is equal to BYTES, then BUFFER is a single byte buffer.
+   Otherwise, BUFFER is a multibyte buffer.
+
+   Make sure N and BYTES are absolutely correct, or you are asking for
+   trouble.
+
+   Value is the string upon success, NULL otherwise.  Any exceptions
+   generated are not cleared.  */
+
+static jstring
+android_text_to_string (JNIEnv *env, char *buffer, ptrdiff_t n,
+                       ptrdiff_t bytes)
+{
+  jchar *utf16;
+  size_t size, index;
+  jstring string;
+  int encoded;
+
+  if (n == bytes)
+    {
+      /* This buffer holds no multibyte characters.  */
+
+      if (INT_MULTIPLY_WRAPV (n, sizeof *utf16, &size))
+       return NULL;
+
+      utf16 = malloc (size);
+      index = 0;
+
+      if (!utf16)
+       return NULL;
+
+      while (n--)
+       {
+         utf16[index] = buffer[index];
+         index++;
+       }
+
+      string = (*env)->NewString (env, utf16, bytes);
+      free (utf16);
+
+      return string;
+    }
+
+  /* Allocate enough to hold N characters.  */
+
+  if (INT_MULTIPLY_WRAPV (n, sizeof *utf16, &size))
+    return NULL;
+
+  utf16 = malloc (size);
+  index = 0;
+
+  if (!utf16)
+    return NULL;
+
+  while (n--)
+    {
+      eassert (CHAR_HEAD_P (*buffer));
+      encoded = STRING_CHAR ((unsigned char *) buffer);
+
+      /* Now figure out how to save ENCODED into the string.
+         Emacs operates on multibyte characters, not UTF-16
+         characters with surrogate pairs as Android does.
+
+         However, character positions in Java are represented in 2
+         byte units, meaning that the text position reported to
+         Android can become out of sync if characters are found in a
+         buffer that require surrogate pairs.
+
+         The hack used by Emacs is to simply replace each multibyte
+         character that doesn't fit in a jchar with the NULL
+         character.  */
+
+      if (encoded >= 65536)
+       encoded = 0;
+
+      utf16[index++] = encoded;
+      buffer += BYTES_BY_CHAR_HEAD (*buffer);
+    }
+
+  /* Create the string.  */
+  string = (*env)->NewString (env, utf16, index);
+  free (utf16);
+  return string;
+}
+
+JNIEXPORT jstring JNICALL
+NATIVE_NAME (getTextAfterCursor) (JNIEnv *env, jobject object, jshort window,
+                                 jint length, jint flags)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  struct android_conversion_query_context context;
+  jstring string;
+
+  /* First, set up the conversion query.  */
+  context.query.position = EMACS_INT_MAX;
+  context.query.direction = TEXTCONV_FORWARD_CHAR;
+  context.query.factor = min (length, 65535);
+  context.query.operation = TEXTCONV_RETRIEVAL;
+
+  /* Next, set the rest of the context.  */
+  context.window = window;
+  context.success = false;
+
+  /* Now try to perform the query.  */
+  android_sync_edit ();
+  if (android_run_in_emacs_thread (android_perform_conversion_query,
+                                  &context))
+    return NULL;
+
+  if (!context.success)
+    return NULL;
+
+  /* context->query.text now contains the text in Emacs's internal
+     UTF-8 based encoding.
+
+     Convert it to Java's UTF-16 encoding, which is the same as
+     UTF-16, except that NULL bytes are encoded as surrogate pairs.
+
+     This assumes that `free' can free data allocated with xmalloc.  */
+
+  string = android_text_to_string (env, context.query.text.text,
+                                  context.query.text.length,
+                                  context.query.text.bytes);
+  free (context.query.text.text);
+
+  return string;
+}
+
+JNIEXPORT jstring JNICALL
+NATIVE_NAME (getTextBeforeCursor) (JNIEnv *env, jobject object, jshort window,
+                                  jint length, jint flags)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  struct android_conversion_query_context context;
+  jstring string;
+
+  /* First, set up the conversion query.  */
+  context.query.position = TYPE_MINIMUM (EMACS_INT);
+  context.query.direction = TEXTCONV_BACKWARD_CHAR;
+  context.query.factor = min (length, 65535);
+  context.query.operation = TEXTCONV_RETRIEVAL;
+
+  /* Next, set the rest of the context.  */
+  context.window = window;
+  context.success = false;
+
+  /* Now try to perform the query.  */
+  android_sync_edit ();
+  if (android_run_in_emacs_thread (android_perform_conversion_query,
+                                  &context))
+    return NULL;
+
+  if (!context.success)
+    return NULL;
+
+  /* context->query.text now contains the text in Emacs's internal
+     UTF-8 based encoding.
+
+     Convert it to Java's UTF-16 encoding, which is the same as
+     UTF-16, except that NULL bytes are encoded as surrogate pairs.
+
+     This assumes that `free' can free data allocated with xmalloc.  */
+
+  string = android_text_to_string (env, context.query.text.text,
+                                  context.query.text.length,
+                                  context.query.text.bytes);
+  free (context.query.text.text);
+
+  return string;
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (setComposingText) (JNIEnv *env, jobject object, jshort window,
+                               jstring composing_text,
+                               jint new_cursor_position)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+  unsigned short *text;
+  size_t length;
+
+  /* First, obtain a copy of the Java string.  */
+  text = android_copy_java_string (env, composing_text, &length);
+
+  if (!text)
+    return;
+
+  /* Next, populate the event.  Events will always eventually be
+     delivered on Android, so handle_one_android_event can be relied
+     on to free text.  */
+
+  event.ime.type = ANDROID_INPUT_METHOD;
+  event.ime.serial = ++event_serial;
+  event.ime.window = window;
+  event.ime.operation = ANDROID_IME_SET_COMPOSING_TEXT;
+  event.ime.start = 0;
+  event.ime.end = 0;
+  event.ime.length = min (length, PTRDIFF_MAX);
+  event.ime.position = new_cursor_position;
+  event.ime.text = text;
+  event.ime.counter = ++edit_counter;
+
+  android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (setComposingRegion) (JNIEnv *env, jobject object, jshort window,
+                                 jint start, jint end)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.ime.type = ANDROID_INPUT_METHOD;
+  event.ime.serial = ++event_serial;
+  event.ime.window = window;
+  event.ime.operation = ANDROID_IME_SET_COMPOSING_REGION;
+  event.ime.start = start + 1;
+  event.ime.end = end + 1;
+  event.ime.length = 0;
+  event.ime.position = 0;
+  event.ime.text = NULL;
+  event.ime.counter = ++edit_counter;
+
+  android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (setSelection) (JNIEnv *env, jobject object, jshort window,
+                           jint start, jint end)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  /* While IMEs want access to the entire selection, Emacs only
+     supports setting the point.  */
+
+  event.ime.type = ANDROID_INPUT_METHOD;
+  event.ime.serial = ++event_serial;
+  event.ime.window = window;
+  event.ime.operation = ANDROID_IME_SET_POINT;
+  event.ime.start = start + 1;
+  event.ime.end = end + 1;
+  event.ime.length = 0;
+  event.ime.position = start;
+  event.ime.text = NULL;
+  event.ime.counter = ++edit_counter;
+
+  android_write_event (&event);
+}
+
+/* Structure describing the context for `getSelection'.  */
+
+struct android_get_selection_context
+{
+  /* The window in question.  */
+  android_window window;
+
+  /* The position of the window's point when it was last
+     redisplayed, and its last mark if active.  */
+  ptrdiff_t point, mark;
+};
+
+/* Function run on the main thread by `getSelection'.
+   Place the character position of point in PT.  */
+
+static void
+android_get_selection (void *data)
+{
+  struct android_get_selection_context *context;
+  struct frame *f;
+  struct window *w;
+  struct buffer *b;
+
+  context = data;
+
+  /* Look up the associated frame and its selected window.  */
+  f = android_window_to_frame (NULL, context->window);
+
+  if (!f)
+    context->point = -1;
+  else
+    {
+      w = XWINDOW (f->selected_window);
+
+      /* Return W's point as it is now.  Then, set
+        W->ephemeral_last_point to match the current point.  */
+      context->point = window_point (w);
+      w->ephemeral_last_point = context->point;
+
+      /* Default context->mark to w->last_point too.  */
+      context->mark = context->point;
+
+      /* If the mark is active, then set it properly.  Also, adjust
+        w->last_mark to match.  */
+      b = XBUFFER (w->contents);
+      if (!NILP (BVAR (b, mark_active)))
+       {
+         context->mark = marker_position (BVAR (b, mark));
+         w->last_mark = context->mark;
+       }
+    }
+}
+
+JNIEXPORT jintArray JNICALL
+NATIVE_NAME (getSelection) (JNIEnv *env, jobject object, jshort window)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  struct android_get_selection_context context;
+  jintArray array;
+  jint contents[2];
+
+  context.window = window;
+
+  android_sync_edit ();
+  if (android_run_in_emacs_thread (android_get_selection,
+                                  &context))
+    return NULL;
+
+  if (context.point == -1)
+    return NULL;
+
+  /* Wraparound actually makes more sense than truncation; at least
+     editing will sort of work.  Convert the positions to start from
+     index 0, as that is what Android expects.  */
+  contents[0] = (unsigned int) min (context.point,
+                                   context.mark) - 1;
+  contents[1] = (unsigned int) max (context.point,
+                                   context.mark) - 1;
+
+  /* Now create the array.  */
+  array = (*env)->NewIntArray (env, 2);
+
+  if (!array)
+    return NULL;
+
+  /* Set its contents.  */
+  (*env)->SetIntArrayRegion (env, array, 0, 2, contents);
+  return array;
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (performEditorAction) (JNIEnv *env, jobject object,
+                                  jshort window, int action)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  /* It's a good idea to call `android_sync_edit' before sending the
+     key event.  Otherwise, if RET causes the current window to be
+     changed, any text previously committed might end up in the newly
+     selected window.  */
+
+  android_sync_edit ();
+
+  /* Undocumented behavior: performEditorAction is apparently expected
+     to finish composing any text.  */
+
+  event.ime.type = ANDROID_INPUT_METHOD;
+  event.ime.serial = ++event_serial;
+  event.ime.window = window;
+  event.ime.operation = ANDROID_IME_FINISH_COMPOSING_TEXT;
+  event.ime.start = 0;
+  event.ime.end = 0;
+
+  /* This value of `length' means that the input method should receive
+     an update containing the new conversion region.  */
+
+  event.ime.length = 1;
+  event.ime.position = 0;
+  event.ime.text = NULL;
+  event.ime.counter = ++edit_counter;
+
+  android_write_event (&event);
+
+  /* Finally, send the return key press.  `counter' is set; this means
+     that a text conversion barrier will be generated once the event
+     is read, which will cause subsequent edits to wait until the
+     edits associated with this key press complete.  */
+
+  event.xkey.type = ANDROID_KEY_PRESS;
+  event.xkey.serial = ++event_serial;
+  event.xkey.window = window;
+  event.xkey.time = 0;
+  event.xkey.state = 0;
+  event.xkey.keycode = 66;
+  event.xkey.unicode_char = 0;
+  event.xkey.counter = ++edit_counter;
+
+  android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (performContextMenuAction) (JNIEnv *env, jobject object,
+                                       jshort window, int action)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+  int key;
+
+  /* Note that ACTION is determined in EmacsInputConnection, and as
+     such they are not actual resource IDs.  */
+
+  switch (action)
+    {
+    case 0: /* android.R.id.selectAll */
+    case 1: /* android.R.id.startSelectingText */
+    case 2: /* android.R.id.stopSelectingText */
+    default:
+      /* These actions are not implemented.  */
+      return;
+
+    case 3: /* android.R.id.cut */
+      key = 277;
+      break;
+
+    case 4: /* android.R.id.copy */
+      key = 278;
+      break;
+
+    case 5: /* android.R.id.paste */
+      key = 279;
+      break;
+    }
+
+  event.xkey.type = ANDROID_KEY_PRESS;
+  event.xkey.serial = ++event_serial;
+  event.xkey.window = window;
+  event.xkey.time = 0;
+  event.xkey.state = 0;
+  event.xkey.keycode = key;
+  event.xkey.unicode_char = 0;
+  event.xkey.counter = ++edit_counter;
+
+  android_write_event (&event);
+}
+
+
+
+/* Text extraction.  */
+
+struct android_get_extracted_text_context
+{
+  /* The parameters of the request.  */
+  int hint_max_chars;
+
+  /* Token for the request.  */
+  int token;
+
+  /* Flags associated with the request.  */
+  int flags;
+
+  /* The returned text, or NULL.  */
+  char *text;
+
+  /* The size of that text in characters and bytes.  */
+  ptrdiff_t length, bytes;
+
+  /* Offsets into that text.  */
+  ptrdiff_t start, start_offset, end_offset;
+
+  /* The window.  */
+  android_window window;
+
+  /* Whether or not the mark is active.  */
+  bool mark_active;
+};
+
+/* Return the extracted text in the extracted text context specified
+   by DATA.  Save its flags and token into its frame's state.  */
+
+static void
+android_get_extracted_text (void *data)
+{
+  struct android_get_extracted_text_context *request;
+  struct frame *f;
+
+  request = data;
+
+  /* Find the frame associated with the window.  */
+  f = android_window_to_frame (NULL, request->window);
+
+  if (!f)
+    return;
+
+  /* Now get the extracted text.  */
+  request->text
+    = get_extracted_text (f, min (request->hint_max_chars, 600),
+                         &request->start, &request->start_offset,
+                         &request->end_offset, &request->length,
+                         &request->bytes, &request->mark_active);
+
+  /* See if request->flags & GET_EXTRACTED_TEXT_MONITOR.  If so, then
+     the input method has asked to monitor changes to the extracted
+     text until the next IM context reset.  */
+
+  FRAME_ANDROID_OUTPUT (f)->extracted_text_flags = request->flags;
+  FRAME_ANDROID_OUTPUT (f)->extracted_text_token = request->token;
+  FRAME_ANDROID_OUTPUT (f)->extracted_text_hint = request->hint_max_chars;
+}
+
+/* Structure describing the `ExtractedTextRequest' class.
+   Valid only on the UI thread.  */
+
+struct android_extracted_text_request_class
+{
+  bool initialized;
+  jfieldID hint_max_chars;
+  jfieldID token;
+};
+
+/* Structure describing the `ExtractedText' class.
+   Valid only on the UI thread.  */
+
+struct android_extracted_text_class
+{
+  jclass class;
+  jmethodID constructor;
+  jfieldID flags;
+  jfieldID partial_start_offset;
+  jfieldID partial_end_offset;
+  jfieldID selection_start;
+  jfieldID selection_end;
+  jfieldID start_offset;
+  jfieldID text;
+};
+
+/* Fields and methods associated with the `ExtractedTextRequest'
+   class.  */
+struct android_extracted_text_request_class request_class;
+
+/* Fields and methods associated with the `ExtractedText' class.  */
+struct android_extracted_text_class text_class;
+
+/* Return an ExtractedText object corresponding to the extracted text
+   TEXT.  START is a character position describing the offset of the
+   first character in TEXT.  START_OFFSET is the offset of the lesser
+   of point or mark relative to START, and END_OFFSET is that of the
+   greater of point or mark relative to START.  MARK_ACTIVE specifies
+   whether or not the mark is currently active.
+
+   Assume that request_class and text_class have already been
+   initialized.
+
+   Value is NULL if an error occurs; the exception is not cleared,
+   else a local reference to the ExtractedText object.  */
+
+static jobject
+android_build_extracted_text (jstring text, ptrdiff_t start,
+                             ptrdiff_t start_offset,
+                             ptrdiff_t end_offset, bool mark_active)
+{
+  JNIEnv *env;
+  jobject object;
+
+  env = android_java_env;
+
+  /* Return NULL if the class has not yet been obtained.  */
+  if (!text_class.class)
+    return NULL;
+
+  /* Create an ExtractedText object containing this information.  */
+  object = (*env)->NewObject (env, text_class.class,
+                             text_class.constructor);
+  if (!object)
+    return NULL;
+
+  (*env)->SetIntField (env, object, text_class.flags,
+                      /* ExtractedText.FLAG_SELECTING */
+                      mark_active ? 2 : 0);
+  (*env)->SetIntField (env, object, text_class.partial_start_offset, -1);
+  (*env)->SetIntField (env, object, text_class.partial_end_offset, -1);
+  (*env)->SetIntField (env, object, text_class.selection_start,
+                      min (start_offset, TYPE_MAXIMUM (jint)));
+  (*env)->SetIntField (env, object, text_class.selection_end,
+                      min (end_offset, TYPE_MAXIMUM (jint)));
+
+  /* Subtract 1 from start: point indices in Emacs start from 1, but
+     Android expects 0.  */
+  (*env)->SetIntField (env, object, text_class.start_offset,
+                      min (start - 1, TYPE_MAXIMUM (jint)));
+  (*env)->SetObjectField (env, object, text_class.text, text);
+  return object;
+}
+
+JNIEXPORT jobject JNICALL
+NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object,
+                               jshort window, jobject request,
+                               jint flags)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  struct android_get_extracted_text_context context;
+  jstring string;
+  jclass class;
+  jobject object;
+
+  /* Initialize both classes if necessary.  */
+
+  if (!request_class.initialized)
+    {
+      class
+       = (*env)->FindClass (env, ("android/view/inputmethod"
+                                  "/ExtractedTextRequest"));
+      assert (class);
+
+      request_class.hint_max_chars
+       = (*env)->GetFieldID (env, class, "hintMaxChars", "I");
+      assert (request_class.hint_max_chars);
+
+      request_class.token
+       = (*env)->GetFieldID (env, class, "token", "I");
+      assert (request_class.token);
+
+      request_class.initialized = true;
+    }
+
+  if (!text_class.class)
+    {
+      text_class.class
+       = (*env)->FindClass (env, ("android/view/inputmethod"
+                                  "/ExtractedText"));
+      assert (text_class.class);
+
+      class
+       = text_class.class
+       = (*env)->NewGlobalRef (env, text_class.class);
+      assert (text_class.class);
+
+      text_class.flags
+       = (*env)->GetFieldID (env, class, "flags", "I");
+      text_class.partial_start_offset
+       = (*env)->GetFieldID (env, class, "partialStartOffset", "I");
+      text_class.partial_end_offset
+       = (*env)->GetFieldID (env, class, "partialEndOffset", "I");
+      text_class.selection_start
+       = (*env)->GetFieldID (env, class, "selectionStart", "I");
+      text_class.selection_end
+       = (*env)->GetFieldID (env, class, "selectionEnd", "I");
+      text_class.start_offset
+       = (*env)->GetFieldID (env, class, "startOffset", "I");
+      text_class.text
+       = (*env)->GetFieldID (env, class, "text", "Ljava/lang/CharSequence;");
+      text_class.constructor
+       = (*env)->GetMethodID (env, class, "<init>", "()V");
+    }
+
+  context.hint_max_chars
+    = (*env)->GetIntField (env, request, request_class.hint_max_chars);
+  context.token
+    = (*env)->GetIntField (env, request, request_class.token);
+  context.flags = flags;
+  context.text = NULL;
+  context.window = window;
+
+  android_sync_edit ();
+  if (android_run_in_emacs_thread (android_get_extracted_text,
+                                  &context))
+    return NULL;
+
+  if (!context.text)
+    return NULL;
+
+  /* Encode the returned text.  */
+  string = android_text_to_string (env, context.text, context.length,
+                                  context.bytes);
+  free (context.text);
+
+  if (!string)
+    return NULL;
+
+  /* Create an ExtractedText object containing this information.  */
+  object = (*env)->NewObject (env, text_class.class,
+                             text_class.constructor);
+  if (!object)
+    return NULL;
+
+  (*env)->SetIntField (env, object, text_class.flags,
+                      /* ExtractedText.FLAG_SELECTING */
+                      context.mark_active ? 2 : 0);
+  (*env)->SetIntField (env, object, text_class.partial_start_offset, -1);
+  (*env)->SetIntField (env, object, text_class.partial_end_offset, -1);
+  (*env)->SetIntField (env, object, text_class.selection_start,
+                      min (context.start_offset, TYPE_MAXIMUM (jint)));
+  (*env)->SetIntField (env, object, text_class.selection_end,
+                      min (context.end_offset, TYPE_MAXIMUM (jint)));
+
+  /* Subtract 1 from start: point indices in Emacs start from 1, but
+     Android expects 0.  */
+  (*env)->SetIntField (env, object, text_class.start_offset,
+                      min (context.start - 1, TYPE_MAXIMUM (jint)));
+  (*env)->SetObjectField (env, object, text_class.text, string);
+  return object;
+}
+
+
+
+JNIEXPORT jstring JNICALL
+NATIVE_NAME (getSelectedText) (JNIEnv *env, jobject object,
+                              jshort window)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  struct android_get_extracted_text_context context;
+  jstring string;
+
+  context.hint_max_chars = -1;
+  context.token = 0;
+  context.text = NULL;
+  context.window = window;
+
+  android_sync_edit ();
+  if (android_run_in_emacs_thread (android_get_extracted_text,
+                                  &context))
+    return NULL;
+
+  if (!context.text)
+    return NULL;
+
+  /* Encode the returned text.  */
+  string = android_text_to_string (env, context.text, context.length,
+                                  context.bytes);
+  free (context.text);
+
+  return string;
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (requestSelectionUpdate) (JNIEnv *env, jobject object,
+                                     jshort window)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.ime.type = ANDROID_INPUT_METHOD;
+  event.ime.serial = ++event_serial;
+  event.ime.window = window;
+  event.ime.operation = ANDROID_IME_REQUEST_SELECTION_UPDATE;
+  event.ime.start = 0;
+  event.ime.end = 0;
+  event.ime.length = 0;
+  event.ime.position = 0;
+  event.ime.text = NULL;
+  event.ime.counter = ++edit_counter;
+
+  android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (requestCursorUpdates) (JNIEnv *env, jobject object,
+                                   jshort window, jint mode)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.ime.type = ANDROID_INPUT_METHOD;
+  event.ime.serial = ++event_serial;
+  event.ime.window = window;
+  event.ime.operation = ANDROID_IME_REQUEST_CURSOR_UPDATES;
+  event.ime.start = 0;
+  event.ime.end = 0;
+  event.ime.length = mode;
+  event.ime.position = 0;
+  event.ime.text = NULL;
+
+  /* Since this does not affect the state of the buffer text, there is
+     no need to apply synchronization to this event.  */
+  event.ime.counter = 0;
+
+  android_write_event (&event);
+}
+
+/* Notice that a new input method connection has been initialized and
+   clear cursor update requests, extracted text requests, and the
+   composing region.  */
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (clearInputFlags) (JNIEnv *env, jobject object,
+                              jshort window)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  union android_event event;
+
+  event.ime.type = ANDROID_INPUT_METHOD;
+  event.ime.serial = ++event_serial;
+  event.ime.window = window;
+  event.ime.operation = ANDROID_IME_FINISH_COMPOSING_TEXT;
+  event.ime.start = 0;
+  event.ime.end = 0;
+
+  /* This value of `length' means that updates to the cursor position
+     and extracted text should not be reported anymore.  */
+
+  event.ime.length = 2;
+  event.ime.position = 0;
+  event.ime.text = NULL;
+  event.ime.counter = ++edit_counter;
+
+  android_write_event (&event);
+}
+
+
+
+/* Context for a call to `getSurroundingText'.  */
+
+struct android_get_surrounding_text_context
+{
+  /* Number of characters before the region to return.  */
+  int before_length;
+
+  /* Number of characters after the region to return.  */
+  int after_length;
+
+  /* The returned text, or NULL.  */
+  char *text;
+
+  /* The size of that text in characters and bytes.  */
+  ptrdiff_t length, bytes;
+
+  /* Offsets into that text.  */
+  ptrdiff_t offset, start, end;
+
+  /* The start and end indices of the conversion region.
+     -1 if it does not exist.  */
+  ptrdiff_t conversion_start, conversion_end;
+
+  /* The window.  */
+  android_window window;
+};
+
+/* Return the surrounding text in the surrounding text context
+   specified by DATA.  */
+
+static void
+android_get_surrounding_text (void *data)
+{
+  struct android_get_surrounding_text_context *request;
+  struct frame *f;
+  ptrdiff_t temp;
+
+  request = data;
+
+  /* Find the frame associated with the window.  */
+  f = android_window_to_frame (NULL, request->window);
+
+  if (!f)
+    return;
+
+  /* Now get the surrounding text.  */
+  request->text
+    = get_surrounding_text (f, request->before_length,
+                           request->after_length, &request->length,
+                           &request->bytes, &request->offset,
+                           &request->start, &request->end);
+
+  /* Sort request->start and request->end for compatibility with some
+     bad input methods.  */
+
+  if (request->end < request->start)
+    {
+      temp = request->start;
+      request->start = request->end;
+      request->end = temp;
+    }
+
+  /* Retrieve the conversion region.  */
+
+  request->conversion_start = -1;
+  request->conversion_end = -1;
+
+  if (MARKERP (f->conversion.compose_region_start))
+    {
+      request->conversion_start
+       = marker_position (f->conversion.compose_region_start) - 1;
+      request->conversion_end
+       = marker_position (f->conversion.compose_region_end) - 1;
+    }
+}
+
+/* Return a local reference to a `SurroundingText' object describing
+   WINDOW's surrounding text.  ENV should be a valid JNI environment
+   for the current thread.
+
+   BEFORE_LENGTH and AFTER_LENGTH specify the number of characters
+   around point and mark to return.
+
+   Return the conversion region (or -1) in *CONVERSION_START and
+   *CONVERSION_END if non-NULL.
+
+   Value is the object upon success, else NULL.  */
+
+static jobject
+android_get_surrounding_text_internal (JNIEnv *env, jshort window,
+                                      jint before_length,
+                                      jint after_length,
+                                      ptrdiff_t *conversion_start,
+                                      ptrdiff_t *conversion_end)
+{
+  struct android_get_surrounding_text_context context;
+  jstring string;
+  jobject object;
+
+  static jclass class;
+  static jmethodID constructor;
+
+  /* Initialize CLASS if it has not yet been initialized.  */
+
+  if (!class)
+    {
+      class
+       = (*env)->FindClass (env, ("android/view/inputmethod"
+                                  "/SurroundingText"));
+
+#if __ANDROID_API__ < 31
+      /* If CLASS cannot be found, the version of Android currently
+        running is too old.  */
+
+      if (!class)
+       {
+         (*env)->ExceptionClear (env);
+         return NULL;
+       }
+#else /* __ANDROID_API__ >= 31 */
+      assert (class);
+#endif /* __ANDROID_API__ < 31 */
+
+      class = (*env)->NewGlobalRef (env, class);
+      if (!class)
+       /* Clear class to prevent a local reference from remaining in
+          `class'.  */
+       return (class = NULL);
+
+      /* Now look for its constructor.  */
+      constructor = (*env)->GetMethodID (env, class, "<init>",
+                                        "(Ljava/lang/CharSequence;III)V");
+      assert (constructor);
+    }
+
+  context.before_length = before_length;
+  context.after_length = after_length;
+  context.window = window;
+  context.text = NULL;
+
+  android_sync_edit ();
+  if (android_run_in_emacs_thread (android_get_surrounding_text,
+                                  &context))
+    return NULL;
+
+  if (!context.text)
+    return NULL;
+
+  /* Encode the returned text.  */
+  string = android_text_to_string (env, context.text, context.length,
+                                  context.bytes);
+  free (context.text);
+
+  if (!string)
+    return NULL;
+
+  /* Create an SurroundingText object containing this information.  */
+  object = (*env)->NewObject (env, class, constructor, string,
+                             (jint) min (context.start,
+                                         TYPE_MAXIMUM (jint)),
+                             (jint) min (context.end,
+                                         TYPE_MAXIMUM (jint)),
+                             /* Adjust point offsets to fit into
+                                Android's 0-based indexing. */
+                             (jint) min (context.offset - 1,
+                                         TYPE_MAXIMUM (jint)));
+  if (!object)
+    return NULL;
+
+  /* Now return the conversion region if that was requested.  */
+
+  if (conversion_start)
+    {
+      *conversion_start = context.conversion_start;
+      *conversion_end = context.conversion_start;
+    }
+
+  return object;
+}
+
+JNIEXPORT jobject JNICALL
+NATIVE_NAME (getSurroundingText) (JNIEnv *env, jobject object,
+                                 jshort window, jint before_length,
+                                 jint after_length, jint flags)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  return android_get_surrounding_text_internal (env, window, before_length,
+                                               after_length, NULL, NULL);
+}
+
+JNIEXPORT jobject JNICALL
+NATIVE_NAME (takeSnapshot) (JNIEnv *env, jobject object, jshort window)
+{
+  JNI_STACK_ALIGNMENT_PROLOGUE;
+
+  jobject text;
+  ptrdiff_t start, end;
+
+  static jclass class;
+  static jmethodID constructor;
+
+  /* First, obtain the surrounding text and conversion region.  */
+  text = android_get_surrounding_text_internal (env, window, 600, 600,
+                                               &start, &end);
+
+  /* If that fails, return NULL.  */
+
+  if (!text)
+    return NULL;
+
+  /* Next, initialize the TextSnapshot class.  */
+
+  if (!class)
+    {
+      class
+       = (*env)->FindClass (env, ("android/view/inputmethod"
+                                  "/TextSnapshot"));
+#if __ANDROID_API__ < 33
+      /* If CLASS cannot be found, the version of Android currently
+        running is too old.  */
+
+      if (!class)
+       {
+         (*env)->ExceptionClear (env);
+         return NULL;
+       }
+#else /* __ANDROID_API__ >= 33 */
+      assert (class);
+#endif /* __ANDROID_API__ < 33 */
+
+      class = (*env)->NewGlobalRef (env, class);
+      if (!class)
+       /* Clear class to prevent a local reference from remaining in
+          `class'.  */
+       return (class = NULL);
+
+      constructor = (*env)->GetMethodID (env, class, "<init>",
+                                        "(Landroid/view/inputmethod"
+                                        "/SurroundingText;III)V");
+      assert (constructor);
+    }
+
+  /* Try to create a TextSnapshot object.  */
+  eassert (start <= end);
+  object = (*env)->NewObject (env, class, constructor, text,
+                             (jint) min (start, TYPE_MAXIMUM (jint)),
+                             (jint) min (end, TYPE_MAXIMUM (jint)),
+                             (jint) 0);
+  return object;
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#else
+#pragma GCC diagnostic pop
+#endif
+
+
+
+/* Tell the input method where the composing region and selection of
+   F's selected window is located.  W should be F's selected window;
+   if it is NULL, then F->selected_window is used in its place.  */
+
+static void
+android_update_selection (struct frame *f, struct window *w)
+{
+  ptrdiff_t start, end, point, mark, start_offset, end_offset;
+  ptrdiff_t length, bytes;
+  struct buffer *b;
+  int hint, token;
+  char *text;
+  jobject extracted;
+  jstring string;
+  bool mark_active;
+
+  if (MARKERP (f->conversion.compose_region_start))
+    {
+      eassert (MARKERP (f->conversion.compose_region_end));
+
+      /* Indexing in android starts from 0 instead of 1.  */
+      start = marker_position (f->conversion.compose_region_start) - 1;
+      end = marker_position (f->conversion.compose_region_end) - 1;
+    }
+  else
+    start = -1, end = -1;
+
+  /* Now constrain START and END to the maximium size of a Java
+     integer.  */
+  start = min (start, TYPE_MAXIMUM (jint));
+  end = min (end, TYPE_MAXIMUM (jint));
+
+  if (!w)
+    w = XWINDOW (f->selected_window);
+
+  /* Figure out where the point and mark are.  If the mark is not
+     active, then point is set to equal mark.  */
+  b = XBUFFER (w->contents);
+  point = min (w->ephemeral_last_point,
+              TYPE_MAXIMUM (jint));
+  mark = ((!NILP (BVAR (b, mark_active))
+          && w->last_mark != -1)
+         ? min (w->last_mark, TYPE_MAXIMUM (jint))
+         : point);
+
+  /* Send the update.  Android doesn't have a concept of ``point'' and
+     ``mark''; instead, it only has a selection, where the start of
+     the selection is less than or equal to the end.  Also, convert
+     the indices from 1-based Emacs indices to 0-based Android
+     ones.  */
+  android_update_ic (FRAME_ANDROID_WINDOW (f), min (point, mark) - 1,
+                    max (point, mark) - 1, start, end);
+
+  /* Update the extracted text as well, if the input method has asked
+     for updates.  1 is
+     InputConnection.GET_EXTRACTED_TEXT_MONITOR.  */
+
+  if (FRAME_ANDROID_OUTPUT (f)->extracted_text_flags & 1)
+    {
+      hint = FRAME_ANDROID_OUTPUT (f)->extracted_text_hint;
+      token = FRAME_ANDROID_OUTPUT (f)->extracted_text_token;
+      text = get_extracted_text (f, min (hint, 600), &start,
+                                &start_offset, &end_offset,
+                                &length, &bytes, &mark_active);
+
+      if (text)
+       {
+         /* Make a string out of the extracted text.  */
+         string = android_text_to_string (android_java_env,
+                                          text, length, bytes);
+         xfree (text);
+         android_exception_check ();
+
+         /* Make extracted text out of that string.  */
+         extracted = android_build_extracted_text (string, start,
+                                                   start_offset,
+                                                   end_offset,
+                                                   mark_active);
+         android_exception_check_1 (string);
+         ANDROID_DELETE_LOCAL_REF (string);
+
+         if (extracted)
+           {
+             /* extracted is now an associated ExtractedText object.
+                Perform the update.  */
+             android_update_extracted_text (FRAME_ANDROID_WINDOW (f),
+                                            extracted, token);
+             ANDROID_DELETE_LOCAL_REF (extracted);
+           }
+       }
+    }
+}
+
+/* Return whether or not EVENT is an input method event destined for
+   the frame (struct frame *) ARG.  */
+
+static bool
+android_event_is_for_frame (union android_event *event, void *arg)
+{
+  struct frame *f;
+
+  f = arg;
+  return (event->type == ANDROID_INPUT_METHOD
+         && event->ime.window == FRAME_ANDROID_WINDOW (f));
+}
+
+/* Notice that the input method connection to F should be reset as a
+   result of a change to its contents.  */
+
+static void
+android_reset_conversion (struct frame *f)
+{
+  enum android_ic_mode mode;
+  struct window *w;
+  struct buffer *buffer;
+  Lisp_Object style;
+  union android_event event;
+
+  /* Reset the input method.
+
+     Pick an appropriate ``input mode'' based on whether or not the
+     minibuffer window is selected; this controls whether or not
+     ``RET'' inserts a newline or sends an actual key event.  */
+
+  w = XWINDOW (f->selected_window);
+  buffer = XBUFFER (WINDOW_BUFFER (w));
+
+  style = (EQ (find_symbol_value (Qoverriding_text_conversion_style),
+              Qlambda)
+          ? BVAR (buffer, text_conversion_style)
+          : find_symbol_value (Qoverriding_text_conversion_style));
+
+  if (NILP (style) || conversion_disabled_p ())
+    mode = ANDROID_IC_MODE_NULL;
+  else if (EQ (style, Qaction) || EQ (f->selected_window,
+                                     f->minibuffer_window))
+    mode = ANDROID_IC_MODE_ACTION;
+  else
+    mode = ANDROID_IC_MODE_TEXT;
+
+  /* Remove any existing input method events that apply to FRAME from
+     the event queue.
+
+     There's a small window between this and the call to
+     android_reset_ic between which more events can be generated.  */
+
+  while (android_check_if_event (&event, android_event_is_for_frame, f))
+    {
+      switch (event.ime.operation)
+       {
+       case ANDROID_IME_COMMIT_TEXT:
+       case ANDROID_IME_FINISH_COMPOSING_TEXT:
+       case ANDROID_IME_SET_COMPOSING_TEXT:
+         xfree (event.ime.text);
+         break;
+
+       default:
+         break;
+       }
+    }
+
+  android_reset_ic (FRAME_ANDROID_WINDOW (f), mode);
+
+  /* Clear extracted text flags.  Since the IM has been reinitialised,
+     it should no longer be displaying extracted text.  */
+  FRAME_ANDROID_OUTPUT (f)->extracted_text_flags = 0;
+
+  /* Move its selection to the specified position.  */
+  android_update_selection (f, NULL);
+}
+
+/* Notice that point has moved in the F's selected window's selected
+   buffer.  W is the window, and BUFFER is that buffer.  */
+
+static void
+android_set_point (struct frame *f, struct window *w,
+                  struct buffer *buffer)
+{
+  android_update_selection (f, w);
+}
+
+/* Notice that the composition region on F's old selected window has
+   changed.  */
+
+static void
+android_compose_region_changed (struct frame *f)
+{
+  android_update_selection (f, XWINDOW (f->old_selected_window));
+}
+
+/* Notice that the text conversion has completed.  */
+
+static void
+android_notify_conversion (unsigned long counter)
+{
+  int sval;
+
+  if (last_edit_counter < counter)
+    __atomic_store_n (&last_edit_counter, counter,
+                     __ATOMIC_SEQ_CST);
+
+  sem_getvalue (&edit_sem, &sval);
+
+  if (sval <= 0)
+    sem_post (&edit_sem);
+}
+
+/* Android text conversion interface.  */
+
+static struct textconv_interface text_conversion_interface =
+  {
+    android_reset_conversion,
+    android_set_point,
+    android_compose_region_changed,
+    android_notify_conversion,
+  };
+
+
+
+extern frame_parm_handler android_frame_parm_handlers[];
+
+#endif /* !ANDROID_STUBIFY */
+
+static struct redisplay_interface android_redisplay_interface =
+  {
+#ifndef ANDROID_STUBIFY
+    android_frame_parm_handlers,
+    gui_produce_glyphs,
+    gui_write_glyphs,
+    gui_insert_glyphs,
+    gui_clear_end_of_line,
+    android_scroll_run,
+    android_after_update_window_line,
+    NULL, /* update_window_begin */
+    NULL, /* update_window_end   */
+    android_flip_and_flush,
+    gui_clear_window_mouse_face,
+    gui_get_glyph_overhangs,
+    gui_fix_overlapping_area,
+    android_draw_fringe_bitmap,
+    NULL, /* define_fringe_bitmap */
+    NULL, /* destroy_fringe_bitmap */
+    android_compute_glyph_string_overhangs,
+    android_draw_glyph_string,
+    android_define_frame_cursor,
+    android_clear_frame_area,
+    android_clear_under_internal_border,
+    android_draw_window_cursor,
+    android_draw_vertical_window_border,
+    android_draw_window_divider,
+    NULL,
+    android_show_hourglass,
+    android_hide_hourglass,
+    android_default_font_parameter,
+#endif
+  };
+
+
+
+void
+frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
+{
+  /* This cannot be implemented on Android, and as such is left
+     blank.  */
+}
+
+char *
+get_keysym_name (int keysym)
+{
+  static char buffer[64];
+
+#ifndef ANDROID_STUBIFY
+  android_get_keysym_name (keysym, buffer, 64);
+#else
+  emacs_abort ();
+#endif
+  return buffer;
+}
+
+
+
+/* Create a struct terminal, initialize it with the Android specific
+   functions and make DISPLAY->TERMINAL point to it.  */
+
+static struct terminal *
+android_create_terminal (struct android_display_info *dpyinfo)
+{
+  struct terminal *terminal;
+
+  terminal = create_terminal (output_android,
+                             &android_redisplay_interface);
+  terminal->display_info.android = dpyinfo;
+  dpyinfo->terminal = terminal;
+
+  /* kboard is initialized in android_term_init.  */
+
+#ifndef ANDROID_STUBIFY
+
+  terminal->clear_frame_hook = android_clear_frame;
+  terminal->ring_bell_hook = android_ring_bell;
+  terminal->toggle_invisible_pointer_hook
+    = android_toggle_invisible_pointer;
+  terminal->update_begin_hook = android_update_begin;
+  terminal->update_end_hook = android_update_end;
+  terminal->read_socket_hook = android_read_socket;
+  terminal->frame_up_to_date_hook = android_frame_up_to_date;
+  terminal->buffer_flipping_unblocked_hook
+    = android_buffer_flipping_unblocked_hook;
+  terminal->defined_color_hook = android_defined_color;
+  terminal->query_frame_background_color
+    = android_query_frame_background_color;
+  terminal->query_colors = android_query_colors;
+  terminal->mouse_position_hook = android_mouse_position;
+  terminal->get_focus_frame = android_get_focus_frame;
+  terminal->focus_frame_hook = android_focus_frame;
+  terminal->frame_rehighlight_hook = android_frame_rehighlight_hook;
+  terminal->frame_raise_lower_hook = android_frame_raise_lower;
+  terminal->frame_visible_invisible_hook
+    = android_make_frame_visible_invisible;
+  terminal->fullscreen_hook = android_fullscreen_hook;
+  terminal->iconify_frame_hook = android_iconify_frame;
+  terminal->set_window_size_hook = android_set_window_size;
+  terminal->set_frame_offset_hook = android_set_offset;
+  terminal->set_frame_alpha_hook = android_set_alpha;
+  terminal->set_new_font_hook = android_new_font;
+  terminal->set_bitmap_icon_hook = android_bitmap_icon;
+  terminal->implicit_set_name_hook = android_implicitly_set_name;
+  terminal->menu_show_hook = android_menu_show;
+  terminal->popup_dialog_hook = android_popup_dialog;
+  terminal->change_tab_bar_height_hook = android_change_tab_bar_height;
+  terminal->change_tool_bar_height_hook = android_change_tool_bar_height;
+  terminal->set_scroll_bar_default_width_hook
+    = android_set_scroll_bar_default_width;
+  terminal->set_scroll_bar_default_height_hook
+    = android_set_scroll_bar_default_height;
+  terminal->free_pixmap = android_free_pixmap_hook;
+  terminal->delete_frame_hook = android_delete_frame;
+  terminal->delete_terminal_hook = android_delete_terminal;
+
+#else
+  emacs_abort ();
+#endif
+
+  return terminal;
+}
+
+/* Initialize the Android terminal interface.  The display connection
+   has already been set up by the system at this point.  */
+
+void
+android_term_init (void)
+{
+  struct terminal *terminal;
+  struct android_display_info *dpyinfo;
+  Lisp_Object color_file, color_map;
+
+  dpyinfo = xzalloc (sizeof *dpyinfo);
+  terminal = android_create_terminal (dpyinfo);
+  terminal->kboard = allocate_kboard (Qandroid);
+  terminal->kboard->reference_count++;
+
+  dpyinfo->n_planes = 24;
+
+  /* This function should only be called once at startup.  */
+  eassert (!x_display_list);
+  x_display_list = dpyinfo;
+
+  dpyinfo->name_list_element
+    = Fcons (build_pure_c_string ("android"), Qnil);
+
+  color_file = Fexpand_file_name (build_string ("rgb.txt"),
+                                 Vdata_directory);
+  color_map = Fx_load_color_file (color_file);
+
+  if (NILP (color_map))
+    fatal ("Could not read %s.\n", SDATA (color_file));
+
+  dpyinfo->color_map = color_map;
+
+#ifndef ANDROID_STUBIFY
+  dpyinfo->resx = android_pixel_density_x;
+  dpyinfo->resy = android_pixel_density_y;
+  dpyinfo->font_resolution = android_scaled_pixel_density;
+#endif /* ANDROID_STUBIFY */
+
+  /* https://lists.gnu.org/r/emacs-devel/2015-11/msg00194.html  */
+  dpyinfo->smallest_font_height = 1;
+  dpyinfo->smallest_char_width = 1;
+
+  terminal->name = xstrdup ("android");
+
+  /* The display "connection" is now set up, and it must never go
+     away.  */
+  terminal->reference_count = 30000;
+
+  /* Set the baud rate to the same value it gets set to on X.  */
+  baud_rate = 19200;
+
+#ifndef ANDROID_STUBIFY
+  sem_init (&edit_sem, false, 0);
+  register_textconv_interface (&text_conversion_interface);
+#endif
+}
+
+
+
+/* Set Vandroid_build_fingerprint to a reasonable value, and also
+   Vandroid_build_manufacturer.  */
+
+static void
+android_set_build_fingerprint (void)
+{
+#ifdef ANDROID_STUBIFY
+  Vandroid_build_fingerprint = Qnil;
+#else
+  jclass class;
+  jfieldID field;
+  jobject string;
+  const char *data;
+
+  /* Set class to NULL so freeing an uninitialized local ref can be
+     avoided.  */
+  class = NULL;
+
+  /* Likewise for string.  */
+  string = NULL;
+
+  if (!android_init_gui)
+    goto fail;
+  else
+    {
+      /* Obtain Build.FINGERPRINT.  Clear exceptions after each query;
+        JNI can't find Build.FINGERPRIN on some systems.  */
+
+      class = (*android_java_env)->FindClass (android_java_env,
+                                             "android/os/Build");
+      (*android_java_env)->ExceptionClear (android_java_env);
+
+      if (!class)
+       goto fail;
+
+      field = (*android_java_env)->GetStaticFieldID (android_java_env,
+                                                    class,
+                                                    "FINGERPRINT",
+                                                    "Ljava/lang/String;");
+      (*android_java_env)->ExceptionClear (android_java_env);
+
+      if (!field)
+       goto fail;
+
+      string
+       = (*android_java_env)->GetStaticObjectField (android_java_env,
+                                                    class, field);
+      (*android_java_env)->ExceptionClear (android_java_env);
+
+      if (!string)
+       goto fail;
+
+      data = (*android_java_env)->GetStringUTFChars (android_java_env,
+                                                    string, NULL);
+      (*android_java_env)->ExceptionClear (android_java_env);
+
+      if (!data)
+       goto fail;
+
+      Vandroid_build_fingerprint = build_string_from_utf8 (data);
+      (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+                                                 string, data);
+
+      /* Now obtain Build.MANUFACTURER.  */
+
+      ANDROID_DELETE_LOCAL_REF (string);
+      string = NULL;
+
+      field = (*android_java_env)->GetStaticFieldID (android_java_env,
+                                                    class,
+                                                    "MANUFACTURER",
+                                                    "Ljava/lang/String;");
+      (*android_java_env)->ExceptionClear (android_java_env);
+
+      if (!field)
+       goto fail;
+
+      string
+       = (*android_java_env)->GetStaticObjectField (android_java_env,
+                                                    class, field);
+      (*android_java_env)->ExceptionClear (android_java_env);
+
+      if (!string)
+       goto fail;
+
+      data = (*android_java_env)->GetStringUTFChars (android_java_env,
+                                                    string, NULL);
+      (*android_java_env)->ExceptionClear (android_java_env);
+
+      if (!data)
+       goto fail;
+
+      Vandroid_build_manufacturer = build_string_from_utf8 (data);
+      (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+                                                 string, data);
+    }
+
+  if (string)
+    ANDROID_DELETE_LOCAL_REF (string);
+
+  ANDROID_DELETE_LOCAL_REF (class);
+
+  return;
+
+ fail:
+  if (class)
+    ANDROID_DELETE_LOCAL_REF (class);
+
+  Vandroid_build_fingerprint = Qnil;
+  Vandroid_build_manufacturer = Qnil;
+#endif
+}
+
+void
+syms_of_androidterm (void)
+{
+  Fprovide (Qandroid, Qnil);
+
+  DEFVAR_LISP ("android-wait-for-event-timeout",
+              Vandroid_wait_for_event_timeout,
+    doc: /* How long to wait for Android events.
+
+Emacs will wait up to this many seconds to receive events after
+making changes which affect the state of the graphical interface.
+Under some situations this can take an indefinite amount of time,
+so it is important to limit the wait.
+
+If set to a non-float value, there will be no wait at all.  */);
+  Vandroid_wait_for_event_timeout = make_float (0.1);
+
+  DEFVAR_BOOL ("x-use-underline-position-properties",
+              x_use_underline_position_properties,
+     doc: /* SKIP: real doc in xterm.c.  */);
+  x_use_underline_position_properties = true;
+  DEFSYM (Qx_use_underline_position_properties,
+         "x-use-underline-position-properties");
+
+  DEFVAR_BOOL ("x-underline-at-descent-line",
+              x_underline_at_descent_line,
+     doc: /* SKIP: real doc in xterm.c.  */);
+  x_underline_at_descent_line = false;
+
+  DEFVAR_LISP ("android-build-fingerprint", Vandroid_build_fingerprint,
+    doc: /* String identifying the device's OS version.
+This is a string that uniquely identifies the version of Android
+Emacs is running on.  */);
+  Vandroid_build_fingerprint = Qnil;
+
+  DEFVAR_LISP ("android-build-manufacturer", Vandroid_build_manufacturer,
+    doc: /* Name of the developer of the running version of Android.  */);
+  Vandroid_build_manufacturer = Qnil;
+
+  /* Only defined so loadup.el loads scroll-bar.el.  */
+  DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
+    doc: /* SKIP: real doc in xterm.c.  */);
+  Vx_toolkit_scroll_bars = Qnil;
+
+  /* Avoid dumping Vandroid_build_fingerprint.  */
+  pdumper_do_now_and_after_load (android_set_build_fingerprint);
+
+  DEFSYM (Qx_underline_at_descent_line, "x-underline-at-descent-line");
+}
+
+void
+mark_androidterm (void)
+{
+  if (x_display_list)
+    mark_object (x_display_list->color_map);
+}
diff --git a/src/androidterm.h b/src/androidterm.h
new file mode 100644
index 00000000000..e75d46b1dfb
--- /dev/null
+++ b/src/androidterm.h
@@ -0,0 +1,481 @@
+/* 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/>.  */
+
+#ifndef _ANDROID_TERM_H_
+#define _ANDROID_TERM_H_
+
+#include "androidgui.h"
+#include "frame.h"
+#include "character.h"
+#include "dispextern.h"
+#include "font.h"
+
+struct android_bitmap_record
+{
+  /* The image backing the bitmap and its mask.  */
+  android_pixmap pixmap, mask;
+
+  /* The file from which it comes.  */
+  char *file;
+
+  /* The number of references to it.  */
+  int refcount;
+
+  /* The height and width and the depth.  */
+  int height, width, depth;
+
+  /* Whether or not there is a mask.  */
+  bool have_mask;
+};
+
+struct android_display_info
+{
+  /* Chain of all struct android_display_info structures.  */
+  struct android_display_info *next;
+
+  /* The terminal.  */
+  struct terminal *terminal;
+
+  /* The root window.  This field is unused.  */
+  Emacs_Window root_window;
+
+  /* List possibly used only for the font cache but probably used for
+     something else too.  */
+  Lisp_Object name_list_element;
+
+  /* List of predefined X colors.  */
+  Lisp_Object color_map;
+
+  /* DPI of the display.  */
+  double resx, resy;
+
+  /* DPI used to convert font point sizes into pixel dimensions.
+     This is resy adjusted by a fixed scaling factor specified by
+     the user.  */
+  double font_resolution;
+
+  /* Scratch GC for drawing a cursor in a non-default face. */
+  struct android_gc *scratch_cursor_gc;
+
+  /* Mouse highlight information.  */
+  Mouse_HLInfo mouse_highlight;
+
+  /* Number of planes on this screen.  Always 24.  */
+  int n_planes;
+
+  /* Mask of things causing the mouse to be grabbed.  */
+  int grabbed;
+
+  /* Minimum width over all characters in all fonts in font_table.  */
+  int smallest_char_width;
+
+  /* Minimum font height over all fonts in font_table.  */
+  int smallest_font_height;
+
+  /* The number of fonts opened for this display.  */
+  int n_fonts;
+
+  /* Pointer to bitmap records.  */
+  struct android_bitmap_record *bitmaps;
+
+  /* Allocated size of bitmaps field.  */
+  ptrdiff_t bitmaps_size;
+
+  /* Last used bitmap index.  */
+  ptrdiff_t bitmaps_last;
+
+  /* The frame currently with the input focus.  */
+  struct frame *focus_frame;
+
+  /* The last frame mentioned in a focus event.  */
+  struct frame *x_focus_event_frame;
+
+  /* The frame which currently has the visual highlight, and should
+     get keyboard input.  It points to the focus frame's selected
+     window's frame, but can differ.  */
+  struct frame *highlight_frame;
+
+  /* The frame waiting to be auto-raised in android_read_socket.  */
+  struct frame *pending_autoraise_frame;
+
+  /* The frame where the mouse was the last time a button event
+     happened.  */
+  struct frame *last_mouse_frame;
+
+  /* The frame where the mouse was the last time the mouse glyph
+     changed.  */
+  struct frame *last_mouse_glyph_frame;
+
+  /* The frame where the mouse was the last time mouse motion
+     happened.  */
+  struct frame *last_mouse_motion_frame;
+
+  /* Position where the mouse was last time we reported a motion.
+     This is a position on last_mouse_motion_frame.  It is used in to
+     report the mouse position as well: see
+     android_mouse_position.  */
+  int last_mouse_motion_x, last_mouse_motion_y;
+
+  /* Where the mouse was the last time the mouse moved.  */
+  Emacs_Rectangle last_mouse_glyph;
+
+  /* The time of the last mouse movement.  */
+  Time last_mouse_movement_time;
+
+  /* ID of the last menu event received.  -1 means Emacs is waiting
+     for a context menu event.  */
+  int menu_event_id;
+
+  /* The invisible cursor used for pointer blanking.  */
+  android_cursor invisible_cursor;
+};
+
+/* Structure representing a single tool (finger or stylus) pressed
+   onto a frame.  */
+
+struct android_touch_point
+{
+  /* The next tool on this list.  */
+  struct android_touch_point *next;
+
+  /* The tool ID and the last known X and Y positions.  */
+  int tool_id, x, y;
+
+  /* Whether or not the tool is pressed on the tool bar.  */
+  bool tool_bar_p;
+};
+
+struct android_output
+{
+  /* Graphics contexts for the default font.  */
+  struct android_gc *normal_gc, *reverse_gc, *cursor_gc;
+
+  /* The window used for this frame.  */
+  Emacs_Window window;
+
+  /* Unused field.  */
+  Emacs_Window parent_desc;
+
+  /* Default ASCII font of this frame.  */
+  struct font *font;
+
+  /* The baseline offset of the default ASCII font.  */
+  int baseline_offset;
+
+  /* If a fontset is specified for this frame instead of font, this
+     value contains an ID of the fontset, else -1.  */
+  int fontset;
+
+  /* Various colors.  */
+  unsigned long cursor_pixel;
+  unsigned long mouse_pixel;
+  unsigned long cursor_foreground_pixel;
+
+  /* Foreground color for scroll bars.  A value of -1 means use the
+     default (black for non-toolkit scroll bars).  */
+  unsigned long scroll_bar_foreground_pixel;
+
+  /* Background color for scroll bars.  A value of -1 means use the
+     default (background color of the frame for non-toolkit scroll
+     bars).  */
+  unsigned long scroll_bar_background_pixel;
+
+  /* Cursors associated with this frame.  */
+  Emacs_Cursor text_cursor;
+  Emacs_Cursor nontext_cursor;
+  Emacs_Cursor modeline_cursor;
+  Emacs_Cursor hand_cursor;
+  Emacs_Cursor hourglass_cursor;
+  Emacs_Cursor horizontal_drag_cursor;
+  Emacs_Cursor vertical_drag_cursor;
+  Emacs_Cursor current_cursor;
+  Emacs_Cursor left_edge_cursor;
+  Emacs_Cursor top_left_corner_cursor;
+  Emacs_Cursor top_edge_cursor;
+  Emacs_Cursor top_right_corner_cursor;
+  Emacs_Cursor right_edge_cursor;
+  Emacs_Cursor bottom_right_corner_cursor;
+  Emacs_Cursor bottom_edge_cursor;
+  Emacs_Cursor bottom_left_corner_cursor;
+
+  /* Whether or not the hourglass cursor is being displayed.  */
+  bool hourglass;
+
+  /* This is the Emacs structure for the display this frame is on.  */
+  struct android_display_info *display_info;
+
+  /* True if this frame was ever previously visible.  */
+  bool_bf has_been_visible : 1;
+
+  /* True if this frame's alpha value is the same for both the active
+     and inactive states.  */
+  bool_bf alpha_identical_p : 1;
+
+  /* Flag that indicates whether or not the frame contents are
+     complete and can be safely flushed while handling async
+     input.  */
+  bool_bf complete : 1;
+
+  /* True that indicates whether or not a buffer flip is required
+     because the frame contents have been dirtied.  */
+  bool_bf need_buffer_flip : 1;
+
+  /* Whether or not the input method should be notified every time the
+     position of this frame's selected window changes.  */
+  bool_bf need_cursor_updates : 1;
+
+  /* Relief GCs, colors etc.  */
+  struct relief {
+    struct android_gc *gc;
+    unsigned long pixel;
+  } black_relief, white_relief;
+
+  /* The background for which the above relief GCs were set up.
+     They are changed only when a different background is involved.  */
+  unsigned long relief_background;
+
+  /* Focus state.  Only present for consistency with X; it is actually
+     a boolean.  */
+  int focus_state;
+
+  /* List of all tools (either styluses or fingers) pressed onto the
+     frame.  */
+  struct android_touch_point *touch_points;
+
+  /* Flags associated with the last request to obtain ``extracted
+     text''.  */
+  int extracted_text_flags;
+
+  /* Token asssociated with that request.  */
+  int extracted_text_token;
+
+  /* The number of characters of extracted text wanted by the IM.  */
+  int extracted_text_hint;
+};
+
+enum
+  {
+    /* Values for focus_state, used as bit mask.  EXPLICIT means we
+       received a FocusIn for the frame and know it has the focus.
+       IMPLICIT means we received an EnterNotify and the frame may
+       have the focus if no window manager is running.  FocusOut and
+       LeaveNotify clears EXPLICIT/IMPLICIT. */
+    FOCUS_NONE     = 0,
+    FOCUS_IMPLICIT = 1,
+    FOCUS_EXPLICIT = 2
+  };
+
+/* Return the Android output data for frame F.  */
+#define FRAME_ANDROID_OUTPUT(f)        ((f)->output_data.android)
+#define FRAME_OUTPUT_DATA(f)   ((f)->output_data.android)
+
+/* Return the Android window used for displaying data in frame F.  */
+#define FRAME_ANDROID_WINDOW(f)        ((f)->output_data.android->window)
+#define FRAME_NATIVE_WINDOW(f) ((f)->output_data.android->window)
+
+/* Return the need-buffer-flip flag for frame F.  */
+#define FRAME_ANDROID_NEED_BUFFER_FLIP(f)      \
+  ((f)->output_data.android->need_buffer_flip)
+
+/* Return the drawable used for rendering to frame F and mark the
+   frame as needing a buffer flip later.  There's no easy way to run
+   code after any drawing command, but code can be run whenever
+   someone asks for the handle necessary to draw.  */
+#define FRAME_ANDROID_DRAWABLE(f)                      \
+  (((f))->output_data.android->need_buffer_flip = true, \
+   FRAME_ANDROID_WINDOW ((f)))
+
+/* Return whether or not the frame F has been completely drawn.  Used
+   while handling async input.  */
+#define FRAME_ANDROID_COMPLETE_P(f)            \
+  ((f)->output_data.android->complete)
+
+#define FRAME_FONT(f)          ((f)->output_data.android->font)
+#define FRAME_FONTSET(f)       ((f)->output_data.android->fontset)
+
+#define FRAME_BASELINE_OFFSET(f)               \
+  ((f)->output_data.android->baseline_offset)
+
+/* This gives the android_display_info structure for the display F is
+   on.  */
+#define FRAME_DISPLAY_INFO(f) ((f)->output_data.android->display_info)
+
+/* Some things for X compatibility.  */
+#define BLACK_PIX_DEFAULT(f) 0
+#define WHITE_PIX_DEFAULT(f) 0xffffffff
+
+/* Android-specific scroll bar stuff.  */
+
+/* We represent scroll bars as lisp vectors.  This allows us to place
+   references to them in windows without worrying about whether we'll
+   end up with windows referring to dead scroll bars; the garbage
+   collector will free it when its time comes.
+
+   We use struct scroll_bar as a template for accessing fields of the
+   vector.  */
+
+struct scroll_bar
+{
+  /* These fields are shared by all vectors.  */
+  union vectorlike_header header;
+
+  /* The window we're a scroll bar for.  */
+  Lisp_Object window;
+
+  /* The next and previous in the chain of scroll bars in this frame.  */
+  Lisp_Object next, prev;
+
+  /* Fields after 'prev' are not traced by the GC.  */
+
+  /* The X window representing this scroll bar.  */
+  Emacs_Window x_window;
+
+  /* The position and size of the scroll bar in pixels, relative to the
+     frame.  */
+  int top, left, width, height;
+
+  /* The starting and ending positions of the handle, relative to the
+     handle area (i.e. zero is the top position, not
+     SCROLL_BAR_TOP_BORDER).  If they're equal, that means the handle
+     hasn't been drawn yet.
+
+     These are not actually the locations where the beginning and end
+     are drawn; in order to keep handles from becoming invisible when
+     editing large files, we establish a minimum height by always
+     drawing handle bottoms VERTICAL_SCROLL_BAR_MIN_HANDLE pixels below
+     where they would be normally; the bottom and top are in a
+     different coordinate system.  */
+  int start, end;
+
+  /* If the scroll bar handle is currently being dragged by the user,
+     this is the number of pixels from the top of the handle to the
+     place where the user grabbed it.  If the handle isn't currently
+     being dragged, this is -1.  */
+  int dragging;
+
+  /* True if the scroll bar is horizontal.  */
+  bool horizontal;
+};
+
+/* Turning a lisp vector value into a pointer to a struct scroll_bar.  */
+#define XSCROLL_BAR(vec) ((struct scroll_bar *) XVECTOR (vec))
+
+
+
+/* This is a chain of structures for all the Android displays
+   currently in use.  There is only ever one, but the rest of Emacs is
+   written with systems on which there can be many in mind.  */
+extern struct android_display_info *x_display_list;
+
+
+
+/* Start of function definitions.  These should be a neat subset of
+   the same ones in xterm.h, and come in the same order.  */
+
+/* From androidfns.c.  */
+
+extern void android_free_gcs (struct frame *);
+extern void android_default_font_parameter (struct frame *, Lisp_Object);
+extern void android_set_preeditarea (struct window *, int, int);
+
+/* Defined in androidterm.c.  */
+
+extern void android_term_init (void);
+extern void android_set_window_size (struct frame *, bool, int, int);
+extern void android_iconify_frame (struct frame *);
+extern void android_make_frame_visible (struct frame *);
+extern void android_make_frame_invisible (struct frame *);
+extern void android_free_frame_resources (struct frame *);
+
+extern int android_parse_color (struct frame *, const char *,
+                               Emacs_Color *);
+extern bool android_alloc_nearest_color (struct frame *, Emacs_Color *);
+extern void android_query_colors (struct frame *, Emacs_Color *, int);
+extern void android_clear_under_internal_border (struct frame *);
+
+extern void syms_of_androidterm (void);
+extern void mark_androidterm (void);
+
+/* Defined in androidfns.c.  */
+
+extern void android_change_tab_bar_height (struct frame *, int);
+extern void android_change_tool_bar_height (struct frame *, int);
+extern void android_set_scroll_bar_default_width (struct frame *);
+extern void android_set_scroll_bar_default_height (struct frame *);
+extern bool android_defined_color (struct frame *, const char *,
+                                  Emacs_Color *, bool, bool);
+extern void android_implicitly_set_name (struct frame *, Lisp_Object,
+                                        Lisp_Object);
+extern void android_explicitly_set_name (struct frame *, Lisp_Object,
+                                        Lisp_Object);
+
+extern void syms_of_androidfns (void);
+
+/* Defined in androidfont.c.  */
+
+extern struct font_driver androidfont_driver;
+
+extern void init_androidfont (void);
+extern void syms_of_androidfont (void);
+
+extern void android_finalize_font_entity (struct font_entity *);
+
+/* Defined in androidmenu.c.  */
+
+#ifndef ANDROID_STUBIFY
+
+extern unsigned int current_menu_serial;
+
+#endif
+
+extern Lisp_Object android_menu_show (struct frame *, int, int, int,
+                                     Lisp_Object, const char **);
+extern Lisp_Object android_popup_dialog (struct frame *, Lisp_Object,
+                                        Lisp_Object);
+
+extern void init_androidmenu (void);
+extern void syms_of_androidmenu (void);
+
+/* Defined in sfntfont-android.c.  */
+
+extern const struct font_driver android_sfntfont_driver;
+
+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))
+#define RED_FROM_ULONG(color)  (((color) >> 16) & 0xff)
+#define GREEN_FROM_ULONG(color)        (((color) >> 8) & 0xff)
+#define BLUE_FROM_ULONG(color) ((color) & 0xff)
+
+
+
+#endif /* _ANDROID_TERM_H_ */
diff --git a/src/androidvfs.c b/src/androidvfs.c
new file mode 100644
index 00000000000..4234e337acb
--- /dev/null
+++ b/src/androidvfs.c
@@ -0,0 +1,7193 @@
+/* Android virtual file-system support for GNU Emacs.
+
+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 <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+#include <dlfcn.h>
+#include <dirent.h>
+#include <errno.h>
+#include <minmax.h>
+#include <string.h>
+#include <semaphore.h>
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <stat-time.h>
+
+#include <linux/ashmem.h>
+
+#include "android.h"
+#include "systime.h"
+#include "blockinput.h"
+
+#if __ANDROID_API__ >= 9
+#include <android/asset_manager.h>
+#include <android/asset_manager_jni.h>
+#else /* __ANDROID_API__ < 9 */
+#include "android-asset.h"
+#endif /* __ANDROID_API__ >= 9 */
+
+#include <android/log.h>
+
+/* This file implements support for the various special-purpose
+   directories found on Android systems through a series of functions
+   that substitute for Unix system call wrappers.  Such directories
+   are not mounted in the Unix virtual file-system, but instead
+   require the use of special system APIs to access; Emacs pretends
+   they are mounted at specific folders within the root directory.
+
+   There are presently two directories: /assets, granting access to
+   asset files stored within the APK, and /content, providing direct
+   access to content URIs (in Android 4.4 and later) and content
+   directory trees (in Android 5.0 and later.)
+
+   Substitutes for the C library `open', `fstat', `close', `fclose',
+   `unlink', `symlink', `rmdir', `rename', `stat' system call wrappers
+   are implemented, which delegate their actions to function tables
+   contained inside ``VFS nodes''.
+
+   The functions of a VFS node are to provide the implementations of
+   the Unix file system operations that can be carried out on the file
+   designated by its name and to connect useful information (such as
+   internal file handles or identifiers) with those file names.  To
+   those ends, there exist several different types of vnodes, each
+   with a different set of functions and supplementary attributes.
+
+   The key to locating the correct vnode for any given file name is an
+   additional file system operation, defined by each node, which
+   ``names'' children.  This operation takes a relative file name and
+   returns a second node designating a constituent sub-file.
+
+   When a file system function is called, it invokes the `name'
+   operation of a special root vnode conceptually located at the top
+   of the Unix file system hierarchy, handing it the complete file
+   name given to it.  This vnode's name operation examines the first
+   component of the relative file name it receives and creates either
+   an asset, content, or Unix vnode, and calls the new vnode's `name'
+   operation with the remainder of the file name.
+
+   The vnode(s) created by each `name' operation may in turn create
+   different vnodes based on the components of the names they have
+   been provided that are used to repeat this process until no
+   components remain.  The vnode created for the last component of the
+   file name will provide its file system operations or be passed as
+   an argument to other file system operations to which the file has
+   been passed as an argument.
+
+   The substitute functions defined have two caveats, which however
+   don't prove problematic in an Emacs context: the first is that the
+   treatment of `..' is inconsistent with Unix, and has not really
+   been tested, while the second is that errno values do not always
+   conform to what the corresponding Unix system calls may return.
+   These caveats are described in more detail inside the last few
+   pages of this file.  */
+
+/* Structure describing an array of VFS operations.  */
+
+struct android_vnode;
+
+struct android_vdir
+{
+  /* Return a `struct dirent' describing the next file in this
+     directory stream, or NULL if the stream has reached its end.  */
+  struct dirent *(*readdir) (struct android_vdir *);
+
+  /* Close and release all resources allocated for this directory
+     stream.  */
+  void (*closedir) (struct android_vdir *);
+
+  /* Return a ``file descriptor'' tied to this directory stream.  */
+  int (*dirfd) (struct android_vdir *);
+};
+
+struct android_vops
+{
+  /* Name a child of the given VFS node, which should be a
+     directory.
+
+     LENGTH should be the length of NAME, excluding that of any
+     trailing NULL byte.
+
+     NAME should be a normalized and NULL-terminated relative file
+     name; it may contain a leading separator characters, but no
+     consecutive ones.
+
+     If NAME is empty, create another VFS node designating the same
+     file instead.
+
+     NAME should also be located within writable storage; it may be
+     overwritten as the vnode sees fit.
+
+     Value is a VFS node corresponding to the child, or NULL upon
+     failure.
+
+     A VFS node may be returned even if NAME does not exist, the
+     expectation being that either a later filesystem operation will
+     fail, or will create the file.  */
+  struct android_vnode *(*name) (struct android_vnode *, char *, size_t);
+
+  /* Open the specified VNODE, returning either a file descriptor or
+     an asset file descriptor.
+
+     FLAGS and MODE mean the same as they do to the Unix `open' system
+     call.
+
+     ASSET_P stipulates if an asset file descriptor may be returned;
+     if true, *ASSET may be set to an asset file descriptor.
+
+     If an asset file descriptor is unavailable or ASSET_P is false,
+     *FD will be set to a file descriptor.
+
+     If the vnode cannot be opened, value is -1 with errno set
+     accordingly.  Otherwise, value is 0 if a file descriptor was
+     returned, and 1 if an asset file descriptor was returned.  */
+  int (*open) (struct android_vnode *, int, mode_t, bool,
+              int *, AAsset **);
+
+  /* Close the specified VNODE, releasing all of its resources.
+     Save errno before making system calls that may set it, and
+     restore it to its original value before returning.
+
+     This is unrelated to `android_close', which primarily releases on
+     stat buffers linked to file or asset file descriptors.  */
+  void (*close) (struct android_vnode *);
+
+  /* Unlink the file and the specified VNODE.  Value and errno are the
+     same as Unix `unlink'.  */
+  int (*unlink) (struct android_vnode *);
+
+  /* Create a symlink from the specified VNODE to the target TARGET.
+     Value and errno are the same as `symlink' on Linux (which notably
+     means that errno is set to EPERM if VNODE doesn't support
+     symlinks.)  */
+  int (*symlink) (const char *, struct android_vnode *);
+
+  /* Remove VNODE from its parent directory.  VNODE must be an empty
+     directory.  Value and errno are the same as Unix `rmdir'.  */
+  int (*rmdir) (struct android_vnode *);
+
+  /* Move the file designated by SRC to DST, overwriting DST if
+     KEEP_EXISTING is false.
+
+     If KEEP_EXISTING is true and DST already exists, value is -1 with
+     errno set to EEXIST.
+
+     If VNODE does not natively support checking for a preexisting DST
+     and KEEP_EXISTING is true, value is -1 with errno set to ENOSYS.
+
+     Value is otherwise the same as `rename'.  */
+  int (*rename) (struct android_vnode *, struct android_vnode *, bool);
+
+  /* Return statistics for the specified VNODE.
+     Value and errno are the same as with Unix `stat'.  */
+  int (*stat) (struct android_vnode *, struct stat *);
+
+  /* Return whether or not VNODE is accessible.
+     Value, errno and MODE are the same as with Unix `access'.  */
+  int (*access) (struct android_vnode *, int);
+
+  /* Make a directory designated by VNODE, like Unix `mkdir'.  */
+  int (*mkdir) (struct android_vnode *, mode_t);
+
+  /* Change the access mode of the provided VNODE to MODE.  Value is
+     the same as with `chmod'.  FLAGS is passed verbatim from the call
+     to the delegating at-func, and is probably
+     AT_SYMLINK_NOFOLLOW.  */
+  int (*chmod) (struct android_vnode *, mode_t, int);
+
+  /* Open the specified VNODE as a directory.
+     Value is a ``directory handle'', or NULL upon failure.  */
+  struct android_vdir *(*opendir) (struct android_vnode *);
+};
+
+struct android_vnode
+{
+  /* Operations associated with this vnode.  */
+  struct android_vops *ops;
+
+  /* Type of this vnode and its flags.  */
+  short type, flags;
+};
+
+/* Structure describing a special named vnode relative to the root
+   vnode, or another directory vnode.  */
+
+struct android_special_vnode
+{
+  /* The name of the special file.  */
+  const char *name;
+
+  /* The length of that name.  */
+  size_t length;
+
+  /* Function called to create the initial vnode from the rest of the
+     component.  */
+  struct android_vnode *(*initial) (char *, size_t);
+};
+
+enum android_vnode_type
+  {
+    ANDROID_VNODE_UNIX,
+    ANDROID_VNODE_AFS,
+    ANDROID_VNODE_CONTENT,
+    ANDROID_VNODE_CONTENT_AUTHORITY,
+    ANDROID_VNODE_SAF_ROOT,
+    ANDROID_VNODE_SAF_TREE,
+    ANDROID_VNODE_SAF_FILE,
+    ANDROID_VNODE_SAF_NEW,
+  };
+
+
+
+/* Structure describing the android.database.Cursor class.  */
+
+struct android_cursor_class
+{
+  jclass class;
+  jmethodID close;
+};
+
+/* Structure describing the EmacsDirectoryEntry class.  */
+
+struct emacs_directory_entry_class
+{
+  jclass class;
+  jfieldID d_type;
+  jfieldID d_name;
+};
+
+/* Structure describing the android.os.ParcelFileDescriptor class used
+   to wrap file descriptors sent over IPC.  */
+
+struct android_parcel_file_descriptor_class
+{
+  jclass class;
+  jmethodID close;
+  jmethodID get_fd;
+  jmethodID detach_fd;
+};
+
+/* The java.lang.String class.  */
+static jclass java_string_class;
+
+/* Fields and methods associated with the Cursor class.  */
+static struct android_cursor_class cursor_class;
+
+/* Fields and methods associated with the EmacsDirectoryEntry
+   class.  */
+static struct emacs_directory_entry_class entry_class;
+
+/* Fields and methods associated with the ParcelFileDescriptor
+   class.  */
+static struct android_parcel_file_descriptor_class fd_class;
+
+/* Global references to several exception classes.  */
+static jclass file_not_found_exception, security_exception;
+static jclass operation_canceled_exception;
+static jclass unsupported_operation_exception, out_of_memory_error;
+
+/* Initialize `cursor_class' using the given JNI environment ENV.
+   Calling this function is not necessary on Android 4.4 and
+   earlier.  */
+
+static void
+android_init_cursor_class (JNIEnv *env)
+{
+  jclass old;
+
+  cursor_class.class
+    = (*env)->FindClass (env, "android/database/Cursor");
+  eassert (cursor_class.class);
+
+  old = cursor_class.class;
+  cursor_class.class
+    = (jclass) (*env)->NewGlobalRef (env, (jobject) old);
+  (*env)->DeleteLocalRef (env, old);
+
+  if (!cursor_class.class)
+    emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature)           \
+  cursor_class.c_name                                  \
+    = (*env)->GetMethodID (env, cursor_class.class,    \
+                          name, signature);            \
+  assert (cursor_class.c_name);
+  FIND_METHOD (close, "close", "()V");
+#undef FIND_METHOD
+}
+
+/* Initialize `entry_class' using the given JNI environment ENV.
+   Calling this function is not necessary on Android 4.4 and
+   earlier.  */
+
+static void
+android_init_entry_class (JNIEnv *env)
+{
+  jclass old;
+
+  entry_class.class
+    = (*env)->FindClass (env, "org/gnu/emacs/EmacsDirectoryEntry");
+  eassert (entry_class.class);
+
+  old = entry_class.class;
+  entry_class.class
+    = (jclass) (*env)->NewGlobalRef (env, (jobject) old);
+  (*env)->DeleteLocalRef (env, old);
+
+  if (!entry_class.class)
+    emacs_abort ();
+
+  entry_class.d_type = (*env)->GetFieldID (env, entry_class.class,
+                                          "d_type", "I");
+  entry_class.d_name = (*env)->GetFieldID (env, entry_class.class,
+                                          "d_name",
+                                          "Ljava/lang/String;");
+  assert (entry_class.d_type && entry_class.d_name);
+}
+
+
+/* Initialize `fd_class' using the given JNI environment ENV.  Calling
+   this function is not necessary on Android 4.4 and earlier.  */
+
+static void
+android_init_fd_class (JNIEnv *env)
+{
+  jclass old;
+
+  fd_class.class
+    = (*env)->FindClass (env, "android/os/ParcelFileDescriptor");
+  eassert (fd_class.class);
+
+  old = fd_class.class;
+  fd_class.class
+    = (jclass) (*env)->NewGlobalRef (env, (jobject) old);
+  (*env)->DeleteLocalRef (env, old);
+
+  if (!fd_class.class)
+    emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature)           \
+  fd_class.c_name                                      \
+    = (*env)->GetMethodID (env, fd_class.class,                \
+                          name, signature);            \
+  assert (fd_class.c_name);
+  FIND_METHOD (close, "close", "()V");
+  FIND_METHOD (get_fd, "getFd", "()I");
+  FIND_METHOD (detach_fd, "detachFd", "()I");
+#undef FIND_METHOD
+}
+
+
+
+/* Delete redundant instances of `.' and `..' from NAME in-place.
+   NAME must be *LENGTH long, excluding a mandatory trailing NULL
+   byte.
+
+   Transform each directory component in NAME to avoid instances
+   of the `.' and `..' directories.  For example, turn:
+
+     a/../b/c/.
+
+   into
+
+     b/c/
+
+   and return NULL, writing the new length of NAME into *LENGTH.
+
+   If there are more `..' components in NAME than there are normal
+   file name components, return NAME incremented to the position after
+   the first `..' component that cannot be transformed.  For example,
+   if NAME is
+
+     a/../../a
+
+   value will be
+
+     a
+
+   If NAME is a directory separator and LENGTH is 1, return without
+   modifying NAME.  In any other case, omit any leading directory
+   separator when writing to NAME.  This is useful when a vnode that
+   can only be opened as a directory is desired, as this status is
+   made clear by suffixing the file name with a trailing
+   directory separator.  */
+
+static char *
+android_vfs_canonicalize_name (char *name, size_t *length)
+{
+  size_t nellipsis, i;
+  char *last_component, *prev_component, *fill, *orig_name;
+  size_t size;
+
+  /* Special case described in the last paragraph of the comment
+     above.  */
+
+  size = *length;
+  orig_name = name;
+
+  if (*name == '/' && size == 1)
+    return NULL;
+  else if (*name == '/')
+    size -= 1;
+
+  nellipsis = 0; /* Number of ellipsis encountered within the current
+                   file name component, or -1.  */
+  prev_component = NULL; /* Pointer to the separator character of
+                           the component immediately before the
+                           component currently being written.  */
+  last_component = name; /* Pointer to the separator character of
+                           the component currently being read.  */
+  fill = name; /* Pointer to the next character that will be written
+                 within NAME.  */
+
+  /* Adjust name to skip the leading directory separator.  But only
+     after fill is set.  */
+  if (*name == '/')
+    name++;
+
+  for (i = 0; i < size; ++i)
+    {
+      switch (name[i])
+       {
+       case '/':
+         /* See if the previous component was `..' or `.'.
+
+            If it is .., and if no previous directory separator was
+            encountered, return or look up a vnode representing the
+            parent.  */
+
+         if (nellipsis == 2)
+           {
+             /* .. */
+
+             if (!prev_component)
+               goto parent_vnode;
+
+             /* Return to the last component.  */
+             fill = prev_component;
+
+             /* Restore last_component to prev_component, and
+                prev_component back to the component before that.  */
+             last_component = prev_component;
+
+             if (last_component != name)
+               prev_component = memrchr (name, '/',
+                                         last_component - name - 1);
+             else
+               prev_component = NULL;
+
+             /* prev_component may now be NULL.  If last_component is
+                the same as NAME, then fill has really been returned
+                to the beginning of the string, so leave it be.  But
+                if it's something else, then it must be the first
+                separator character in the string, so set
+                prev_component to NAME itself.  */
+
+             if (!prev_component && last_component != name)
+               prev_component = name;
+           }
+         else if (nellipsis == 1)
+           /* If it's ., return to this component.  */
+           fill = last_component;
+         else
+           {
+             /* Record the position of the last directory separator,
+                so NAME can be overwritten from there onwards if `..'
+                or `.' are encountered.  */
+             prev_component = last_component;
+             last_component = fill;
+           }
+
+         /* Allow tracking ellipses again.  */
+         nellipsis = 0;
+         break;
+
+       case '.':
+         if (nellipsis != -1)
+           nellipsis++;
+         break;
+
+       default:
+         nellipsis = -1;
+         break;
+       }
+
+      /* Now copy this character over from NAME.  */
+      *fill++ = name[i];
+    }
+
+  /* See if the previous component was `..' or `.'.
+
+     If it is .., and if no previous directory separator was
+     encountered, return or look up a vnode representing the
+     parent.  */
+
+  if (nellipsis == 2)
+    {
+      /* .. */
+
+      if (!prev_component)
+       /* Look up the rest of the vnode in its parent.  */
+       goto parent_vnode;
+
+      /* Return to the last component.  */
+      fill = prev_component;
+      nellipsis = -2;
+    }
+  else if (nellipsis == 1)
+    {
+      /* If it's ., return to this component.  */
+      fill = last_component;
+      nellipsis = -2;
+    }
+
+  /* Now, if there's enough room and an ellipsis file name was the
+     last component of END, append a trailing `/' before NULL
+     terminating it, indicating that the file name must be a
+     directory.  */
+
+  if (fill + 1 < name + size && nellipsis == -2)
+    *fill++ = '/';
+
+  /* NULL terminate fill.  */
+  *fill = '\0';
+  *length = fill - orig_name;
+  return NULL;
+
+ parent_vnode:
+  /* .. was encountered and the parent couldn't be found through
+     stripping off preceding components.
+
+     Find the parent vnode and name the rest of NAME starting from
+     there.  */
+  return name + i;
+}
+
+
+
+/* Unix vnode implementation.  These VFS nodes directly wrap around
+   the Unix filesystem, with the exception of the root vnode.  */
+
+struct android_unix_vnode
+{
+  /* The vnode data itself.  */
+  struct android_vnode vnode;
+
+  /* Length of the name without a trailing null byte.  */
+  size_t name_length;
+
+  /* Name of the vnode.  */
+  char *name;
+};
+
+struct android_unix_vdir
+{
+  /* The directory function table.  */
+  struct android_vdir vdir;
+
+  /* The directory stream.  */
+  DIR *directory;
+};
+
+/* The vnode representing the root filesystem.  */
+static struct android_unix_vnode root_vnode;
+
+static struct android_vnode *android_unix_name (struct android_vnode *,
+                                               char *, size_t);
+static int android_unix_open (struct android_vnode *, int,
+                             mode_t, bool, int *, AAsset **);
+static void android_unix_close (struct android_vnode *);
+static int android_unix_unlink (struct android_vnode *);
+static int android_unix_symlink (const char *, struct android_vnode *);
+static int android_unix_rmdir (struct android_vnode *);
+static int android_unix_rename (struct android_vnode *,
+                               struct android_vnode *, bool);
+static int android_unix_stat (struct android_vnode *, struct stat *);
+static int android_unix_access (struct android_vnode *, int);
+static int android_unix_mkdir (struct android_vnode *, mode_t);
+static int android_unix_chmod (struct android_vnode *, mode_t, int);
+static struct android_vdir *android_unix_opendir (struct android_vnode *);
+
+/* Vector of VFS operations associated with Unix filesystem VFS
+   nodes.  */
+
+static struct android_vops unix_vfs_ops =
+  {
+    android_unix_name,
+    android_unix_open,
+    android_unix_close,
+    android_unix_unlink,
+    android_unix_symlink,
+    android_unix_rmdir,
+    android_unix_rename,
+    android_unix_stat,
+    android_unix_access,
+    android_unix_mkdir,
+    android_unix_chmod,
+    android_unix_opendir,
+  };
+
+static struct android_vnode *
+android_unix_name (struct android_vnode *vnode, char *name,
+                  size_t length)
+{
+  struct android_unix_vnode *vp, *input, temp;
+  char *fill, *remainder;
+  size_t j;
+
+  /* Canonicalize NAME.  */
+  input = (struct android_unix_vnode *) vnode;
+  remainder = android_vfs_canonicalize_name (name, &length);
+
+  /* If remainder is set, it's a name relative to the parent
+     vnode.  */
+  if (remainder)
+    goto parent_vnode;
+
+  /* Create a new unix vnode.  */
+  vp = xmalloc (sizeof *vp);
+
+  /* If name is empty, duplicate the current vnode.  */
+
+  if (length < 1)
+    {
+      memcpy (vp, vnode, sizeof *vp);
+      vp->name = xstrdup (vp->name);
+      return &vp->vnode;
+    }
+
+  /* Otherwise, fill in the vnode.  */
+
+  vp->vnode.ops = &unix_vfs_ops;
+  vp->vnode.type = ANDROID_VNODE_UNIX;
+  vp->vnode.flags = 0;
+
+  /* Generate the new name of the vnode.  Remove any trailing slash
+     from vp->name.  */
+
+  vp->name_length = input->name_length + length;
+  vp->name = xmalloc (vp->name_length + 2);
+
+  /* Copy the parent name over.  */
+  fill = mempcpy (vp->name, input->name, input->name_length);
+
+  /* Check if it contains a trailing slash.  input->name cannot be
+     empty, as the root vnode's name is `/'.  */
+
+  if (fill[-1] != '/' && *name != '/')
+    /* If not, append a trailing slash and adjust vp->name_length
+       correspondingly.  */
+    *fill++ = '/', vp->name_length++;
+  else if (fill[-1] == '/' && *name == '/')
+    /* If name has a leading slash and fill does too, move fill
+       backwards so that name's slash will override that of fill.  */
+    fill--, vp->name_length--;
+
+  /* Now copy NAME.  */
+  fill = mempcpy (fill, name, length);
+
+  /* And NULL terminate fill.  */
+  *fill = '\0';
+  return &vp->vnode;
+
+ parent_vnode:
+  /* .. was encountered and the parent couldn't be found through
+     stripping off preceding components.
+
+     Find the parent vnode and name the rest of NAME starting from
+     there.  */
+
+  if (input->name_length == 1)
+    /* This is the vnode representing the root directory; just look
+       within itself... */
+    vnode = &root_vnode.vnode;
+  else
+    {
+      /* Create a temporary asset vnode within the parent and use it
+         instead.  First, establish the length of vp->name before its
+         last component.  */
+
+      for (j = input->name_length - 1; j; --j)
+       {
+         if (input->name[j - 1] == '/')
+           break;
+       }
+
+      /* There must be at least one leading directory separator in an
+        asset vnode's `name' field.  */
+
+      if (!j)
+       abort ();
+
+      /* j is now the length of the string minus the size of its last
+        component.  Create a temporary vnode with that as its
+        name.  */
+
+      temp.vnode.ops = &unix_vfs_ops;
+      temp.vnode.type = ANDROID_VNODE_UNIX;
+      temp.vnode.flags = 0;
+      temp.name_length = j;
+      temp.name = xmalloc (j + 1);
+      fill = mempcpy (temp.name, input->name, j);
+      *fill = '\0';
+
+      /* Search for the remainder of NAME relative to its parent.  */
+      vnode = android_unix_name (&temp.vnode, remainder,
+                                strlen (remainder));
+      xfree (temp.name);
+      return vnode;
+    }
+
+  return (*vnode->ops->name) (vnode, remainder, strlen (remainder));
+}
+
+/* Create a Unix vnode representing the given file NAME.  Use this
+   function to create vnodes that aren't rooted in the root VFS
+   node.  */
+
+static struct android_vnode *
+android_unix_vnode (const char *name)
+{
+  struct android_unix_vnode *vp;
+
+  vp = xmalloc (sizeof *vp);
+  vp->vnode.ops = &unix_vfs_ops;
+  vp->vnode.type = ANDROID_VNODE_UNIX;
+  vp->vnode.flags = 0;
+  vp->name_length = strlen (name);
+  vp->name = xstrdup (name);
+  return &vp->vnode;
+}
+
+static int
+android_unix_open (struct android_vnode *vnode, int flags,
+                  mode_t mode, bool asset_p, int *fd,
+                  AAsset **asset)
+{
+  struct android_unix_vnode *vp;
+  int fds;
+
+  vp = (struct android_unix_vnode *) vnode;
+  fds = open (vp->name, flags, mode);
+
+  if (fds < 0)
+    return -1;
+
+  *fd = fds;
+  return 0;
+}
+
+static void
+android_unix_close (struct android_vnode *vnode)
+{
+  struct android_unix_vnode *vp;
+  int save_errno;
+
+  save_errno = errno;
+  vp = (struct android_unix_vnode *) vnode;
+  xfree (vp->name);
+  xfree (vp);
+  errno = save_errno;
+}
+
+static int
+android_unix_unlink (struct android_vnode *vnode)
+{
+  struct android_unix_vnode *vp;
+
+  vp = (struct android_unix_vnode *) vnode;
+  return unlink (vp->name);
+}
+
+static int
+android_unix_symlink (const char *target, struct android_vnode *vnode)
+{
+  struct android_unix_vnode *vp;
+
+  vp = (struct android_unix_vnode *) vnode;
+  return symlink (target, vp->name);
+}
+
+static int
+android_unix_rmdir (struct android_vnode *vnode)
+{
+  struct android_unix_vnode *vp;
+
+  vp = (struct android_unix_vnode *) vnode;
+  return rmdir (vp->name);
+}
+
+static int
+android_unix_rename (struct android_vnode *src,
+                    struct android_vnode *dst,
+                    bool keep_existing)
+{
+  struct android_unix_vnode *vp, *dest;
+
+  if (src->type != dst->type)
+    {
+      /* If the types of both vnodes differ, complain that they're on
+        two different filesystems (which is correct from a abstract
+        viewpoint.)  */
+      errno = EXDEV;
+      return -1;
+    }
+
+  vp = (struct android_unix_vnode *) src;
+  dest = (struct android_unix_vnode *) dst;
+
+  return (keep_existing
+         ? renameat_noreplace (AT_FDCWD, vp->name,
+                               AT_FDCWD, dest->name)
+         : rename (vp->name, dest->name));
+}
+
+static int
+android_unix_stat (struct android_vnode *vnode, struct stat *statb)
+{
+  struct android_unix_vnode *vp;
+
+  vp = (struct android_unix_vnode *) vnode;
+  return stat (vp->name, statb);
+}
+
+static int
+android_unix_access (struct android_vnode *vnode, int mode)
+{
+  struct android_unix_vnode *vp;
+
+  vp = (struct android_unix_vnode *) vnode;
+  return access (vp->name, mode);
+}
+
+static int
+android_unix_mkdir (struct android_vnode *vnode, mode_t mode)
+{
+  struct android_unix_vnode *vp;
+
+  vp = (struct android_unix_vnode *) vnode;
+  return mkdir (vp->name, mode);
+}
+
+static int
+android_unix_chmod (struct android_vnode *vnode, mode_t mode,
+                   int flags)
+{
+  struct android_unix_vnode *vp;
+
+  vp = (struct android_unix_vnode *) vnode;
+  return fchmodat (AT_FDCWD, vp->name, mode, flags);
+}
+
+static struct dirent *
+android_unix_readdir (struct android_vdir *vdir)
+{
+  struct android_unix_vdir *dir;
+
+  dir = (struct android_unix_vdir *) vdir;
+  return readdir (dir->directory);
+}
+
+static void
+android_unix_closedir (struct android_vdir *vdir)
+{
+  struct android_unix_vdir *dir;
+
+  dir = (struct android_unix_vdir *) vdir;
+  closedir (dir->directory);
+  xfree (vdir);
+}
+
+static int
+android_unix_dirfd (struct android_vdir *vdir)
+{
+  struct android_unix_vdir *dir;
+
+  dir = (struct android_unix_vdir *) vdir;
+  return dirfd (dir->directory);
+}
+
+static struct android_vdir *
+android_unix_opendir (struct android_vnode *vnode)
+{
+  struct android_unix_vnode *vp;
+  struct android_unix_vdir *dir;
+  DIR *directory;
+
+  /* Try to opendir the vnode.  */
+  vp = (struct android_unix_vnode *) vnode;
+  directory = opendir (vp->name);
+
+  if (!directory)
+    return NULL;
+
+  dir = xmalloc (sizeof *dir);
+  dir->vdir.readdir = android_unix_readdir;
+  dir->vdir.closedir = android_unix_closedir;
+  dir->vdir.dirfd = android_unix_dirfd;
+  dir->directory = directory;
+  return &dir->vdir;
+}
+
+
+
+/* Asset directory handling functions.  ``directory-tree'' is a file in
+   the root of the assets directory describing its contents.
+
+   See lib-src/asset-directory-tool for more details.  */
+
+/* The Android directory tree.  */
+static const char *directory_tree;
+
+/* The size of the directory tree.  */
+static size_t directory_tree_size;
+
+/* The asset manager being used.  */
+static AAssetManager *asset_manager;
+
+/* Read an unaligned (32-bit) long from the address POINTER.  */
+
+static unsigned int
+android_extract_long (char *pointer)
+{
+  unsigned int number;
+
+  memcpy (&number, pointer, sizeof number);
+  return number;
+}
+
+/* Scan to the file FILE in the asset directory tree.  Return a
+   pointer to the end of that file (immediately before any children)
+   in the directory tree, or NULL if that file does not exist.
+
+   If returning non-NULL, also return the offset to the end of the
+   last subdirectory or file in *LIMIT_RETURN.  LIMIT_RETURN may be
+   NULL.
+
+   FILE must have less than 11 levels of nesting.  If it ends with a
+   trailing slash, then NULL will be returned if it is not actually a
+   directory.  */
+
+static const char *
+android_scan_directory_tree (char *file, size_t *limit_return)
+{
+  char *token, *saveptr, *copy, *copy1, *start, *max, *limit;
+  size_t token_length, ntokens, i;
+  char *tokens[10];
+
+  USE_SAFE_ALLOCA;
+
+  /* Skip past the 5 byte header.  */
+  start = (char *) directory_tree + 5;
+
+  /* Figure out the current limit.  */
+  limit = (char *) directory_tree + directory_tree_size;
+
+  /* Now, split `file' into tokens, with the delimiter being the file
+     name separator.  Look for the file and seek past it.  */
+
+  ntokens = 0;
+  saveptr = NULL;
+  copy = copy1 = xstrdup (file);
+  memset (tokens, 0, sizeof tokens);
+
+  while ((token = strtok_r (copy, "/", &saveptr)))
+    {
+      copy = NULL;
+
+      /* Make sure ntokens is within bounds.  */
+      if (ntokens == ARRAYELTS (tokens))
+       {
+         xfree (copy1);
+         goto fail;
+       }
+
+      tokens[ntokens] = SAFE_ALLOCA (strlen (token) + 1);
+      memcpy (tokens[ntokens], token, strlen (token) + 1);
+      ntokens++;
+    }
+
+  /* Free the copy created for strtok_r.  */
+  xfree (copy1);
+
+  /* If there are no tokens, just return the start of the directory
+     tree.  */
+
+  if (!ntokens)
+    {
+      SAFE_FREE ();
+
+      /* Return the size of the directory tree as the limit.
+         Do not subtract the initial header bytes, as the limit
+         is an offset from the start of the file.  */
+
+      if (limit_return)
+       *limit_return = directory_tree_size;
+
+      return start;
+    }
+
+  /* Loop through tokens, indexing the directory tree each time.  */
+
+  for (i = 0; i < ntokens; ++i)
+    {
+      token = tokens[i];
+
+      /* Figure out how many bytes to compare.  */
+      token_length = strlen (token);
+
+    again:
+
+      /* If this would be past the directory, return NULL.  */
+      if (start + token_length > limit)
+       goto fail;
+
+      /* Now compare the file name.  */
+      if (!memcmp (start, token, token_length))
+       {
+         /* They probably match.  Find the NULL byte.  It must be
+            either one byte past start + token_length, with the last
+            byte a trailing slash (indicating that it is a
+            directory), or just start + token_length.  Return 4 bytes
+            past the next NULL byte.  */
+
+         max = memchr (start, 0, limit - start);
+
+         if (max != start + token_length
+             && !(max == start + token_length + 1
+                  && *(max - 1) == '/'))
+           goto false_positive;
+
+         /* Return it if it exists and is in range, and this is the
+            last token.  Otherwise, set it as start and the limit as
+            start + the offset and continue the loop.  */
+
+         if (max && max + 5 <= limit)
+           {
+             if (i < ntokens - 1)
+               {
+                 start = max + 5;
+                 limit = ((char *) directory_tree
+                          + android_extract_long (max + 1));
+
+                 /* Make sure limit is still in range.  */
+                 if (limit > directory_tree + directory_tree_size
+                     || start > directory_tree + directory_tree_size)
+                   goto fail;
+
+                 continue;
+               }
+
+             /* Now see if max is not a directory and file is.  If
+                file is a directory, then return NULL.  */
+             if (*(max - 1) != '/' && file[strlen (file) - 1] == '/')
+               max = NULL;
+             else
+               {
+                 /* Figure out the limit.  */
+                 if (limit_return)
+                   *limit_return = android_extract_long (max + 1);
+
+                 /* Go to the end of this file.  */
+                 max += 5;
+               }
+
+             SAFE_FREE ();
+             return max;
+           }
+
+         /* Return NULL otherwise.  */
+         __android_log_print (ANDROID_LOG_WARN, __func__,
+                              "could not scan to end of directory tree"
+                              ": %s", file);
+         goto fail;
+       }
+
+    false_positive:
+
+      /* No match was found.  Set start to the next sibling and try
+        again.  */
+
+      start = memchr (start, 0, limit - start);
+
+      if (!start || start + 5 > limit)
+       goto fail;
+
+      start = ((char *) directory_tree
+              + android_extract_long (start + 1));
+
+      /* Make sure start is still in bounds.  */
+
+      if (start > limit)
+       goto fail;
+
+      /* Continue the loop.  */
+      goto again;
+    }
+
+ fail:
+  SAFE_FREE ();
+  return NULL;
+}
+
+/* Return whether or not the directory tree entry DIR is a
+   directory.
+
+   DIR should be a value returned by
+   `android_scan_directory_tree'.  */
+
+static bool
+android_is_directory (const char *dir)
+{
+  /* If the directory is the directory tree, then it is a
+     directory.  */
+  if (dir == directory_tree + 5)
+    return true;
+
+  /* Otherwise, look 5 bytes behind.  If it is `/', then it is a
+     directory.  */
+  return (dir - 6 >= directory_tree
+         && *(dir - 6) == '/');
+}
+
+/* Initialize asset retrieval.  ENV should be a JNI environment for
+   the Emacs thread, and MANAGER should be a local reference to a Java
+   asset manager object created for the Emacs service context.  */
+
+static void
+android_init_assets (JNIEnv *env, jobject manager)
+{
+  AAsset *asset;
+
+  /* Set the asset manager.  */
+  asset_manager = AAssetManager_fromJava (env, manager);
+
+  /* Initialize the directory tree.  */
+  asset = AAssetManager_open (asset_manager, "directory-tree",
+                             AASSET_MODE_BUFFER);
+
+  if (!asset)
+    {
+      __android_log_print (ANDROID_LOG_FATAL, __func__,
+                          "Failed to open directory tree");
+      emacs_abort ();
+    }
+
+  directory_tree = AAsset_getBuffer (asset);
+
+  if (!directory_tree)
+    emacs_abort ();
+
+  /* Now figure out how big the directory tree is, and compare the
+     first few bytes.  */
+  directory_tree_size = AAsset_getLength (asset);
+  if (directory_tree_size < 5
+      || memcmp (directory_tree, "EMACS", 5))
+    {
+      __android_log_print (ANDROID_LOG_FATAL, __func__,
+                          "Directory tree has bad magic");
+      emacs_abort ();
+    }
+
+  /* Hold a VM reference to the asset manager to prevent the native
+     object from being deleted.  */
+  (*env)->NewGlobalRef (env, manager);
+
+  /* Abort if there's no more memory for the global reference.  */
+  if ((*env)->ExceptionCheck (env))
+    abort ();
+}
+
+
+
+/* Asset-to-file descriptor conversion.  */
+
+/* Pointer to the `ASharedMemory_create' function which is loaded
+   dynamically.  */
+static int (*asharedmemory_create) (const char *, size_t);
+
+/* Do the same as android_hack_asset_fd, but use an unlinked temporary
+   file to cater to old Android kernels where ashmem files are not
+   readable.  */
+
+static int
+android_hack_asset_fd_fallback (AAsset *asset)
+{
+  int fd;
+  char filename[PATH_MAX];
+  size_t size;
+  void *mem;
+
+  /* Assets must be small enough to fit in size_t, if off_t is
+     larger.  */
+  size = AAsset_getLength (asset);
+
+  /* Get an unlinked file descriptor from a file in the cache
+     directory, which is guaranteed to only be written to by Emacs.
+     Creating an ashmem file descriptor and reading from it doesn't
+     work on these old Android versions.  */
+
+  snprintf (filename, PATH_MAX, "%s/temp~unlinked.%d",
+           android_cache_dir, getpid ());
+  fd = open (filename, O_CREAT | O_RDWR | O_TRUNC,
+            S_IRUSR | S_IWUSR);
+
+  if (fd < 0)
+    return -1;
+
+  if (unlink (filename))
+    goto fail;
+
+  if (ftruncate (fd, size))
+    goto fail;
+
+  mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0);
+  if (mem == MAP_FAILED)
+    {
+      __android_log_print (ANDROID_LOG_ERROR, __func__,
+                          "mmap: %s", strerror (errno));
+      goto fail;
+    }
+
+  if (AAsset_read (asset, mem, size) != size)
+    {
+      /* Too little was read.  Close the file descriptor and
+        report an error.  */
+      __android_log_print (ANDROID_LOG_ERROR, __func__,
+                          "AAsset_read: %s", strerror (errno));
+      goto fail;
+    }
+
+  munmap (mem, size);
+  return fd;
+
+ fail:
+  close (fd);
+  return -1;
+}
+
+/* Return whether or not shared memory file descriptors can also be
+   read from, and are thus suitable for creating asset files.
+
+   This does not work on some ancient Android systems running old
+   versions of the kernel.  */
+
+static bool
+android_detect_ashmem (void)
+{
+  int fd, rc;
+  void *mem;
+  char test_buffer[10];
+
+  memcpy (test_buffer, "abcdefghi", 10);
+
+  /* Create the file descriptor to be used for the test.  */
+
+  /* Android 28 and earlier let Emacs access /dev/ashmem directly, so
+     prefer that over using ASharedMemory.  */
+
+  if (android_get_current_api_level () <= 28)
+    {
+      fd = open ("/dev/ashmem", O_RDWR);
+
+      if (fd < 0)
+       return false;
+
+      /* An empty name means the memory area will exist until the file
+        descriptor is closed, because no other process can
+        attach.  */
+      rc = ioctl (fd, ASHMEM_SET_NAME, "");
+
+      if (rc < 0)
+       {
+         close (fd);
+         return false;
+       }
+
+      rc = ioctl (fd, ASHMEM_SET_SIZE, sizeof test_buffer);
+
+      if (rc < 0)
+       {
+         close (fd);
+         return false;
+       }
+    }
+  else
+    {
+      /* On the other hand, SELinux restrictions on Android 29 and
+        later require that Emacs use a system service to obtain
+        shared memory.  Load this dynamically, as this service is not
+        available on all versions of the NDK.  */
+
+      if (!asharedmemory_create)
+       {
+         *(void **) (&asharedmemory_create)
+           = dlsym (RTLD_DEFAULT, "ASharedMemory_create");
+
+         if (!asharedmemory_create)
+           {
+             __android_log_print (ANDROID_LOG_FATAL, __func__,
+                                  "dlsym: %s\n",
+                                  strerror (errno));
+             emacs_abort ();
+           }
+       }
+
+      fd = (*asharedmemory_create) ("", sizeof test_buffer);
+
+      if (fd < 0)
+       return false;
+    }
+
+  /* Now map the resource and write the test contents.  */
+
+  mem = mmap (NULL, sizeof test_buffer, PROT_WRITE,
+             MAP_SHARED, fd, 0);
+  if (mem == MAP_FAILED)
+    {
+      close (fd);
+      return false;
+    }
+
+  /* Copy over the test contents.  */
+  memcpy (mem, test_buffer, sizeof test_buffer);
+
+  /* Return anyway even if munmap fails.  */
+  munmap (mem, sizeof test_buffer);
+
+  /* Try to read the content back into test_buffer.  If this does not
+     compare equal to the original string, or the read fails, then
+     ashmem descriptors are not readable on this system.  */
+
+  if ((read (fd, test_buffer, sizeof test_buffer)
+       != sizeof test_buffer)
+      || memcmp (test_buffer, "abcdefghi", sizeof test_buffer))
+    {
+      __android_log_print (ANDROID_LOG_WARN, __func__,
+                          "/dev/ashmem does not produce real"
+                          " temporary files on this system, so"
+                          " Emacs will fall back to creating"
+                          " unlinked temporary files.");
+      close (fd);
+      return false;
+    }
+
+  close (fd);
+  return true;
+}
+
+/* Get a file descriptor backed by a temporary in-memory file for the
+   given asset.  */
+
+static int
+android_hack_asset_fd (AAsset *asset)
+{
+  static bool ashmem_readable_p;
+  static bool ashmem_initialized;
+  int fd, rc;
+  unsigned char *mem;
+  size_t size;
+
+  /* The first time this function is called, try to determine whether
+     or not ashmem file descriptors can be read from.  */
+
+  if (!ashmem_initialized)
+    ashmem_readable_p
+      = android_detect_ashmem ();
+  ashmem_initialized = true;
+
+  /* If it isn't, fall back.  */
+
+  if (!ashmem_readable_p)
+    return android_hack_asset_fd_fallback (asset);
+
+  /* Assets must be small enough to fit in size_t, if off_t is
+     larger.  */
+  size = AAsset_getLength (asset);
+
+  /* Android 28 and earlier let Emacs access /dev/ashmem directly, so
+     prefer that over using ASharedMemory.  */
+
+  if (android_get_current_api_level () <= 28)
+    {
+      fd = open ("/dev/ashmem", O_RDWR);
+
+      if (fd < 0)
+       return -1;
+
+      /* An empty name means the memory area will exist until the file
+        descriptor is closed, because no other process can
+        attach.  */
+      rc = ioctl (fd, ASHMEM_SET_NAME, "");
+
+      if (rc < 0)
+       {
+         __android_log_print (ANDROID_LOG_ERROR, __func__,
+                              "ioctl ASHMEM_SET_NAME: %s",
+                              strerror (errno));
+         close (fd);
+         return -1;
+       }
+
+      rc = ioctl (fd, ASHMEM_SET_SIZE, size);
+
+      if (rc < 0)
+       {
+         __android_log_print (ANDROID_LOG_ERROR, __func__,
+                              "ioctl ASHMEM_SET_SIZE: %s",
+                              strerror (errno));
+         close (fd);
+         return -1;
+       }
+
+      if (!size)
+       return fd;
+
+      /* Now map the resource.  */
+      mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0);
+      if (mem == MAP_FAILED)
+       {
+         __android_log_print (ANDROID_LOG_ERROR, __func__,
+                              "mmap: %s", strerror (errno));
+         close (fd);
+         return -1;
+       }
+
+      if (AAsset_read (asset, mem, size) != size)
+       {
+         /* Too little was read.  Close the file descriptor and
+            report an error.  */
+         __android_log_print (ANDROID_LOG_ERROR, __func__,
+                              "AAsset_read: %s", strerror (errno));
+         close (fd);
+         return -1;
+       }
+
+      /* Return anyway even if munmap fails.  */
+      munmap (mem, size);
+      return fd;
+    }
+
+  /* On the other hand, SELinux restrictions on Android 29 and later
+     require that Emacs use a system service to obtain shared memory.
+     Load this dynamically, as this service is not available on all
+     versions of the NDK.  */
+
+  if (!asharedmemory_create)
+    {
+      *(void **) (&asharedmemory_create)
+       = dlsym (RTLD_DEFAULT, "ASharedMemory_create");
+
+      if (!asharedmemory_create)
+       {
+         __android_log_print (ANDROID_LOG_FATAL, __func__,
+                              "dlsym: %s\n",
+                              strerror (errno));
+         emacs_abort ();
+       }
+    }
+
+  fd = (*asharedmemory_create) ("", size);
+
+  if (fd < 0)
+    {
+      __android_log_print (ANDROID_LOG_ERROR, __func__,
+                          "ASharedMemory_create: %s",
+                          strerror (errno));
+      return -1;
+    }
+
+  /* Now map the resource.  */
+  mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0);
+  if (mem == MAP_FAILED)
+    {
+      __android_log_print (ANDROID_LOG_ERROR, __func__,
+                          "mmap: %s", strerror (errno));
+      close (fd);
+      return -1;
+    }
+
+  if (AAsset_read (asset, mem, size) != size)
+    {
+      /* Too little was read.  Close the file descriptor and
+        report an error.  */
+      __android_log_print (ANDROID_LOG_ERROR, __func__,
+                          "AAsset_read: %s", strerror (errno));
+      close (fd);
+      return -1;
+    }
+
+  /* Return anyway even if munmap fails.  */
+  munmap (mem, size);
+  return fd;
+}
+
+
+
+/* ``Asset file system'' vnode implementation.  These vnodes map to
+   asset files within the application package, provided by the Android
+   ``asset manager''.  */
+
+struct android_afs_vnode
+{
+  /* The vnode data itself.  */
+  struct android_vnode vnode;
+
+  /* Length of the name without a trailing null byte.  */
+  size_t name_length;
+
+  /* Name of the vnode.  */
+  char *name;
+};
+
+struct android_afs_vdir
+{
+  /* The directory function table.  */
+  struct android_vdir vdir;
+
+  /* The next directory stream in `all_afs_vdirs'.  */
+  struct android_afs_vdir *next;
+
+  /* Pointer to the directory in directory_tree.  */
+  char *asset_dir;
+
+  /* And the end of the files in asset_dir.  */
+  char *asset_limit;
+
+  /* Path to the directory relative to /.  */
+  char *asset_file;
+
+  /* File descriptor representing this directory stream, or NULL.  */
+  int fd;
+};
+
+struct android_afs_open_fd
+{
+  /* The next table entry.  */
+  struct android_afs_open_fd *next;
+
+  /* The open file descriptor.  */
+  int fd;
+
+  /* The stat buffer associated with this entry.  */
+  struct stat statb;
+};
+
+static struct android_vnode *android_afs_name (struct android_vnode *,
+                                              char *, size_t);
+static int android_afs_open (struct android_vnode *, int,
+                            mode_t, bool, int *, AAsset **);
+static void android_afs_close (struct android_vnode *);
+static int android_afs_unlink (struct android_vnode *);
+static int android_afs_symlink (const char *, struct android_vnode *);
+static int android_afs_rmdir (struct android_vnode *);
+static int android_afs_rename (struct android_vnode *,
+                              struct android_vnode *, bool);
+static int android_afs_stat (struct android_vnode *, struct stat *);
+static int android_afs_access (struct android_vnode *, int);
+static int android_afs_mkdir (struct android_vnode *, mode_t);
+static int android_afs_chmod (struct android_vnode *, mode_t, int);
+static struct android_vdir *android_afs_opendir (struct android_vnode *);
+
+/* Vector of VFS operations associated with asset VFS nodes.  */
+
+static struct android_vops afs_vfs_ops =
+  {
+    android_afs_name,
+    android_afs_open,
+    android_afs_close,
+    android_afs_unlink,
+    android_afs_symlink,
+    android_afs_rmdir,
+    android_afs_rename,
+    android_afs_stat,
+    android_afs_access,
+    android_afs_mkdir,
+    android_afs_chmod,
+    android_afs_opendir,
+  };
+
+/* Chain consisting of all open asset directory streams.  */
+static struct android_afs_vdir *all_afs_vdirs;
+
+/* List linking open file descriptors to asset information.  This
+   assumes Emacs does not use dup on regular files.  */
+static struct android_afs_open_fd *afs_file_descriptors;
+
+static struct android_vnode *
+android_afs_name (struct android_vnode *vnode, char *name,
+                 size_t length)
+{
+  size_t j;
+  char *remainder, *fill;
+  struct android_afs_vnode *vp, *input;
+  struct android_afs_vnode temp;
+
+  input = (struct android_afs_vnode *) vnode;
+
+  /* Canonicalize NAME.  */
+  remainder = android_vfs_canonicalize_name (name, &length);
+
+  /* If remainder is set, it's a name relative to the parent
+     vnode.  */
+  if (remainder)
+    goto parent_vnode;
+
+  /* Allocate a new vnode.  */
+  vp = xmalloc (sizeof *vp);
+
+  /* See the specified name is empty.  */
+
+  if (length < 1)
+    {
+      memcpy (vp, vnode, sizeof *vp);
+      vp->name = xstrdup (vp->name);
+      return &vp->vnode;
+    }
+
+  /* Recompute length.  */
+  vp->vnode.ops = &afs_vfs_ops;
+  vp->vnode.type = ANDROID_VNODE_AFS;
+  vp->vnode.flags = 0;
+
+  /* Generate the new name of the vnode.  Remove any trailing slash
+     from vp->name.  */
+
+  vp->name_length = input->name_length + length;
+  vp->name = xmalloc (vp->name_length + 2);
+
+  /* Copy the parent name over.  */
+  fill = mempcpy (vp->name, input->name, input->name_length);
+
+  /* Check if it contains a trailing slash.  input->name cannot be
+     empty, as the root vnode's name is `/'.  */
+
+  if (fill[-1] != '/' && *name != '/')
+    /* If not, append a trailing slash and adjust vp->name_length
+       correspondingly.  */
+    *fill++ = '/', vp->name_length++;
+  else if (fill[-1] == '/' && *name == '/')
+    /* If name has a leading slash and fill does too, move fill
+       backwards so that name's slash will override that of fill.  */
+    fill--, vp->name_length--;
+
+  /* Now copy NAME.  */
+  fill = mempcpy (fill, name, length);
+
+  /* And NULL terminate fill.  */
+  *fill = '\0';
+  return &vp->vnode;
+
+ parent_vnode:
+  /* .. was encountered and the parent couldn't be found through
+     stripping off preceding components.
+
+     Find the parent vnode and name the rest of NAME starting from
+     there.  */
+
+  if (input->name_length == 1)
+    /* This is the vnode representing the /assets directory... */
+    vnode = &root_vnode.vnode;
+  else
+    {
+      /* Create a temporary asset vnode within the parent and use it
+         instead.  First, establish the length of vp->name before its
+         last component.  */
+
+      for (j = input->name_length - 1; j; --j)
+       {
+         if (input->name[j - 1] == '/')
+           break;
+       }
+
+      /* There must be at least one leading directory separator in an
+        asset vnode's `name' field.  */
+
+      if (!j)
+       abort ();
+
+      /* j is now the length of the string minus the size of its last
+        component.  Create a temporary vnode with that as its
+        name.  */
+
+      temp.vnode.ops = &afs_vfs_ops;
+      temp.vnode.type = ANDROID_VNODE_AFS;
+      temp.vnode.flags = 0;
+      temp.name_length = j;
+      temp.name = xmalloc (j + 1);
+      fill = mempcpy (temp.name, input->name, j);
+      *fill = '\0';
+
+      /* Search for the remainder of NAME relative to its parent.  */
+      vnode = android_afs_name (&temp.vnode, remainder,
+                               strlen (remainder));
+      xfree (temp.name);
+      return vnode;
+    }
+
+  return (*vnode->ops->name) (vnode, remainder, strlen (remainder));
+}
+
+/* Find the vnode designated by the normalized NAME relative to the
+   root of the asset file system.  NAME may be modified, and must be
+   LENGTH bytes long, excluding its terminating NULL byte.  */
+
+static struct android_vnode *
+android_afs_initial (char *name, size_t length)
+{
+  struct android_afs_vnode temp;
+
+  /* Create a temporary vnode at the root of the asset file
+     system.  */
+
+  temp.vnode.ops = &afs_vfs_ops;
+  temp.vnode.type = ANDROID_VNODE_AFS;
+  temp.vnode.flags = 0;
+  temp.name_length = 1;
+  temp.name = (char *) "/";
+
+  /* Try to name this vnode.  If NAME is empty, it will be duplicated
+     instead.  */
+  return android_afs_name (&temp.vnode, name, length);
+}
+
+/* Make FD close-on-exec.  If any system call fails, do not abort, but
+   log a warning to the system log.  */
+
+static void
+android_close_on_exec (int fd)
+{
+  int flags, rc;
+
+  flags = fcntl (fd, F_GETFD);
+
+  if (flags < 0)
+    {
+      __android_log_print (ANDROID_LOG_WARN, __func__,
+                          "fcntl: %s", strerror (errno));
+      return;
+    }
+
+  rc = fcntl (fd, F_SETFD, flags | O_CLOEXEC);
+
+  if (rc < 0)
+    {
+      __android_log_print (ANDROID_LOG_WARN, __func__,
+                          "fcntl: %s", strerror (errno));
+      return;
+    }
+}
+
+static int
+android_afs_open (struct android_vnode *vnode, int flags,
+                 mode_t mode, bool asset_p, int *fd_return,
+                 AAsset **asset_return)
+{
+  AAsset *asset;
+  struct android_afs_vnode *vp;
+  const char *asset_dir;
+  int fd;
+  struct android_afs_open_fd *info;
+
+  vp = (struct android_afs_vnode *) vnode;
+
+  /* Return suitable error indications for unsupported file
+     operations.  */
+
+  if ((flags & O_WRONLY) || (flags & O_RDWR))
+    {
+      errno = EROFS;
+      return -1;
+    }
+
+  if (flags & O_DIRECTORY)
+    {
+      errno = ENOSYS;
+      return -1;
+    }
+
+  /* Now try to open this asset.  Asset manager APIs expect there to
+     be no trailing directory separator.  */
+  asset = AAssetManager_open (asset_manager, vp->name + 1,
+                             AASSET_MODE_STREAMING);
+
+  /* If it can't be opened, return an error indication.  */
+
+  if (!asset)
+    {
+      /* Scan the directory tree for this file.  */
+      asset_dir = android_scan_directory_tree (vp->name, NULL);
+
+      /* Default errno to ENOTENT.  */
+      errno = ENOENT;
+
+      /* Maybe the caller wants to open a directory vnode as a
+        file?  */
+
+      if (asset_dir && android_is_directory (asset_dir))
+       /* In that case, set errno to ENOSYS.  */
+       errno = ENOSYS;
+
+      return -1;
+    }
+
+  /* An asset has been opened.  If the caller wants a file descriptor,
+     a temporary one must be created and the file contents read
+     inside.  */
+
+  if (!asset_p)
+    {
+      /* Create a shared memory file descriptor containing the asset
+        contents.
+
+         The documentation misleads people into thinking that
+         AAsset_openFileDescriptor does precisely this.  However, it
+         instead returns an offset into any uncompressed assets in the
+         ZIP archive.  This cannot be found in its documentation.  */
+
+      fd = android_hack_asset_fd (asset);
+
+      if (fd == -1)
+       {
+         AAsset_close (asset);
+         errno = EIO;
+         return -1;
+       }
+
+      /* If O_CLOEXEC is specified, make the file descriptor close on
+        exec too.  */
+
+      if (flags & O_CLOEXEC)
+       android_close_on_exec (fd);
+
+      /* Keep a record linking ``hacked'' file descriptors with
+        their file status.  */
+      info = xzalloc (sizeof *info);
+      info->fd   = fd;
+      info->next = afs_file_descriptors;
+
+      /* Fill in some information that will be reported to
+        callers of android_fstat, among others.  */
+      info->statb.st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
+
+      /* Owned by root.  */
+      info->statb.st_uid = 0;
+      info->statb.st_gid = 0;
+
+      /* Concoct a nonexistent device and an inode number.  */
+      info->statb.st_dev = -1;
+      info->statb.st_ino = 0;
+
+      /* Size of the file.  */
+      info->statb.st_size = AAsset_getLength (asset);
+
+      /* Chain info onto afs_file_descriptors.  */
+      afs_file_descriptors = info;
+
+      AAsset_close (asset);
+
+      /* Return the file descriptor.  */
+      *fd_return = fd;
+      return 0;
+    }
+
+  /* Return the asset itself.  */
+  *asset_return = asset;
+  return 1;
+}
+
+static void
+android_afs_close (struct android_vnode *vnode)
+{
+  struct android_afs_vnode *vp;
+  int save_errno;
+
+  save_errno = errno;
+  vp = (struct android_afs_vnode *) vnode;
+  xfree (vp->name);
+  xfree (vp);
+  errno = save_errno;
+}
+
+static int
+android_afs_unlink (struct android_vnode *vnode)
+{
+  const char *dir;
+  struct android_afs_vnode *vp;
+
+  /* If the vnode already exists, return EROFS.  Else, return
+     ENOENT.  */
+
+  vp = (struct android_afs_vnode *) vnode;
+  dir = android_scan_directory_tree (vp->name, NULL);
+
+  if (dir)
+    errno = EROFS;
+  else
+    errno = ENOENT;
+
+  return -1;
+}
+
+static int
+android_afs_symlink (const char *linkname, struct android_vnode *vnode)
+{
+  struct android_afs_vnode *vp;
+
+  /* If this vnode already exists, return EEXIST.  */
+  vp = (struct android_afs_vnode *) vnode;
+
+  if (android_scan_directory_tree (vp->name, NULL))
+    {
+      errno = EEXIST;
+      return -1;
+    }
+
+  /* Symlinks aren't supported on this (read-only) ``file system'',
+     so return -1 with EROFS.  */
+  errno = EROFS;
+  return -1;
+}
+
+static int
+android_afs_rmdir (struct android_vnode *vnode)
+{
+  const char *dir;
+  struct android_afs_vnode *vp;
+
+  /* If the vnode already exists and is a directory, return EROFS.
+     Else, return ENOTDIR or ENOENT.  */
+
+  vp = (struct android_afs_vnode *) vnode;
+  dir = android_scan_directory_tree (vp->name, NULL);
+
+  if (dir && android_is_directory (dir))
+    errno = EROFS;
+  else if (dir)
+    errno = ENOTDIR;
+  else
+    errno = ENOENT;
+
+  return -1;
+}
+
+static int
+android_afs_rename (struct android_vnode *src, struct android_vnode *dst,
+                   bool keep_existing)
+{
+  /* If src and dst are different kinds of vnodes, return EXDEV.
+     Else, return EROFS.  */
+
+  errno = EROFS;
+  if (src->type != dst->type)
+    errno = EXDEV;
+
+  return -1;
+}
+
+static int
+android_afs_stat (struct android_vnode *vnode, struct stat *statb)
+{
+  const char *dir;
+  struct android_afs_vnode *vp;
+  AAsset *asset_desc;
+
+  /* Scan for the vnode to see whether or not it exists.  */
+
+  vp = (struct android_afs_vnode *) vnode;
+  dir = android_scan_directory_tree (vp->name, NULL);
+
+  if (!dir)
+    {
+      /* Return ENOENT; whether the lookup failed because directory
+        components within vp->path weren't really directories is not
+        important to Emacs's error reporting.  */
+      errno = ENOENT;
+      return -1;
+    }
+
+  if (android_is_directory (dir))
+    {
+      memset (statb, 0, sizeof *statb);
+
+      /* Fill in the stat buffer.  */
+      statb->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
+
+      /* Grant search permissions as well.  */
+      statb->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+
+      /* Concoct a nonexistent device and an inode number.  */
+      statb->st_dev = -1;
+      statb->st_ino = 0;
+      return 0;
+    }
+
+  /* AASSET_MODE_STREAMING is fastest here.  */
+  asset_desc = AAssetManager_open (asset_manager, vp->name + 1,
+                                  AASSET_MODE_STREAMING);
+
+  if (!asset_desc)
+    {
+      /* If the asset exists in the directory tree but can't be
+        located by the asset manager, report OOM.  */
+      errno = ENOMEM;
+      return 1;
+    }
+
+  memset (statb, 0, sizeof *statb);
+
+  /* Fill in the stat buffer.  */
+  statb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
+  statb->st_dev = -1;
+  statb->st_ino = 0;
+  statb->st_size = AAsset_getLength (asset_desc);
+
+  /* Close the asset.  */
+  AAsset_close (asset_desc);
+  return 0;
+}
+
+static int
+android_afs_access (struct android_vnode *vnode, int mode)
+{
+  const char *dir;
+  struct android_afs_vnode *vp;
+
+  /* Validate MODE.  */
+
+  if (mode != F_OK && !(mode & (W_OK | X_OK | R_OK)))
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  /* Scan for the vnode to see whether or not it exists.  */
+
+  vp = (struct android_afs_vnode *) vnode;
+  dir = android_scan_directory_tree (vp->name, NULL);
+
+  if (dir)
+    {
+      /* It exists.  If MODE contains W_OK or X_OK, return
+        EACCESS.  */
+
+      if (mode & (W_OK | X_OK))
+       {
+         errno = EACCES;
+         return -1;
+       }
+
+      /* If vp->name is a directory and DIR isn't, return ENOTDIR.  */
+
+      if (vp->name[vp->name_length] == '/'
+         && !android_is_directory (dir))
+       {
+         errno = ENOTDIR;
+         return -1;
+       }
+
+      return 0;
+    }
+
+  errno = ENOENT;
+  return -1;
+}
+
+static int
+android_afs_mkdir (struct android_vnode *vnode, mode_t mode)
+{
+  struct android_afs_vnode *vp;
+  const char *dir;
+
+  /* If the vnode already exists, return EEXIST in lieu of EROFS.  */
+
+  vp = (struct android_afs_vnode *) vnode;
+  dir = android_scan_directory_tree (vp->name, NULL);
+
+  if (dir)
+    errno = EEXIST;
+  else
+    errno = EROFS;
+
+  return -1;
+}
+
+static int
+android_afs_chmod (struct android_vnode *vnode, mode_t mode,
+                  int flags)
+{
+  errno = EROFS;
+  return -1;
+}
+
+static struct dirent *
+android_afs_readdir (struct android_vdir *vdir)
+{
+  static struct dirent dirent;
+  const char *last;
+  struct android_afs_vdir *dir;
+
+  dir = (struct android_afs_vdir *) vdir;
+
+  /* There are no more files to read.  */
+  if (dir->asset_dir >= dir->asset_limit)
+    return NULL;
+
+  /* Otherwise, scan forward looking for the next NULL byte.  */
+  last = memchr (dir->asset_dir, 0,
+                dir->asset_limit - dir->asset_dir);
+
+  /* No more NULL bytes remain.  */
+  if (!last)
+    return NULL;
+
+  /* Forward last past the NULL byte.  */
+  last++;
+
+  /* Make sure it is still within the directory tree.  */
+  if (last >= directory_tree + directory_tree_size)
+    return NULL;
+
+  /* Now, fill in the dirent with the name.  */
+  memset (&dirent, 0, sizeof dirent);
+  dirent.d_ino = 0;
+  dirent.d_off = 0;
+  dirent.d_reclen = sizeof dirent;
+
+  /* Note that dir->asset_dir is actually a NULL terminated
+     string.  */
+  memcpy (dirent.d_name, dir->asset_dir,
+         MIN (sizeof dirent.d_name,
+              last - dir->asset_dir));
+  dirent.d_name[sizeof dirent.d_name - 1] = '\0';
+
+  /* Strip off the trailing slash, if any.  */
+  if (dirent.d_name[MIN (sizeof dirent.d_name,
+                        last - dir->asset_dir)
+                   - 2] == '/')
+    dirent.d_name[MIN (sizeof dirent.d_name,
+                      last - dir->asset_dir)
+                 - 2] = '\0';
+
+  /* If this is not a directory, return DT_REG.  Otherwise, return
+     DT_DIR.  */
+
+  if (last - 2 >= directory_tree && last[-2] == '/')
+    dirent.d_type = DT_DIR;
+  else
+    dirent.d_type = DT_REG;
+
+  /* Forward dir->asset_dir to the file past last.  */
+  dir->asset_dir = ((char *) directory_tree
+                   + android_extract_long ((char *) last));
+
+  return &dirent;
+}
+
+static void
+android_afs_closedir (struct android_vdir *vdir)
+{
+  struct android_afs_vdir *dir, **next, *tem;
+
+  dir = (struct android_afs_vdir *) vdir;
+
+  /* If the ``directory file descriptor'' has been opened, close
+     it.  */
+
+  if (dir->fd != -1)
+    close (dir->fd);
+
+  xfree (dir->asset_file);
+
+  /* Now unlink this directory.  */
+
+  for (next = &all_afs_vdirs; (tem = *next);)
+    {
+      if (tem == dir)
+       *next = dir->next;
+      else
+       next = &(*next)->next;
+    }
+
+  /* Free the directory itself.  */
+
+  xfree (dir);
+}
+
+static int
+android_afs_dirfd (struct android_vdir *vdir)
+{
+  struct android_afs_vdir *dir;
+
+  dir = (struct android_afs_vdir *) vdir;
+
+  /* Since `android_afs_opendir' tries to avoid opening a file
+     descriptor if readdir isn't called, dirfd can fail if open fails.
+
+     open sets errno to a set of errors different from what POSIX
+     stipulates for dirfd, but for ease of implementation the open
+     errors are used instead.  */
+
+  if (dir->fd >= 0)
+    return dir->fd;
+
+  dir->fd = open ("/dev/null", O_RDONLY | O_CLOEXEC);
+  return dir->fd;
+}
+
+static struct android_vdir *
+android_afs_opendir (struct android_vnode *vnode)
+{
+  char *asset_dir;
+  struct android_afs_vdir *dir;
+  struct android_afs_vnode *vp;
+  size_t limit;
+
+  vp = (struct android_afs_vnode *) vnode;
+
+  /* Scan for the asset directory by vp->name.  */
+
+  asset_dir
+    = (char *) android_scan_directory_tree (vp->name, &limit);
+
+  if (!asset_dir)
+    {
+      errno = ENOENT;
+      return NULL;
+    }
+
+  /* Verify that asset_dir is indeed a directory.  */
+
+  if (!android_is_directory (asset_dir))
+    {
+      errno = ENOTDIR;
+      return NULL;
+    }
+
+  /* Fill in the directory stream.  */
+  dir = xmalloc (sizeof *dir);
+  dir->vdir.readdir = android_afs_readdir;
+  dir->vdir.closedir = android_afs_closedir;
+  dir->vdir.dirfd = android_afs_dirfd;
+  dir->asset_dir = asset_dir;
+  dir->asset_limit = (char *) directory_tree + limit;
+  dir->fd = -1;
+  dir->asset_file = xzalloc (vp->name_length + 2);
+  strcpy (dir->asset_file, vp->name);
+
+  /* Make sure dir->asset_file is terminated with /.  */
+  if (dir->asset_file[vp->name_length - 1] != '/')
+    dir->asset_file[vp->name_length] = '/';
+
+  /* Make sure dir->asset_limit is within bounds.  It is a limit,
+     and as such can be exactly one byte past directory_tree.  */
+  if (dir->asset_limit > directory_tree + directory_tree_size)
+    {
+      xfree (dir);
+      xfree (dir->asset_file);
+      errno = EACCES;
+      return NULL;
+    }
+
+  dir->next = all_afs_vdirs;
+  all_afs_vdirs = dir;
+  return &dir->vdir;
+}
+
+/* Return the file name corresponding to DIRFD if it is a
+   ``directory'' file descriptor returned by `android_afs_dirfd' or
+   NULL otherwise.  These file names are relative to the `/assets'
+   directory, but with a leading separator character.  */
+
+static char *
+android_afs_get_directory_name (int dirfd)
+{
+  struct android_afs_vdir *dir;
+
+  for (dir = all_afs_vdirs; dir; dir = dir->next)
+    {
+      if (dir->fd == dirfd && dirfd != -1)
+       return dir->asset_file;
+    }
+
+  return NULL;
+}
+
+
+
+struct android_content_vdir
+{
+  /* The directory function table.  */
+  struct android_vdir vdir;
+
+  /* The next directory stream in `all_content_vdirs'.  */
+  struct android_content_vdir *next;
+
+  /* Pointer to the next file to return.  */
+  const char **next_name;
+
+  /* Temporary file descriptor used to identify this directory to
+     at-funcs, or -1.  */
+  int fd;
+};
+
+static struct android_vnode *android_authority_initial (char *, size_t);
+static struct android_vnode *android_saf_root_initial (char *, size_t);
+
+/* Content provider meta-interface.  This implements a vnode at
+   /content, which is a directory itself containing two additional
+   directories.
+
+   /content/storage only exists on Android 5.0 and later, and contains
+   a list of each directory tree Emacs has been granted permanent
+   access to through the Storage Access Framework.
+
+   /content/by-authority exists on Android 4.4 and later; it contains
+   no directories, but provides a `name' function that converts
+   children into content URIs.  */
+
+static struct android_vnode *android_content_name (struct android_vnode *,
+                                                  char *, size_t);
+static int android_content_open (struct android_vnode *, int,
+                                mode_t, bool, int *, AAsset **);
+static void android_content_close (struct android_vnode *);
+static int android_content_unlink (struct android_vnode *);
+static int android_content_symlink (const char *, struct android_vnode *);
+static int android_content_rmdir (struct android_vnode *);
+static int android_content_rename (struct android_vnode *,
+                                  struct android_vnode *, bool);
+static int android_content_stat (struct android_vnode *, struct stat *);
+static int android_content_access (struct android_vnode *, int);
+static int android_content_mkdir (struct android_vnode *, mode_t);
+static int android_content_chmod (struct android_vnode *, mode_t, int);
+static struct android_vdir *android_content_opendir (struct android_vnode *);
+
+/* Vector of VFS operations associated with the content VFS node.  */
+
+static struct android_vops content_vfs_ops =
+  {
+    android_content_name,
+    android_content_open,
+    android_content_close,
+    android_content_unlink,
+    android_content_symlink,
+    android_content_rmdir,
+    android_content_rename,
+    android_content_stat,
+    android_content_access,
+    android_content_mkdir,
+    android_content_chmod,
+    android_content_opendir,
+  };
+
+/* Table of directories contained within a top-level vnode.  */
+
+static const char *content_directory_contents[] =
+  {
+    "storage", "by-authority",
+  };
+
+/* Chain consisting of all open content directory streams.  */
+static struct android_content_vdir *all_content_vdirs;
+
+static struct android_vnode *
+android_content_name (struct android_vnode *vnode, char *name,
+                     size_t length)
+{
+  char *remainder;
+  struct android_vnode *vp;
+  char *component_end;
+  struct android_special_vnode *special;
+  size_t i;
+  int api;
+
+  static struct android_special_vnode content_vnodes[] = {
+    { "storage", 7, android_saf_root_initial,        },
+    { "by-authority", 12, android_authority_initial, },
+  };
+
+  /* Canonicalize NAME.  */
+  remainder = android_vfs_canonicalize_name (name, &length);
+
+  /* If remainder is set, it's a name relative to the root vnode.  */
+  if (remainder)
+    goto parent_vnode;
+
+  /* If LENGTH is empty or NAME is a single directory separator,
+     return a copy of this vnode.  */
+
+  if (length < 1 || (*name == '/' && length == 1))
+    {
+      vp = xmalloc (sizeof *vp);
+      memcpy (vp, vnode, sizeof *vp);
+      return vp;
+    }
+
+  api = android_get_current_api_level ();
+
+  /* If NAME starts with a directory separator, move it past that.  */
+
+  if (*name == '/')
+    name++, length -= 1;
+
+  /* Look for the first directory separator.  */
+  component_end = strchr (name, '/');
+
+  /* If not there, use name + length.  */
+
+  if (!component_end)
+    component_end = name + length;
+  else
+    /* Move past the separator character.  */
+    component_end++;
+
+  /* Now, find out if the first component is a special vnode; if so,
+     call its root lookup function with the rest of NAME there.  */
+
+  if (api < 19)
+    i = 2;
+  else if (api < 21)
+    i = 1;
+  else
+    i = 0;
+
+  for (; i < ARRAYELTS (content_vnodes); ++i)
+    {
+      special = &content_vnodes[i];
+
+      if (component_end - name == special->length
+         && !memcmp (special->name, name, special->length))
+       return (*special->initial) (component_end,
+                                   length - special->length);
+
+      /* Detect the case where a special is named with a trailing
+        directory separator.  */
+
+      if (component_end - name == special->length + 1
+         && !memcmp (special->name, name, special->length)
+         && name[special->length] == '/')
+       /* Make sure to include the directory separator.  */
+       return (*special->initial) (component_end - 1,
+                                   length - special->length);
+    }
+
+  errno = ENOENT;
+  return NULL;
+
+ parent_vnode:
+  /* The parent of this vnode is always the root filesystem.  */
+  vp = &root_vnode.vnode;
+  return (*vnode->ops->name) (vnode, remainder, strlen (remainder));
+}
+
+static int
+android_content_open (struct android_vnode *vnode, int flags,
+                     mode_t mode, bool asset_p, int *fd,
+                     AAsset **asset)
+{
+  /* Don't allow opening this special directory.  */
+  errno = ENOSYS;
+  return -1;
+}
+
+static void
+android_content_close (struct android_vnode *vnode)
+{
+  int save_errno;
+
+  save_errno = errno;
+  xfree (vnode);
+  errno = save_errno;
+}
+
+static int
+android_content_unlink (struct android_vnode *vnode)
+{
+  errno = ENOSYS;
+  return -1;
+}
+
+static int
+android_content_symlink (const char *target, struct android_vnode *vnode)
+{
+  errno = ENOSYS;
+  return -1;
+}
+
+static int
+android_content_rmdir (struct android_vnode *vnode)
+{
+  errno = ENOSYS;
+  return -1;
+}
+
+static int
+android_content_rename (struct android_vnode *src,
+                       struct android_vnode *dst,
+                       bool keep_existing)
+{
+  if (src->type != dst->type)
+    {
+      /* If the types of both vnodes differ, complain that they're on
+        two different filesystems (which is correct from a abstract
+        viewpoint.)  */
+      errno = EXDEV;
+      return -1;
+    }
+
+  /* Otherwise, return ENOSYS.  */
+  errno = ENOSYS;
+  return -1;
+}
+
+static int
+android_content_stat (struct android_vnode *vnode,
+                     struct stat *statb)
+{
+  memset (statb, 0, sizeof *statb);
+
+  statb->st_uid = getuid ();
+  statb->st_gid = getgid ();
+  statb->st_ino = 0;
+  statb->st_dev = -2;
+  statb->st_mode = S_IFDIR | S_IRUSR | S_IXUSR;
+  return 0;
+}
+
+static int
+android_content_access (struct android_vnode *vnode, int mode)
+{
+  /* Validate MODE.  */
+
+  if (mode != F_OK && !(mode & (W_OK | X_OK | R_OK)))
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  /* Return EROFS if the caller is trying to check for write access to
+     this vnode.  */
+
+  if (mode != F_OK && (mode & (W_OK | X_OK)))
+    {
+      errno = EROFS;
+      return -1;
+    }
+
+  return 0;
+}
+
+static int
+android_content_mkdir (struct android_vnode *vnode, mode_t mode)
+{
+  errno = EEXIST;
+  return 0;
+}
+
+static int
+android_content_chmod (struct android_vnode *vnode, mode_t mode,
+                      int flags)
+{
+  errno = EACCES;
+  return 0;
+}
+
+static struct dirent *
+android_content_readdir (struct android_vdir *vdir)
+{
+  static struct dirent dirent;
+  struct android_content_vdir *dir;
+  const char *name;
+
+  dir = (struct android_content_vdir *) vdir;
+
+  /* There are no more files to be read.  */
+  if (dir->next_name == (content_directory_contents
+                        + ARRAYELTS (content_directory_contents)))
+    return NULL;
+
+  /* Get the next child.  */
+  name = *dir->next_name++;
+
+  /* Now, fill in the dirent with the name.  */
+  memset (&dirent, 0, sizeof dirent);
+  dirent.d_ino = 0;
+  dirent.d_off = 0;
+  dirent.d_reclen = sizeof dirent;
+  dirent.d_type = DT_DIR;
+  strcpy (dirent.d_name, name);
+  return &dirent;
+}
+
+static void
+android_content_closedir (struct android_vdir *vdir)
+{
+  struct android_content_vdir *dir, **next, *tem;
+
+  dir = (struct android_content_vdir *) vdir;
+
+  /* If the ``directory file descriptor'' has been opened, close
+     it.  */
+
+  if (dir->fd != -1)
+    close (dir->fd);
+
+  /* Now unlink this directory.  */
+
+  for (next = &all_content_vdirs; (tem = *next);)
+    {
+      if (tem == dir)
+       *next = dir->next;
+      else
+       next = &(*next)->next;
+    }
+
+  xfree (dir);
+}
+
+static int
+android_content_dirfd (struct android_vdir *vdir)
+{
+  struct android_content_vdir *dir;
+
+  dir = (struct android_content_vdir *) vdir;
+
+  /* Since `android_content_opendir' tries to avoid opening a file
+     descriptor if readdir isn't called, dirfd can fail if open fails.
+
+     open sets errno to a set of errors different from what POSIX
+     stipulates for dirfd, but for ease of implementation the open
+     errors are used instead.  */
+
+  if (dir->fd >= 0)
+    return dir->fd;
+
+  dir->fd = open ("/dev/null", O_RDONLY | O_CLOEXEC);
+  return dir->fd;
+}
+
+static struct android_vdir *
+android_content_opendir (struct android_vnode *vnode)
+{
+  struct android_content_vdir *dir;
+  int api;
+
+  /* Allocate the virtual directory.  */
+  dir = xmalloc (sizeof *dir);
+  dir->vdir.readdir = android_content_readdir;
+  dir->vdir.closedir = android_content_closedir;
+  dir->vdir.dirfd = android_content_dirfd;
+  dir->fd = -1;
+
+  /* Fill in the directory contents.  */
+  dir->next_name = content_directory_contents;
+  api = android_get_current_api_level ();
+
+  /* Android 4.4 and earlier don't support /content/storage.  */
+
+  if (api < 21)
+    dir->next_name++;
+
+  /* Android 4.3 and earlier don't support /content/by-authority.  */
+
+  if (api < 19)
+    dir->next_name++;
+
+  /* Link this stream onto the list of all content directory
+     streams.  */
+  dir->next = all_content_vdirs;
+  all_content_vdirs = dir;
+  return &dir->vdir;
+}
+
+/* Return the file name corresponding to DIRFD if it is a
+   ``directory'' file descriptor returned by `android_content_dirfd'
+   or NULL otherwise.  */
+
+static char *
+android_content_get_directory_name (int dirfd)
+{
+  struct android_content_vdir *dir;
+
+  for (dir = all_content_vdirs; dir; dir = dir->next)
+    {
+      if (dir->fd == dirfd && dirfd != -1)
+       return (char *) "/content";
+    }
+
+  return NULL;
+}
+
+/* Find the vnode designated by the normalized NAME relative to the
+   root of the content file system.  NAME may be modified, and must be
+   LENGTH bytes long, excluding its terminating NULL byte.  */
+
+static struct android_vnode *
+android_content_initial (char *name, size_t length)
+{
+  struct android_vnode temp;
+
+  /* Create a temporary vnode at the root of the asset file
+     system.  */
+
+  temp.ops = &content_vfs_ops;
+  temp.type = ANDROID_VNODE_CONTENT;
+  temp.flags = 0;
+
+  /* Try to name this vnode.  If NAME is empty, it will be duplicated
+     instead.  */
+  return android_content_name (&temp, name, length);
+}
+
+
+
+/* Content URI management functions.  */
+
+/* Return the content URI corresponding to a `/content/by-authority'
+   file name, or NULL if it is invalid for some reason.  FILENAME
+   should be relative to /content/by-authority, with no leading
+   directory separator character.
+
+   This function is not reentrant.  */
+
+static const char *
+android_get_content_name (const char *filename)
+{
+  static char buffer[PATH_MAX + 1], *fill;
+
+  /* Make sure FILENAME isn't obviously invalid: it must contain an
+     authority name and a file name component.  */
+
+  fill = strchr (filename, '/');
+  if (!fill || *(fill + 1) == '\0')
+    {
+      errno = ENOENT;
+      return NULL;
+    }
+
+  /* FILENAME must also not be a directory.  */
+
+  if (filename[strlen (filename)] == '/')
+    {
+      errno = ENOTDIR;
+      return NULL;
+    }
+
+  snprintf (buffer, PATH_MAX + 1, "content://%s", filename);
+  return buffer;
+}
+
+/* Return whether or not the specified URI is an accessible content
+   URI.  MODE specifies what to check.  */
+
+static bool
+android_check_content_access (const char *uri, int mode)
+{
+  jobject string;
+  size_t length;
+  jboolean rc;
+
+  length = strlen (uri);
+
+  string = (*android_java_env)->NewByteArray (android_java_env,
+                                             length);
+  android_exception_check ();
+
+  (*android_java_env)->SetByteArrayRegion (android_java_env,
+                                          string, 0, length,
+                                          (jbyte *) uri);
+  rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+                                              emacs_service,
+                                              service_class.check_content_uri,
+                                              string,
+                                              (jboolean) ((mode & R_OK)
+                                                          != 0),
+                                              (jboolean) ((mode & W_OK)
+                                                          != 0));
+  android_exception_check_1 (string);
+  ANDROID_DELETE_LOCAL_REF (string);
+
+  return rc;
+}
+
+
+
+/* Content authority-based vnode implementation.
+
+   /contents/by-authority is a simple vnode implementation that converts
+   components to content:// URIs.
+
+   It does not canonicalize file names by removing parent directory
+   separators, as these characters can appear in legitimate content
+   file names.  */
+
+struct android_authority_vnode
+{
+  /* The vnode data itself.  */
+  struct android_vnode vnode;
+
+  /* URI associated with this vnode, or NULL if this is the root of
+     the content authority tree.  */
+  char *uri;
+};
+
+static struct android_vnode *android_authority_name (struct android_vnode *,
+                                                    char *, size_t);
+static int android_authority_open (struct android_vnode *, int,
+                                  mode_t, bool, int *, AAsset **);
+static void android_authority_close (struct android_vnode *);
+static int android_authority_unlink (struct android_vnode *);
+static int android_authority_symlink (const char *, struct android_vnode *);
+static int android_authority_rmdir (struct android_vnode *);
+static int android_authority_rename (struct android_vnode *,
+                                    struct android_vnode *, bool);
+static int android_authority_stat (struct android_vnode *, struct stat *);
+static int android_authority_access (struct android_vnode *, int);
+static int android_authority_mkdir (struct android_vnode *, mode_t);
+static int android_authority_chmod (struct android_vnode *, mode_t, int);
+static struct android_vdir *android_authority_opendir (struct android_vnode *);
+
+/* Vector of VFS operations associated with the content VFS node.  */
+
+static struct android_vops authority_vfs_ops =
+  {
+    android_authority_name,
+    android_authority_open,
+    android_authority_close,
+    android_authority_unlink,
+    android_authority_symlink,
+    android_authority_rmdir,
+    android_authority_rename,
+    android_authority_stat,
+    android_authority_access,
+    android_authority_mkdir,
+    android_authority_chmod,
+    android_authority_opendir,
+  };
+
+static struct android_vnode *
+android_authority_name (struct android_vnode *vnode, char *name,
+                       size_t length)
+{
+  struct android_authority_vnode *vp;
+  const char *uri_name;
+
+  if (!android_init_gui)
+    {
+      errno = EIO;
+      return NULL;
+    }
+
+  /* If NAME is empty or consists of a single directory separator
+     _and_ VP->uri is NULL, return a copy of VNODE.  */
+
+  vp = (struct android_authority_vnode *) vnode;
+
+  if (length < 1 || (*name == '/' && length == 1 && !vp->uri))
+    {
+      vp = xmalloc (sizeof *vp);
+      memcpy (vp, vnode, sizeof *vp);
+
+      if (vp->uri)
+       vp->uri = xstrdup (vp->uri);
+
+      return &vp->vnode;
+    }
+
+  /* Else, if VP->uri is NULL, then it is the root of the by-authority
+     tree.  If NAME starts with a directory separator character,
+     remove it.  */
+
+  if (!vp->uri)
+    {
+      if (*name == '/')
+       name++, length -= 1;
+
+      uri_name = android_get_content_name (name);
+      if (!uri_name)
+       goto error;
+
+      /* Now fill in the vnode.  */
+      vp = xmalloc (sizeof *vp);
+      vp->vnode.ops = &authority_vfs_ops;
+      vp->vnode.type = ANDROID_VNODE_CONTENT_AUTHORITY;
+      vp->vnode.flags = 0;
+      vp->uri = xstrdup (uri_name);
+      return &vp->vnode;
+    }
+
+  /* Content files can't have children.  */
+  errno = ENOENT;
+ error:
+  return NULL;
+}
+
+static int
+android_authority_open (struct android_vnode *vnode, int flags,
+                       mode_t mode, bool asset_p, int *fd_return,
+                       AAsset **asset)
+{
+  struct android_authority_vnode *vp;
+  size_t length;
+  jobject string;
+  int fd;
+
+  vp = (struct android_authority_vnode *) vnode;
+
+  if (vp->uri == NULL)
+    {
+      /* This is the `by-authority' directory itself, which can't be
+        opened.  */
+      errno = ENOSYS;
+      return -1;
+    }
+
+  /* Allocate a buffer to hold the file name.  */
+  length = strlen (vp->uri);
+  string = (*android_java_env)->NewByteArray (android_java_env,
+                                             length);
+  if (!string)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      errno = ENOMEM;
+      return -1;
+    }
+
+  /* Copy the URI into this byte array.  */
+  (*android_java_env)->SetByteArrayRegion (android_java_env,
+                                          string, 0, length,
+                                          (jbyte *) vp->uri);
+
+  /* Try to open the file descriptor.  */
+
+  fd
+    = (*android_java_env)->CallIntMethod (android_java_env,
+                                         emacs_service,
+                                         service_class.open_content_uri,
+                                         string,
+                                         (jboolean) ((mode & O_WRONLY
+                                                      || mode & O_RDWR)
+                                                     != 0),
+                                         (jboolean) !(mode & O_WRONLY),
+                                         (jboolean) ((mode & O_TRUNC)
+                                                     != 0));
+
+  if ((*android_java_env)->ExceptionCheck (android_java_env))
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      errno = ENOMEM;
+      ANDROID_DELETE_LOCAL_REF (string);
+      return -1;
+    }
+
+  /* If fd is -1, just assume that the file does not exist,
+     and return -1 with errno set to ENOENT.  */
+
+  if (fd == -1)
+    {
+      errno = ENOENT;
+      goto skip;
+    }
+
+  if (mode & O_CLOEXEC)
+    android_close_on_exec (fd);
+
+ skip:
+  ANDROID_DELETE_LOCAL_REF (string);
+
+  if (fd == -1)
+    return -1;
+
+  *fd_return = fd;
+  return 0;
+}
+
+static void
+android_authority_close (struct android_vnode *vnode)
+{
+  struct android_authority_vnode *vp;
+  int save_errno;
+
+  vp = (struct android_authority_vnode *) vnode;
+  save_errno = errno;
+  xfree (vp->uri);
+  xfree (vp);
+  errno = save_errno;
+}
+
+static int
+android_authority_unlink (struct android_vnode *vnode)
+{
+  errno = EROFS;
+  return -1;
+}
+
+static int
+android_authority_symlink (const char *target,
+                          struct android_vnode *vnode)
+{
+  errno = EROFS;
+  return -1;
+}
+
+static int
+android_authority_rmdir (struct android_vnode *vnode)
+{
+  errno = EROFS;
+  return -1;
+}
+
+static int
+android_authority_rename (struct android_vnode *src,
+                         struct android_vnode *dst,
+                         bool keep_existing)
+{
+  if (src->type != dst->type)
+    {
+      /* If the types of both vnodes differ, complain that they're on
+        two different filesystems (which is correct from a abstract
+        viewpoint.)  */
+      errno = EXDEV;
+      return -1;
+    }
+
+  /* Otherwise, return ENOSYS.  */
+  errno = ENOSYS;
+  return -1;
+}
+
+static int
+android_authority_stat (struct android_vnode *vnode,
+                       struct stat *statb)
+{
+  int rc, fd, save_errno;
+  struct android_authority_vnode *vp;
+
+  /* If this is a vnode representing `by-authority', return some
+     information about this directory.  */
+
+  vp = (struct android_authority_vnode *) vnode;
+
+  if (!vp->uri)
+    {
+      memset (statb, 0, sizeof *statb);
+      statb->st_uid = getuid ();
+      statb->st_gid = getgid ();
+      statb->st_ino = 0;
+      statb->st_dev = -3;
+      statb->st_mode = S_IFDIR | S_IRUSR;
+      return 0;
+    }
+
+  /* Try to open the file and call fstat.  */
+  rc = (*vnode->ops->open) (vnode, O_RDONLY, 0, false, &fd, NULL);
+
+  if (rc < 0)
+    return -1;
+
+  /* If rc is 1, then an asset file descriptor has been returned.
+     This is impossible, so assert that it doesn't transpire.  */
+  assert (rc != 1);
+
+  /* Now, try to stat the file.  */
+  rc = fstat (fd, statb);
+  save_errno = errno;
+
+  /* Close the file descriptor.  */
+  close (fd);
+
+  /* Restore errno.  */
+  errno = save_errno;
+  return rc;
+}
+
+static int
+android_authority_access (struct android_vnode *vnode, int mode)
+{
+  struct android_authority_vnode *vp;
+
+  vp = (struct android_authority_vnode *) vnode;
+
+  /* Validate MODE.  */
+
+  if (mode != F_OK && !(mode & (W_OK | X_OK | R_OK)))
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  if (!vp->uri)
+    {
+      /* Return EACCES if the caller is trying to check for write
+        access to `by-authority'.  */
+
+      if (mode != F_OK && (mode & (W_OK | X_OK)))
+       {
+         errno = EACCES;
+         return -1;
+       }
+
+      return 0;
+    }
+
+  return (android_check_content_access (vp->uri, mode)
+         ? 0 : -1);
+}
+
+static int
+android_authority_mkdir (struct android_vnode *vnode, mode_t mode)
+{
+  errno = EACCES;
+  return -1;
+}
+
+static int
+android_authority_chmod (struct android_vnode *vnode, mode_t mode,
+                        int flags)
+{
+  errno = EACCES;
+  return -1;
+}
+
+static struct android_vdir *
+android_authority_opendir (struct android_vnode *vnode)
+{
+  struct android_authority_vnode *vp;
+
+  /* Forbid listing the `by-authority' directory.  */
+  vp = (struct android_authority_vnode *) vnode;
+  errno = vp->uri ? ENOTDIR : EACCES;
+  return NULL;
+}
+
+/* Find the vnode designated by NAME relative to the root of the
+   by-authority directory.
+
+   If NAME is empty or a single leading separator character, return
+   a vnode representing the by-authority directory itself.
+
+   Otherwise, represent the remainder of NAME as a URI (without
+   normalizing it) and return a vnode corresponding to that.
+
+   Value may also be NULL with errno set if the designated vnode is
+   not available, such as when Android windowing has not been
+   initialized.  */
+
+static struct android_vnode *
+android_authority_initial (char *name, size_t length)
+{
+  struct android_authority_vnode temp;
+
+  temp.vnode.ops = &authority_vfs_ops;
+  temp.vnode.type = ANDROID_VNODE_CONTENT_AUTHORITY;
+  temp.vnode.flags = 0;
+  temp.uri = NULL;
+
+  return android_authority_name (&temp.vnode, name, length);
+}
+
+
+
+/* SAF ``root'' vnode implementation.
+
+   The Storage Access Framework is a system service that manages a
+   registry of document providers, which are a type of file system
+   server.
+
+   Normally, document providers can only provide individual files
+   through preestablished ``content URIs''.  Android 5.0 and later add
+   to that ``tree URIs'', which designate entire file system trees.
+
+   Authorization to access document trees and content URIs is granted
+   transiently by default when an Intent is received, but Emacs can
+   also receive persistent authorization for a given document tree.
+
+   /content/storage returns a list of directories, each representing a
+   single authority providing at least one tree URI Emacs holds
+   persistent authorization for.
+
+   Each one of those directories then contains one document tree that
+   Emacs is authorized to access.  */
+
+struct android_saf_root_vnode
+{
+  /* The vnode data.  */
+  struct android_vnode vnode;
+
+  /* The name of the document authority this directory represents, or
+     NULL.  */
+  char *authority;
+};
+
+struct android_saf_root_vdir
+{
+  /* The directory stream function table.  */
+  struct android_vdir vdir;
+
+  /* The next directory stream in `all_saf_root_vdirs'.  */
+  struct android_saf_root_vdir *next;
+
+  /* Array of strings, one for each directory to return.  */
+  jobjectArray array;
+
+  /* Name of the authority this directory lists, or NULL.  */
+  char *authority;
+
+  /* Length of that array, and the current within it.  */
+  jsize length, i;
+
+  /* ``Directory'' file descriptor associated with this stream, or
+     -1.  */
+  int fd;
+};
+
+static struct android_vnode *android_saf_root_name (struct android_vnode *,
+                                                   char *, size_t);
+static int android_saf_root_open (struct android_vnode *, int,
+                                 mode_t, bool, int *, AAsset **);
+static void android_saf_root_close (struct android_vnode *);
+static int android_saf_root_unlink (struct android_vnode *);
+static int android_saf_root_symlink (const char *, struct android_vnode *);
+static int android_saf_root_rmdir (struct android_vnode *);
+static int android_saf_root_rename (struct android_vnode *,
+                                   struct android_vnode *, bool);
+static int android_saf_root_stat (struct android_vnode *, struct stat *);
+static int android_saf_root_access (struct android_vnode *, int);
+static int android_saf_root_mkdir (struct android_vnode *, mode_t);
+static int android_saf_root_chmod (struct android_vnode *, mode_t, int);
+static struct android_vdir *android_saf_root_opendir (struct android_vnode *);
+
+/* Vector of VFS operations associated with the SAF root VFS node.  */
+
+static struct android_vops saf_root_vfs_ops =
+  {
+    android_saf_root_name,
+    android_saf_root_open,
+    android_saf_root_close,
+    android_saf_root_unlink,
+    android_saf_root_symlink,
+    android_saf_root_rmdir,
+    android_saf_root_rename,
+    android_saf_root_stat,
+    android_saf_root_access,
+    android_saf_root_mkdir,
+    android_saf_root_chmod,
+    android_saf_root_opendir,
+  };
+
+/* Chain containing all SAF root directories.  */
+static struct android_saf_root_vdir *all_saf_root_vdirs;
+
+/* Defined in the next page.  */
+static struct android_vnode *android_saf_tree_from_name (char *, const char *,
+                                                        const char *);
+
+/* Ascertain and return whether or not AUTHORITY designates a content
+   provider offering at least one directory tree accessible to
+   Emacs.  */
+
+static bool
+android_saf_valid_authority_p (const char *authority)
+{
+  jobject string;
+  jboolean valid;
+  jmethodID method;
+
+  /* Make certain AUTHORITY can actually be represented as a Java
+     string.  */
+
+  if (android_verify_jni_string (authority))
+    return false;
+
+  /* Build a string containing AUTHORITY.  */
+
+  string = (*android_java_env)->NewStringUTF (android_java_env,
+                                             authority);
+  android_exception_check ();
+
+  method = service_class.valid_authority;
+  valid
+    = (*android_java_env)->CallNonvirtualBooleanMethod (android_java_env,
+                                                       emacs_service,
+                                                       service_class.class,
+                                                       method, string);
+  android_exception_check_1 (string);
+  ANDROID_DELETE_LOCAL_REF (string);
+  return valid;
+}
+
+static struct android_vnode *
+android_saf_root_name (struct android_vnode *vnode, char *name,
+                      size_t length)
+{
+  char *remainder, *component_end;
+  struct android_saf_root_vnode *vp;
+  struct android_vnode *new;
+  char component[PATH_MAX];
+
+  /* Canonicalize NAME.  */
+  remainder = android_vfs_canonicalize_name (name, &length);
+
+  /* If remainder is set, it's a name relative to the root vnode.  */
+  if (remainder)
+    goto parent_vnode;
+
+  /* If LENGTH is empty or NAME is a single directory separator,
+     return a copy of this vnode.  */
+
+  if (length < 1 || (*name == '/' && length == 1))
+    {
+      vp = xmalloc (sizeof *vp);
+      memcpy (vp, vnode, sizeof *vp);
+
+      if (vp->authority)
+       vp->authority = xstrdup (vp->authority);
+
+      return &vp->vnode;
+    }
+
+  vp = (struct android_saf_root_vnode *) vnode;
+
+  /* If NAME starts with a directory separator, move it past that.  */
+
+  if (*name == '/')
+    name++, length -= 1;
+
+  /* Look for the first directory separator.  */
+  component_end = strchr (name, '/');
+
+  /* If not there, use name + length.  */
+
+  if (!component_end)
+    component_end = name + length;
+
+  if (component_end - name >= PATH_MAX)
+    {
+      errno = ENAMETOOLONG;
+      return NULL;
+    }
+
+  /* Copy the component over.  */
+  memcpy (component, name, component_end - name);
+  component[component_end - name] = '\0';
+
+  /* Create a SAF document vnode for this tree if it represents an
+     authority.  */
+
+  if (vp->authority)
+    return android_saf_tree_from_name (component_end, component,
+                                      vp->authority);
+
+  /* Create the vnode.  */
+  vp = xmalloc (sizeof *vp);
+  vp->vnode.ops = &saf_root_vfs_ops;
+  vp->vnode.type = ANDROID_VNODE_SAF_ROOT;
+  vp->vnode.flags = 0;
+  vp->authority = xstrdup (component);
+
+  /* If there is more of this component to be named, name it through
+     the new vnode.  */
+
+  if (component_end != name + length)
+    {
+      new = (*vp->vnode.ops->name) (&vp->vnode, component_end,
+                                   length - (component_end - name));
+      (*vp->vnode.ops->close) (&vp->vnode);
+
+      return new;
+    }
+
+  return &vp->vnode;
+
+ parent_vnode:
+  vp = (struct android_saf_root_vnode *) vnode;
+
+  /* .. was encountered and the parent couldn't be found through
+     stripping off preceding components.
+
+     Find the parent vnode and name the rest of NAME starting from
+     there.  */
+
+  if (!vp->authority)
+    /* Look this file name up relative to the root of the contents
+       directory.  */
+    return android_content_initial (remainder, strlen (remainder));
+  else
+    /* Look this file name up relative to the root of the storage
+       directory.  */
+    return android_saf_root_initial (remainder, strlen (remainder));
+}
+
+static int
+android_saf_root_open (struct android_vnode *vnode, int flags,
+                      mode_t mode, bool asset_p, int *fd_return,
+                      AAsset **asset)
+{
+  /* /content/storage or one of its authority children cannot be
+     opened, as they are virtual directories.  */
+
+  errno = ENOSYS;
+  return -1;
+}
+
+static void
+android_saf_root_close (struct android_vnode *vnode)
+{
+  struct android_saf_root_vnode *vp;
+  int save_errno;
+
+  vp = (struct android_saf_root_vnode *) vnode;
+  save_errno = errno;
+  xfree (vp->authority);
+  xfree (vp);
+  errno = save_errno;
+}
+
+static int
+android_saf_root_unlink (struct android_vnode *vnode)
+{
+  errno = EROFS;
+  return -1;
+}
+
+static int
+android_saf_root_symlink (const char *target,
+                         struct android_vnode *vnode)
+{
+  errno = EROFS;
+  return -1;
+}
+
+static int
+android_saf_root_rmdir (struct android_vnode *vnode)
+{
+  errno = EROFS;
+  return -1;
+}
+
+static int
+android_saf_root_rename (struct android_vnode *src,
+                        struct android_vnode *dst,
+                        bool keep_existing)
+{
+  errno = EROFS;
+  return -1;
+}
+
+static int
+android_saf_root_stat (struct android_vnode *vnode,
+                      struct stat *statb)
+{
+  struct android_saf_root_vnode *vp;
+
+  /* Verify that the authority actually exists and return ENOENT
+     otherwise, lest `locate-dominating-file' & co call an operation
+     that doesn't require listing URIs under this authority, such as
+     access.  */
+
+  vp = (struct android_saf_root_vnode *) vnode;
+
+  if (vp->authority
+      && !android_saf_valid_authority_p (vp->authority))
+    {
+      errno = ENOENT;
+      return -1;
+    }
+
+  /* Make up some imaginary statistics for this vnode.  */
+
+  memset (statb, 0, sizeof *statb);
+  statb->st_uid = getuid ();
+  statb->st_gid = getgid ();
+  statb->st_ino = 0;
+  statb->st_dev = -4;
+  statb->st_mode = S_IFDIR | S_IRUSR | S_IXUSR;
+  return 0;
+}
+
+static int
+android_saf_root_access (struct android_vnode *vnode, int mode)
+{
+  struct android_saf_root_vnode *vp;
+
+  /* Validate MODE.  */
+
+  if (mode != F_OK && !(mode & (W_OK | X_OK | R_OK)))
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  /* Now, don't allow writing or executing this directory.  */
+
+  if (mode != F_OK && (mode & (W_OK | X_OK)))
+    {
+      errno = EROFS;
+      return -1;
+    }
+
+  /* Verify that the authority actually exists and return ENOENT
+     otherwise, lest `locate-dominating-file' & co call an operation
+     that doesn't require listing URIs under this authority, such as
+     access.  */
+
+  vp = (struct android_saf_root_vnode *) vnode;
+
+  if (vp->authority
+      && !android_saf_valid_authority_p (vp->authority))
+    {
+      errno = ENOENT;
+      return -1;
+    }
+
+  return 0;
+}
+
+static int
+android_saf_root_mkdir (struct android_vnode *vnode, mode_t mode)
+{
+  errno = EROFS;
+  return -1;
+}
+
+static int
+android_saf_root_chmod (struct android_vnode *vnode, mode_t mode,
+                       int flags)
+{
+  errno = EACCES;
+  return -1;
+}
+
+static struct dirent *
+android_saf_root_readdir (struct android_vdir *vdir)
+{
+  static struct dirent *dirent;
+  jobject string;
+  const char *chars;
+  size_t length, size;
+  struct android_saf_root_vdir *dir;
+
+  dir = (struct android_saf_root_vdir *) vdir;
+
+  if (dir->i == dir->length)
+    {
+      /* At the end of the stream.  Free dirent and return NULL.  */
+
+      xfree (dirent);
+      dirent = NULL;
+      return NULL;
+    }
+
+  /* Get this string.  */
+  string = (*android_java_env)->GetObjectArrayElement (android_java_env,
+                                                      dir->array, dir->i++);
+  android_exception_check ();
+  chars = (*android_java_env)->GetStringUTFChars (android_java_env,
+                                                 (jstring) string,
+                                                 NULL);
+  android_exception_check_nonnull ((void *) chars, string);
+
+  /* Figure out how large it is, and then resize dirent to fit.  */
+  length = strlen (chars) + 1;
+  size   = offsetof (struct dirent, d_name) + length;
+  dirent = xrealloc (dirent, size);
+
+  /* Clear dirent.  */
+  memset (dirent, 0, size);
+
+  /* Fill in the generic directory information and copy the string
+     over.  */
+  dirent->d_ino = 0;
+  dirent->d_off = 0;
+  dirent->d_reclen = size;
+  dirent->d_type = DT_DIR;
+  strcpy (dirent->d_name, chars);
+
+  /* Release the string data and the local reference to STRING.  */
+  (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+                                             (jstring) string, chars);
+  ANDROID_DELETE_LOCAL_REF (string);
+  return dirent;
+}
+
+static void
+android_saf_root_closedir (struct android_vdir *vdir)
+{
+  struct android_saf_root_vdir *dir, **next, *tem;
+
+  dir = (struct android_saf_root_vdir *) vdir;
+
+  /* If the ``directory file descriptor'' has been opened, close
+     it.  */
+
+  if (dir->fd != -1)
+    close (dir->fd);
+
+  /* Delete the local reference to the file name array.  */
+  ANDROID_DELETE_LOCAL_REF (dir->array);
+
+  /* Free the authority name if set.  */
+  xfree (dir->authority);
+
+  /* Now unlink this directory.  */
+
+  for (next = &all_saf_root_vdirs; (tem = *next);)
+    {
+      if (tem == dir)
+       *next = dir->next;
+      else
+       next = &(*next)->next;
+    }
+
+  /* Free the directory itself.  */
+  xfree (dir);
+}
+
+static int
+android_saf_root_dirfd (struct android_vdir *vdir)
+{
+  struct android_saf_root_vdir *dir;
+
+  dir = (struct android_saf_root_vdir *) vdir;
+
+  /* Since `android_saf_root_opendir' tries to avoid opening a file
+     descriptor if readdir isn't called, dirfd can fail if open fails.
+
+     open sets errno to a set of errors different from what POSIX
+     stipulates for dirfd, but for ease of implementation the open
+     errors are used instead.  */
+
+  if (dir->fd >= 0)
+    return dir->fd;
+
+  dir->fd = open ("/dev/null", O_RDONLY | O_CLOEXEC);
+  return dir->fd;
+}
+
+static struct android_vdir *
+android_saf_root_opendir (struct android_vnode *vnode)
+{
+  struct android_saf_root_vnode *vp;
+  jobjectArray array;
+  jmethodID method;
+  jbyteArray authority;
+  struct android_saf_root_vdir *dir;
+  size_t length;
+
+  vp = (struct android_saf_root_vnode *) vnode;
+
+  if (vp->authority)
+    {
+      /* Build a string containing the authority.  */
+      length = strlen (vp->authority);
+      authority = (*android_java_env)->NewByteArray (android_java_env,
+                                                    length);
+      android_exception_check ();
+
+      /* Copy the authority name to that byte array.  */
+      (*android_java_env)->SetByteArrayRegion (android_java_env,
+                                              authority, 0, length,
+                                              (jbyte *) vp->authority);
+
+      /* Acquire a list of every tree provided by this authority.  */
+
+      method = service_class.get_document_trees;
+      array
+       = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env,
+                                                          emacs_service,
+                                                          service_class.class,
+                                                          method, authority);
+      android_exception_check_1 (authority);
+      ANDROID_DELETE_LOCAL_REF (authority);
+
+      /* Ascertain the length of the array.  If it is empty or NULL,
+         return ENOENT.  */
+
+      if (!array)
+       {
+         errno = ENOENT;
+         return NULL;
+       }
+
+      length = (*android_java_env)->GetArrayLength (android_java_env, array);
+
+      if (!length)
+       {
+         ANDROID_DELETE_LOCAL_REF (array);
+         errno = ENOENT;
+         return NULL;
+       }
+
+      /* Now allocate the directory stream.  It will retain a local
+        reference to the array, and should thus only be used within the
+        same JNI local reference frame.  */
+
+      dir = xmalloc (sizeof *dir);
+      dir->vdir.readdir = android_saf_root_readdir;
+      dir->vdir.closedir = android_saf_root_closedir;
+      dir->vdir.dirfd = android_saf_root_dirfd;
+      dir->fd = -1;
+      dir->array = array;
+      dir->length = length;
+      dir->i = 0;
+      dir->authority = xstrdup (vp->authority);
+
+      /* Link this stream onto the list of all SAF root directory
+        streams.  */
+      dir->next = all_saf_root_vdirs;
+      all_saf_root_vdirs = dir;
+      return &dir->vdir;
+    }
+
+  /* Acquire a list of every document authority.  */
+
+  method = service_class.get_document_authorities;
+  array = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env,
+                                                          emacs_service,
+                                                          service_class.class,
+                                                          method);
+  android_exception_check ();
+
+  if (!array)
+    emacs_abort ();
+
+  /* Now allocate the directory stream.  It will retain a local
+     reference to the array, and should thus only be used within the
+     same JNI local reference frame.  */
+
+  dir = xmalloc (sizeof *dir);
+  dir->vdir.readdir = android_saf_root_readdir;
+  dir->vdir.closedir = android_saf_root_closedir;
+  dir->vdir.dirfd = android_saf_root_dirfd;
+  dir->fd = -1;
+  dir->array = array;
+  dir->length = (*android_java_env)->GetArrayLength (android_java_env,
+                                                    array);
+  dir->i = 0;
+  dir->authority = NULL;
+
+  /* Link this stream onto the list of all SAF root directory
+     streams.  */
+  dir->next = all_saf_root_vdirs;
+  all_saf_root_vdirs = dir;
+  return &dir->vdir;
+}
+
+/* Find the vnode designated by NAME relative to the root of the
+   storage directory.
+
+   If NAME is empty or a single leading separator character, return a
+   vnode representing the storage directory itself.
+
+   If NAME actually resides in a parent directory, look for it within
+   the vnode representing the content directory.  */
+
+static struct android_vnode *
+android_saf_root_initial (char *name, size_t length)
+{
+  struct android_saf_root_vnode temp;
+
+  temp.vnode.ops = &saf_root_vfs_ops;
+  temp.vnode.type = ANDROID_VNODE_SAF_ROOT;
+  temp.vnode.flags = 0;
+  temp.authority = NULL;
+
+  return android_saf_root_name (&temp.vnode, name, length);
+}
+
+/* Return any open SAF root directory stream for which dirfd has
+   returned the file descriptor DIRFD.  Return NULL otherwise.  */
+
+static struct android_saf_root_vdir *
+android_saf_root_get_directory (int dirfd)
+{
+  struct android_saf_root_vdir *dir;
+
+  for (dir = all_saf_root_vdirs; dir; dir = dir->next)
+    {
+      if (dir->fd == dirfd && dirfd != -1)
+       return dir;
+    }
+
+  return NULL;
+}
+
+
+
+/* Functions common to both SAF directory and file nodes.  */
+
+/* Whether or not Emacs is within an operation running from the SAF
+   thread.  */
+static bool inside_saf_critical_section;
+
+/* Check for JNI exceptions, clear them, and set errno accordingly.
+   Also, free each of the N local references given as arguments if an
+   exception takes place.
+
+   Value is 1 if an exception has taken place, 0 otherwise.
+
+   If the exception thrown derives from FileNotFoundException, set
+   errno to ENOENT.
+
+   If the exception thrown derives from SecurityException, set errno
+   to EACCES.
+
+   If the exception thrown derives from OperationCanceledException,
+   set errno to EINTR.
+
+   If the exception thrown derives from UnsupportedOperationException,
+   set errno to ENOSYS.
+
+   If the exception thrown derives from OutOfMemoryException, call
+   `memory_full'.
+
+   If the exception thrown is anything else, set errno to EIO.  */
+
+static int
+android_saf_exception_check (int n, ...)
+{
+  jthrowable exception;
+  JNIEnv *env;
+  va_list ap;
+  int new_errno;
+
+  env = android_java_env;
+  va_start (ap, n);
+
+  /* First, check for an exception.  */
+
+  if (!(*env)->ExceptionCheck (env))
+    /* No exception has taken place.  Return 0.  */
+    return 0;
+
+  /* Print the exception.  */
+  (*env)->ExceptionDescribe (env);
+
+  exception = (*env)->ExceptionOccurred (env);
+
+  if (!exception)
+    /* JNI couldn't return a local reference to the exception.  */
+    memory_full (0);
+
+  /* Clear the exception, making it safe to subsequently call other
+     JNI functions.  */
+  (*env)->ExceptionClear (env);
+
+  /* Delete each of the N arguments.  */
+
+  while (n > 0)
+    {
+      ANDROID_DELETE_LOCAL_REF (va_arg (ap, jobject));
+      n--;
+    }
+
+  /* Now set errno or signal memory_full as required.  */
+
+  if ((*env)->IsInstanceOf (env, (jobject) exception,
+                           file_not_found_exception))
+    new_errno = ENOENT;
+  else if ((*env)->IsInstanceOf (env, (jobject) exception,
+                                security_exception))
+    new_errno = EACCES;
+  else if ((*env)->IsInstanceOf (env, (jobject) exception,
+                                operation_canceled_exception))
+    new_errno = EINTR;
+  else if ((*env)->IsInstanceOf (env, (jobject) exception,
+                                unsupported_operation_exception))
+    new_errno = ENOSYS;
+  else if ((*env)->IsInstanceOf (env, (jobject) exception,
+                                out_of_memory_error))
+    {
+      ANDROID_DELETE_LOCAL_REF ((jobject) exception);
+      memory_full (0);
+    }
+  else
+    new_errno = EIO;
+
+  /* expression is still a local reference! */
+  ANDROID_DELETE_LOCAL_REF ((jobject) exception);
+  errno = new_errno;
+  return 1;
+}
+
+/* Return file status for the document designated by ID_NAME within
+   the document tree identified by URI_NAME.
+
+   If the file status is available, place it within *STATB and return
+   0.  If not, return -1 and set errno to EPERM.  */
+
+static int
+android_saf_stat (const char *uri_name, const char *id_name,
+                 struct stat *statb)
+{
+  jmethodID method;
+  jstring uri, id;
+  jobject status;
+  jlong mode, size, mtim, *longs;
+
+  /* Now guarantee that it is safe to call functions which
+     synchronize with the SAF thread.  */
+
+  if (inside_saf_critical_section)
+    {
+      errno = EIO;
+      return -1;
+    }
+
+  /* Build strings for both URI and ID.  */
+  uri = (*android_java_env)->NewStringUTF (android_java_env, uri_name);
+  android_exception_check ();
+
+  if (id_name)
+    {
+      id = (*android_java_env)->NewStringUTF (android_java_env,
+                                             id_name);
+      android_exception_check_1 (uri);
+    }
+  else
+    id = NULL;
+
+  /* Try to retrieve the file status.  */
+  method = service_class.stat_document;
+  inside_saf_critical_section = true;
+  status = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env,
+                                                           emacs_service,
+                                                           service_class.class,
+                                                           method, uri, id);
+  inside_saf_critical_section = false;
+
+  /* Check for exceptions and release unneeded local references.  */
+
+  if (id)
+    {
+      if (android_saf_exception_check (2, uri, id))
+       return -1;
+
+      ANDROID_DELETE_LOCAL_REF (id);
+    }
+  else if (android_saf_exception_check (1, uri))
+    return -1;
+
+  ANDROID_DELETE_LOCAL_REF (uri);
+
+  /* Check for failure.  */
+
+  if (!status)
+    {
+      errno = EPERM;
+      return -1;
+    }
+
+  /* Read the file status from the array returned.  */
+
+  longs = (*android_java_env)->GetLongArrayElements (android_java_env,
+                                                    status, NULL);
+  android_exception_check_nonnull (longs, status);
+  mode = longs[0];
+  size = longs[1];
+  mtim = longs[2];
+  (*android_java_env)->ReleaseLongArrayElements (android_java_env, status,
+                                                longs, JNI_ABORT);
+  ANDROID_DELETE_LOCAL_REF (status);
+
+  /* Fill in STATB with this information.  */
+  memset (statb, 0, sizeof *statb);
+  statb->st_size = MAX (0, MIN (TYPE_MAXIMUM (off_t), size));
+  statb->st_mode = mode;
+#ifdef STAT_TIMESPEC
+  STAT_TIMESPEC (statb, st_mtim).tv_sec = mtim / 1000;
+  STAT_TIMESPEC (statb, st_mtim).tv_nsec = (mtim % 1000) * 1000000;
+#else /* !STAT_TIMESPEC */
+  /* Headers supplied by the NDK r10b contain a `struct stat' without
+     POSIX fields for nano-second timestamps.  */
+  statb->st_mtime = mtim / 1000;
+  statb->st_mtime_nsec = (mtim % 1000) * 1000000;
+#endif /* STAT_TIMESPEC */
+  statb->st_uid = getuid ();
+  statb->st_gid = getgid ();
+  return 0;
+}
+
+/* Detect if Emacs has access to the document designated by the the
+   documen ID ID_NAME within the tree URI_NAME.  If ID_NAME is NULL,
+   use the document ID in URI_NAME itself.
+
+   If WRITABLE, also check that the file is writable, which is true
+   if it is either a directory or its flags contains
+   FLAG_SUPPORTS_WRITE.
+
+   Value is 0 if the file is accessible, and -1 with errno set
+   appropriately if not.  */
+
+static int
+android_saf_access (const char *uri_name, const char *id_name,
+                   bool writable)
+{
+  jmethodID method;
+  jstring uri, id;
+  jint rc;
+
+  /* Now guarantee that it is safe to call functions which
+     synchronize with the SAF thread.  */
+
+  if (inside_saf_critical_section)
+    {
+      errno = EIO;
+      return -1;
+    }
+
+  /* Build strings for both URI and ID.  */
+  uri = (*android_java_env)->NewStringUTF (android_java_env, uri_name);
+  android_exception_check ();
+
+  if (id_name)
+    {
+      id = (*android_java_env)->NewStringUTF (android_java_env,
+                                             id_name);
+      android_exception_check_1 (uri);
+    }
+  else
+    id = NULL;
+
+  /* Try to retrieve the file status.  */
+  method = service_class.access_document;
+  inside_saf_critical_section = true;
+  rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
+                                                    emacs_service,
+                                                    service_class.class,
+                                                    method, uri, id,
+                                                    (jboolean) writable);
+  inside_saf_critical_section = false;
+
+  /* Check for exceptions and release unneeded local references.  */
+
+  if (id)
+    {
+      if (android_saf_exception_check (2, uri, id))
+       return -1;
+
+      ANDROID_DELETE_LOCAL_REF (id);
+    }
+  else if (android_saf_exception_check (1, uri))
+    return -1;
+
+  ANDROID_DELETE_LOCAL_REF (uri);
+
+  switch (rc)
+    {
+    case -1:
+      /* -1 means it doesn't exist.  */
+      errno = ENOENT;
+      return -1;
+
+    case -2:
+      /* -2 means access has been denied.  */
+      errno = EACCES;
+      return -1;
+
+    case -3:
+      /* -3 refers to an internal error.  */
+      errno = EIO;
+      return -1;
+    }
+
+  /* Return success.  */
+  return 0;
+}
+
+/* Delete the document designated by DOC_ID within the tree identified
+   through the URI TREE.  Return 0 if the document has been deleted,
+   set errno and return -1 upon failure.
+
+   DOC_NAME should be the name of the file itself, as a file name
+   whose constituent components lead to a document named DOC_ID.  It
+   isn't used to search for a document ID, but is used to invalidate
+   the file cache.  */
+
+static int
+android_saf_delete_document (const char *tree, const char *doc_id,
+                            const char *doc_name)
+{
+  jobject id, uri, name;
+  jmethodID method;
+  jint rc;
+
+  /* Build the strings holding the ID, URI and NAME.  */
+  id = (*android_java_env)->NewStringUTF (android_java_env,
+                                         doc_id);
+  android_exception_check ();
+  uri = (*android_java_env)->NewStringUTF (android_java_env,
+                                          tree);
+  android_exception_check_1 (id);
+  name = (*android_java_env)->NewStringUTF (android_java_env,
+                                           doc_name);
+  android_exception_check_2 (id, name);
+
+  /* Now, try to delete the document.  */
+  method = service_class.delete_document;
+  rc = (*android_java_env)->CallIntMethod (android_java_env,
+                                          emacs_service,
+                                          method, uri, id,
+                                          name);
+
+  if (android_saf_exception_check (3, id, uri, name))
+    return -1;
+
+  ANDROID_DELETE_LOCAL_REF (id);
+  ANDROID_DELETE_LOCAL_REF (uri);
+  ANDROID_DELETE_LOCAL_REF (name);
+
+  if (rc)
+    {
+      errno = EACCES;
+      return -1;
+    }
+
+  return 0;
+}
+
+/* Declared further below.  */
+static int android_document_id_from_name (const char *, const char *,
+                                         char **);
+
+/* Rename the document designated by DOC_ID inside the directory tree
+   identified by URI, which should be within the directory by the name
+   of DIR, to NAME.  If the document can't be renamed, return -1 and
+   set errno to a value describing the error.  Return 0 if the rename
+   is successful.
+
+   Android permits the same document to appear in multiple
+   directories, but stores the display name inside the document
+   ``inode'' itself instead of the directory entries that refer to it.
+   Because of this, this operation may cause other directory entries
+   outside DIR to be renamed.  */
+
+static int
+android_saf_rename_document (const char *uri, const char *doc_id,
+                            const char *dir, const char *name)
+{
+  int rc;
+  jstring uri1, doc_id1, dir1, name1;
+  jmethodID method;
+
+  /* Now build the strings for the URI, document ID, directory name
+     and directory ID.  */
+
+  uri1 = (*android_java_env)->NewStringUTF (android_java_env, uri);
+  android_exception_check ();
+  doc_id1 = (*android_java_env)->NewStringUTF (android_java_env, doc_id);
+  android_exception_check_1 (uri1);
+  dir1 = (*android_java_env)->NewStringUTF (android_java_env, dir);
+  android_exception_check_2 (doc_id1, uri1);
+  name1 = (*android_java_env)->NewStringUTF (android_java_env, name);
+  android_exception_check_3 (dir1, doc_id1, uri1);
+
+  method = service_class.rename_document;
+  rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
+                                                    emacs_service,
+                                                    service_class.class,
+                                                    method, uri1, doc_id1,
+                                                    dir1, name1);
+
+  /* Check for exceptions.  */
+
+  if (android_saf_exception_check (4, uri1, doc_id1, dir1, name1))
+    {
+      /* Substitute EXDEV for ENOSYS, so callers fall back on
+        delete-then-copy.  */
+
+      if (errno == ENOSYS)
+       errno = EXDEV;
+
+      return -1;
+    }
+
+  /* Delete unused local references.  */
+  ANDROID_DELETE_LOCAL_REF (uri1);
+  ANDROID_DELETE_LOCAL_REF (doc_id1);
+  ANDROID_DELETE_LOCAL_REF (dir1);
+  ANDROID_DELETE_LOCAL_REF (name1);
+
+  /* Then check for errors handled within the Java code.  */
+
+  if (rc == -1)
+    {
+      /* UnsupportedOperationException.  Trick the caller into falling
+        back on delete-then-copy code.  */
+      errno = EXDEV;
+      return -1;
+    }
+
+  return 0;
+}
+
+/* Move the document designated by *DOC_ID from the directory under
+   DIR_NAME to the directory designated by DST_ID.  All three
+   directories are located within the tree identified by the given
+   URI.
+
+   If the document's ID changes as a result of the movement, free
+   *DOC_ID and store the new document ID within.
+
+   Value is 0 upon success, -1 otherwise with errno set.  */
+
+static int
+android_saf_move_document (const char *uri, char **doc_id,
+                          const char *dir_name, const char *dst_id)
+{
+  char *src_id, *id;
+  jobject uri1, doc_id1, dir_name1, dst_id1, src_id1;
+  jstring result;
+  jmethodID method;
+  int rc;
+  const char *new_id;
+
+  /* Obtain the name of the source directory.  */
+  src_id = NULL;
+  rc = android_document_id_from_name (uri, dir_name, &src_id);
+
+  if (rc != 1)
+    {
+      /* This file is either not a directory or nonexistent.  */
+      xfree (src_id);
+
+      switch (rc)
+       {
+       case 0:
+         errno = ENOTDIR;
+         return -1;
+
+       case -1:
+       case -2:
+         errno = ENOENT;
+         return -1;
+
+       default:
+         emacs_abort ();
+       }
+    }
+
+  /* Build Java strings for all five arguments.  */
+  id = *doc_id;
+  uri1 = (*android_java_env)->NewStringUTF (android_java_env, uri);
+  android_exception_check ();
+  doc_id1 = (*android_java_env)->NewStringUTF (android_java_env, id);
+  android_exception_check_1 (uri1);
+  dir_name1 = (*android_java_env)->NewStringUTF (android_java_env, dir_name);
+  android_exception_check_2 (doc_id1, uri1);
+  dst_id1 = (*android_java_env)->NewStringUTF (android_java_env, dst_id);
+  android_exception_check_3 (dir_name1, doc_id1, uri1);
+  src_id1 = (*android_java_env)->NewStringUTF (android_java_env, src_id);
+  xfree (src_id);
+  android_exception_check_4 (dst_id1, dir_name1, doc_id1, uri1);
+
+  /* Do the rename.  */
+  method = service_class.move_document;
+  result = (*android_java_env)->CallObjectMethod (android_java_env,
+                                                 emacs_service,
+                                                 method, uri1,
+                                                 doc_id1, dir_name1,
+                                                 dst_id1, src_id1);
+  if (android_saf_exception_check (5, src_id1, dst_id1, dir_name1,
+                                  doc_id1, uri1))
+    {
+      /* Substitute EXDEV for ENOSYS, so callers fall back on
+        delete-then-copy.  */
+
+      if (errno == ENOSYS)
+       errno = EXDEV;
+
+      return -1;
+    }
+
+  /* Delete unused local references.  */
+  ANDROID_DELETE_LOCAL_REF (src_id1);
+  ANDROID_DELETE_LOCAL_REF (dst_id1);
+  ANDROID_DELETE_LOCAL_REF (dir_name1);
+  ANDROID_DELETE_LOCAL_REF (doc_id1);
+  ANDROID_DELETE_LOCAL_REF (uri1);
+
+  if (result)
+    {
+      /* The document ID changed.  Free id and replace *DOC_ID with
+        the new ID.  */
+      xfree (id);
+      new_id = (*android_java_env)->GetStringUTFChars (android_java_env,
+                                                      result, NULL);
+      android_exception_check_nonnull ((void *) new_id, result);
+      *doc_id = xstrdup (new_id);
+      (*android_java_env)->ReleaseStringUTFChars (android_java_env, result,
+                                                 new_id);
+      ANDROID_DELETE_LOCAL_REF (result);
+    }
+
+  return 0;
+}
+
+
+
+/* SAF directory vnode.  A file within a SAF directory tree is
+   identified by the URI of the directory tree itself, an opaque
+   ``file identifier'' value, and a display name.  This information is
+   recorded in each vnode representing either a directory or a file
+   itself.  */
+
+struct android_saf_tree_vnode
+{
+  /* The vnode data itself.  */
+  struct android_vnode vnode;
+
+  /* The URI of the directory tree represented.  This is Java string
+     data in ``modified UTF format'', which is essentially a modified
+     UTF-8 format capable of storing NULL bytes while also utilizing
+     NULL termination.  */
+  const char *tree_uri;
+
+  /* The ID of the document tree designated by TREE_URI.  */
+  char *tree_id;
+
+  /* The document ID of the directory represented, or NULL if this is
+     the root directory of the tree.  Since file and new vnodes don't
+     represent the root directory, this field is always set in
+     them.  */
+  char *document_id;
+
+  /* The file name of this tree vnode.  This is a ``path'' to the
+     file, where each directory component consists of the display name
+     of a directory leading up to a file within, terminated with a
+     directory separator character.  */
+  char *name;
+};
+
+struct android_saf_tree_vdir
+{
+  /* The virtual directory stream function table.  */
+  struct android_vdir vdir;
+
+  /* The next directory in `all_saf_tree_vdirs'.  */
+  struct android_saf_tree_vdir *next;
+
+  /* Name of this directory relative to the root file system.  */
+  char *name;
+
+  /* Local reference to the cursor representing the directory
+     stream.  */
+  jobject cursor;
+
+  /* The ``directory'' file descriptor used to identify this directory
+     stream, or -1.  */
+  int fd;
+};
+
+static struct android_vnode *android_saf_tree_name (struct android_vnode *,
+                                                   char *, size_t);
+static int android_saf_tree_open (struct android_vnode *, int,
+                                 mode_t, bool, int *, AAsset **);
+static void android_saf_tree_close (struct android_vnode *);
+static int android_saf_tree_unlink (struct android_vnode *);
+static int android_saf_tree_symlink (const char *, struct android_vnode *);
+static int android_saf_tree_rmdir (struct android_vnode *);
+static int android_saf_tree_rename (struct android_vnode *,
+                                   struct android_vnode *, bool);
+static int android_saf_tree_stat (struct android_vnode *, struct stat *);
+static int android_saf_tree_access (struct android_vnode *, int);
+static int android_saf_tree_mkdir (struct android_vnode *, mode_t);
+static int android_saf_tree_chmod (struct android_vnode *, mode_t, int);
+static struct android_vdir *android_saf_tree_opendir (struct android_vnode *);
+
+/* Vector of VFS operations associated with SAF tree VFS nodes.  */
+
+static struct android_vops saf_tree_vfs_ops =
+  {
+    android_saf_tree_name,
+    android_saf_tree_open,
+    android_saf_tree_close,
+    android_saf_tree_unlink,
+    android_saf_tree_symlink,
+    android_saf_tree_rmdir,
+    android_saf_tree_rename,
+    android_saf_tree_stat,
+    android_saf_tree_access,
+    android_saf_tree_mkdir,
+    android_saf_tree_chmod,
+    android_saf_tree_opendir,
+  };
+
+/* Vector of VFS operations associated with SAF file VFS nodes.
+   Defined later in the next page.  */
+static struct android_vops saf_file_vfs_ops;
+
+/* Vector of VFS operations associated with SAF ``new'' VFS nodes.
+   Defined two pages below.  */
+static struct android_vops saf_new_vfs_ops;
+
+/* Chain of all open SAF directory streams.  */
+static struct android_saf_tree_vdir *all_saf_tree_vdirs;
+
+/* Find the document ID of the file within TREE_URI designated by
+   NAME.
+
+   NAME is a ``file name'' comprised of the display names of
+   individual files.  Each constituent component prior to the last
+   must name a directory file within TREE_URI.
+
+   If NAME is not correct for the Java ``modified UTF-8'' coding
+   system, return -1 and set errno to ENOENT.
+
+   Upon success, return 0 or 1 (contingent upon whether or not the
+   last component within NAME is a directory) and place the document
+   ID of the named file in ID.
+
+   If the designated file doesn't exist, but the penultimate component
+   within NAME does and is also a directory, return -2 and place the
+   document ID of that directory within *ID.
+
+   If the designated file can't be located, return -1 and set errno
+   accordingly.  The reasons for which a file can't be located are not
+   all immediately obvious: quitting, for example, can cause document
+   ID lookup to be canceled.  */
+
+static int
+android_document_id_from_name (const char *tree_uri, const char *name,
+                              char **id)
+{
+  jobjectArray result;
+  jstring uri;
+  jbyteArray java_name;
+  jint rc;
+  jmethodID method;
+  const char *doc_id;
+
+  /* Verify the format of NAME.  Don't allow creating files that
+     contain characters that can't be encoded in Java.  */
+
+  if (android_verify_jni_string (name))
+    {
+      errno = ENOENT;
+      return -1;
+    }
+
+  /* Now guarantee that it is safe to call
+     `document_id_from_name'.  */
+
+  if (inside_saf_critical_section)
+    {
+      errno = EIO;
+      return -1;
+    }
+
+  /* First, create the array that will hold the result.  */
+  result = (*android_java_env)->NewObjectArray (android_java_env, 1,
+                                               java_string_class,
+                                               NULL);
+  android_exception_check ();
+
+  /* Next, create the string for the tree URI and name.  */
+  java_name = (*android_java_env)->NewStringUTF (android_java_env,
+                                                name);
+  android_exception_check_1 (result);
+  uri = (*android_java_env)->NewStringUTF (android_java_env, tree_uri);
+  android_exception_check_2 (result, java_name);
+
+  /* Now, call documentIdFromName.  This will synchronize with the SAF
+     thread, so make sure reentrant calls don't happen.  */
+  method = service_class.document_id_from_name;
+  inside_saf_critical_section = true;
+  rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
+                                                    emacs_service,
+                                                    service_class.class,
+                                                    method,
+                                                    uri, java_name,
+                                                    result);
+  inside_saf_critical_section = false;
+
+  if (android_saf_exception_check (3, result, uri, java_name))
+    return -1;
+
+  ANDROID_DELETE_LOCAL_REF (uri);
+  ANDROID_DELETE_LOCAL_REF (java_name);
+
+  /* If rc indicates failure, don't try to copy from result.  */
+
+  if (rc == -1)
+    {
+      ANDROID_DELETE_LOCAL_REF (result);
+      errno = ENOENT;
+      return -1;
+    }
+
+  eassert (rc == -2 || rc >= 0);
+
+  /* Otherwise, obtain the contents of the string returned in Java
+     ``UTF-8'' encoding.  */
+  uri = (*android_java_env)->GetObjectArrayElement (android_java_env,
+                                                   result, 0);
+  android_exception_check_nonnull (uri, result);
+  ANDROID_DELETE_LOCAL_REF (result);
+
+  doc_id = (*android_java_env)->GetStringUTFChars (android_java_env,
+                                                  uri, NULL);
+  android_exception_check_nonnull ((void *) doc_id, uri);
+
+  /* Make *ID its copy.  */
+  *id = xstrdup (doc_id);
+
+  /* And release it.  */
+  (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+                                             (jstring) uri, doc_id);
+  ANDROID_DELETE_LOCAL_REF (uri);
+  return rc;
+}
+
+static struct android_vnode *
+android_saf_tree_name (struct android_vnode *vnode, char *name,
+                      size_t length)
+{
+  char *remainder;
+  int rc;
+  struct android_saf_tree_vnode *vp, *new;
+  size_t vp_length;
+  char *filename, *fill, *doc_id, *end;
+  struct android_saf_root_vnode root;
+  struct android_saf_tree_vnode tree;
+
+  /* Canonicalize NAME.  */
+  remainder = android_vfs_canonicalize_name (name, &length);
+
+  /* If remainder is set, it's a name relative to the root vnode.  */
+  if (remainder)
+    goto parent_vnode;
+
+  /* If LENGTH is empty or NAME is a single directory separator,
+     return a copy of this vnode.  */
+
+  if (length < 1 || (*name == '/' && length == 1))
+    {
+      vp = xmalloc (sizeof *vp);
+      memcpy (vp, vnode, sizeof *vp);
+
+      /* Duplicate the information contained within VNODE.  */
+
+      vp->tree_uri = xstrdup (vp->tree_uri);
+      vp->tree_id = xstrdup (vp->tree_id);
+      vp->name = xstrdup (vp->name);
+
+      if (vp->document_id)
+       vp->document_id = xstrdup (vp->name);
+
+      return &vp->vnode;
+    }
+
+  /* Now, search for the document ID of the file designated by NAME
+     relative to this vnode.  */
+
+  vp = (struct android_saf_tree_vnode *) vnode;
+  vp_length = strlen (vp->name);
+
+  /* If NAME starts with a directory separator, move it past that.  */
+
+  if (*name == '/')
+    name++, length -= 1;
+
+  /* Concatenate VP->name with NAME.  Leave one byte at the end for an
+     extra trailing directory separator.  */
+
+  filename = xmalloc (vp_length + length + 2);
+  fill = stpcpy (filename, vp->name);
+  fill = stpcpy (fill, name);
+
+  /* And search for a document ID in the result.  */
+  rc = android_document_id_from_name (vp->tree_uri, name,
+                                     &doc_id);
+
+  if (rc < 0)
+    {
+      if (rc == -2)
+       {
+         /* This is a vnode representing a nonexistent file in a real
+            directory, so create a vnode whose sole use is to create
+            the file.  */
+
+         new = xmalloc (sizeof *new);
+         new->vnode.ops = &saf_new_vfs_ops;
+         new->vnode.type = ANDROID_VNODE_SAF_NEW;
+         new->vnode.flags = 0;
+
+         /* Here, doc_id is actually the ID of the penultimate
+            component in NAME.  */
+
+         new->document_id = doc_id;
+         new->tree_uri = xstrdup (vp->tree_uri);
+         new->tree_id = xstrdup (vp->tree_id);
+         new->name = filename;
+         return &new->vnode;
+       }
+
+      /* The document ID can't be found.  */
+      xfree (filename);
+      return NULL;
+    }
+
+  if (!rc)
+    {
+      /* rc set to 0 means that NAME is a regular file.  Detect if
+         NAME is supposed to be a directory; if it is, set errno to
+         ENODIR.  */
+
+      if (name[length - 1] == '/')
+       {
+         xfree (filename);
+         xfree (doc_id);
+         errno = ENOTDIR;
+         return NULL;
+       }
+    }
+
+  /* So this is either a directory or really a file.  Fortunately,
+     directory and file vnodes share everything in common except for a
+     few file operations, so create a new directory vnode with the new
+     file name and return it.  */
+
+  new = xmalloc (sizeof *new);
+  new->vnode.ops = (rc ? &saf_tree_vfs_ops
+                   : &saf_file_vfs_ops);
+  new->vnode.type = (rc ? ANDROID_VNODE_SAF_TREE
+                    : ANDROID_VNODE_SAF_FILE);
+  new->vnode.flags = 0;
+
+  if (rc)
+    {
+      /* If fill[-1] is not a directory separator character, append
+        one to the end of filename.  */
+
+      if (fill[-1] != '/')
+       {
+         *fill++ = '/';
+         *fill   = '\0';
+       }
+    }
+
+  new->document_id = doc_id;
+  new->tree_uri = xstrdup (vp->tree_uri);
+  new->tree_id = xstrdup (vp->tree_id);
+  new->name = filename;
+  return &new->vnode;
+
+ parent_vnode:
+  vp = (struct android_saf_tree_vnode *) vnode;
+
+  /* .. was encountered and the parent couldn't be found through
+     stripping off preceding components.
+
+     Find the parent vnode and name the rest of NAME starting from
+     there.  */
+
+  if (!vp->document_id)
+    {
+      /* VP->document_id is NULL, meaning this is the root of this
+        directory tree.  The parent vnode is an SAF root vnode with
+        VP->tree_uri's authority.  */
+
+      root.vnode.ops = &saf_root_vfs_ops;
+      root.vnode.type = ANDROID_VNODE_SAF_ROOT;
+      root.vnode.flags = 0;
+
+      /* Find the authority from the URI.  */
+
+      fill = (char *) vp->tree_uri;
+
+      if (strncmp (fill, "content://", 10))
+       emacs_abort ();
+
+      /* Skip the content header.  */
+      fill += sizeof "content://" - 1;
+
+      /* The authority segment of the URI is between here and the
+        next slash.  */
+
+      end = strchr (fill, '/');
+
+      if (!end)
+       emacs_abort ();
+
+      root.authority = xmalloc (end - fill + 1);
+      memcpy (root.authority, fill, end - fill);
+      root.authority[end - fill] = '\0';
+
+      /* Now search using this vnode.  */
+      vnode = (*root.vnode.ops->name) (&root.vnode, remainder,
+                                      strlen (remainder));
+      xfree (root.authority);
+      return vnode;
+    }
+
+  /* Otherwise, strip off the last directory component.  */
+
+  fill = strrchr (vp->name, '/');
+  if (!fill)
+    emacs_abort ();
+
+  /* Create a new vnode at the top of the directory tree, and search
+     for remainder from there.  */
+
+  tree.vnode.ops = &saf_tree_vfs_ops;
+  tree.vnode.type = ANDROID_VNODE_SAF_TREE;
+  tree.vnode.flags = 0;
+  tree.document_id = NULL;
+  tree.name = (char *) "/";
+  tree.tree_uri = vp->tree_uri;
+  tree.tree_id = vp->tree_id;
+
+  length   = strlen (remainder + (*remainder == '/'));
+  filename = xmalloc (fill - vp->name + length + 2);
+  fill = mempcpy (filename, vp->name,
+                 /* Include the separator character (*FILL) within
+                    this copy.  */
+                 fill - vp->name + 1);
+  /* Skip a leading separator in REMAINDER.  */
+  strcpy (fill, remainder + (*remainder == '/'));
+
+  /* Use this filename to find a vnode relative to the start of this
+     tree.  */
+
+  vnode = android_saf_tree_name (&tree.vnode, filename,
+                                strlen (filename));
+  xfree (filename);
+  return vnode;
+}
+
+static int
+android_saf_tree_open (struct android_vnode *vnode, int flags,
+                      mode_t mode, bool asset_p, int *fd,
+                      AAsset **asset)
+{
+  /* Don't allow opening this special directory.  */
+  errno = ENOSYS;
+  return -1;
+}
+
+static void
+android_saf_tree_close (struct android_vnode *vnode)
+{
+  struct android_saf_tree_vnode *vp;
+  int save_errno;
+
+  vp = (struct android_saf_tree_vnode *) vnode;
+
+  save_errno = errno;
+  xfree ((void *) vp->tree_uri);
+  xfree (vp->tree_id);
+  xfree (vp->name);
+  xfree (vp->document_id);
+  xfree (vp);
+  errno = save_errno;
+}
+
+static int
+android_saf_tree_unlink (struct android_vnode *vnode)
+{
+  errno = EISDIR;
+  return -1;
+}
+
+static int
+android_saf_tree_symlink (const char *target, struct android_vnode *vnode)
+{
+  errno = EPERM;
+  return -1;
+}
+
+static int
+android_saf_tree_rmdir (struct android_vnode *vnode)
+{
+  struct android_saf_tree_vnode *vp;
+
+  vp = (struct android_saf_tree_vnode *) vnode;
+
+  /* Don't allow deleting the root directory.  */
+
+  if (!vp->document_id)
+    {
+      errno = EROFS;
+      return -1;
+    }
+
+  return android_saf_delete_document (vp->tree_uri,
+                                     vp->document_id,
+                                     vp->name);
+}
+
+static int
+android_saf_tree_rename (struct android_vnode *src,
+                        struct android_vnode *dst,
+                        bool keep_existing)
+{
+  char *last, *dst_last;
+  struct android_saf_tree_vnode *vp, *vdst;
+  char path[PATH_MAX], path1[PATH_MAX];
+  char *fill, *dst_id;
+  int rc;
+
+  /* If dst isn't a tree, file or new vnode, return EXDEV.  */
+
+  if (dst->type != ANDROID_VNODE_SAF_TREE
+      && dst->type != ANDROID_VNODE_SAF_FILE
+      && dst->type != ANDROID_VNODE_SAF_NEW)
+    {
+      errno = EXDEV;
+      return -1;
+    }
+
+  vp = (struct android_saf_tree_vnode *) src;
+  vdst = (struct android_saf_tree_vnode *) dst;
+
+  /* if vp and vdst refer to different tree URIs, return EXDEV.  */
+
+  if (strcmp (vp->tree_uri, vdst->tree_uri))
+    {
+      errno = EXDEV;
+      return -1;
+    }
+
+  /* If `keep_existing' and the destination vnode designates an
+     existing file, return EEXIST.  */
+
+  if (keep_existing && dst->type != ANDROID_VNODE_SAF_NEW)
+    {
+      errno = EEXIST;
+      return -1;
+    }
+
+  /* Unix `rename' maps to two Android content provider operations.
+     The first case is a simple rename, where src and dst are both
+     located within the same directory.  Compare the file names of
+     both up to the component before the last.  */
+
+  last = strrchr (vp->name, '/');
+  eassert (last != NULL);
+
+  if (last[1] == '\0')
+    {
+      if (last == vp->name)
+       {
+         /* This means the caller is trying to rename the root
+            directory of the tree.  */
+         errno = EROFS;
+         return -1;
+       }
+
+      /* The name is terminated by a trailing directory separator.
+         Search backwards for the preceding directory separator.  */
+      last = memrchr (vp->name, '/', last - vp->name);
+      eassert (last != NULL);
+    }
+
+  /* Find the end of the second-to-last component in vdst's name.  */
+
+  dst_last = strrchr (vdst->name, '/');
+  eassert (dst_last != NULL);
+
+  if (dst_last[1] == '\0')
+    {
+      if (dst_last == vdst->name)
+       {
+         /* Forbid overwriting the root of the tree either.  */
+         errno = EROFS;
+         return -1;
+       }
+
+      dst_last = memrchr (vdst->name, '/', dst_last - vdst->name);
+      eassert (dst_last != NULL);
+    }
+
+  if (dst_last - vdst->name != last - vp->name
+      || memcmp (vp->name, vdst->name, last - vp->name))
+    {
+      /* The second case is where the file must be moved from one
+         directory to the other, and possibly then recreated under a
+         new name.  */
+
+      /* The names of the source and destination directories will have
+        to be copied to path.  */
+
+      if (last - vp->name >= PATH_MAX
+         || dst_last - vdst->name >= PATH_MAX)
+       {
+         errno = ENAMETOOLONG;
+         return -1;
+       }
+
+      fill = mempcpy (path, vp->name, last - vp->name);
+      *fill = '\0';
+
+      /* If vdst doesn't already exist, its document_id field is
+        already the name of its parent directory.  */
+
+      if (dst->type == ANDROID_VNODE_SAF_NEW)
+       {
+         /* First, move the document.  This will update
+            VP->document_id if it changes.  */
+
+         if (android_saf_move_document (vp->tree_uri,
+                                        &vp->document_id,
+                                        path,
+                                        vdst->document_id))
+           return -1;
+
+         fill = mempcpy (path, vdst->name, dst_last - vdst->name);
+         *fill = '\0';
+
+         /* Next, rename the document, if its display name differs
+            from that of the source.  */
+
+         if (strcmp (dst_last + 1, last + 1)
+             /* By now vp->document_id is already in the destination
+                directory.  */
+             && android_saf_rename_document (vp->tree_uri,
+                                             vp->document_id,
+                                             path,
+                                             dst_last + 1))
+           return -1;
+
+         return 0;
+       }
+
+      /* Retrieve the ID designating the destination document's parent
+        directory.  */
+
+      fill = mempcpy (path1, vdst->name, dst_last - vdst->name);
+      *fill = '\0';
+
+      rc = android_document_id_from_name (vp->tree_uri,
+                                         path1, &dst_id);
+
+      if (rc != 1)
+       {
+         /* This file is either not a directory or nonexistent.  */
+
+         switch (rc)
+           {
+           case 0:
+             errno = ENOTDIR;
+             goto error;
+
+           case -1:
+             /* dst_id is not set here, as the penultimate component
+                also couldn't be located.  */
+             errno = ENOENT;
+             return -1;
+
+           case -2:
+             errno = ENOENT;
+             goto error;
+
+           default:
+             emacs_abort ();
+           }
+       }
+
+      /* vdst already exists, so it needs to be deleted first.  */
+
+      if (android_saf_delete_document (vdst->tree_uri,
+                                      vdst->document_id,
+                                      vdst->name))
+        goto error;
+
+      /* First, move the document.  This will update
+        VP->document_id if it changes.  */
+
+      if (android_saf_move_document (vp->tree_uri,
+                                    &vp->document_id,
+                                    path, dst_id))
+        goto error;
+
+      /* Next, rename the document, if its display name differs from
+        that of the source.  */
+
+      if (strcmp (dst_last + 1, last + 1)
+         /* By now vp->document_id is already in the destination
+            directory.  */
+         && android_saf_rename_document (vp->tree_uri,
+                                         vp->document_id,
+                                         path1,
+                                         dst_last + 1))
+       goto error;
+
+      xfree (dst_id);
+      return 0;
+
+    error:
+      xfree (dst_id);
+      return 1;
+    }
+
+  /* Otherwise, do this simple rename.  The name of the parent
+     directory is required, as it provides the directory whose entries
+     will be modified.  */
+
+  if (last - vp->name >= PATH_MAX)
+    {
+      errno = ENAMETOOLONG;
+      return -1;
+    }
+
+  /* If the destination document exists, delete it.  */
+
+  if (dst->type != ANDROID_VNODE_SAF_NEW
+      && android_saf_delete_document (vdst->tree_uri,
+                                     vdst->document_id,
+                                     vdst->name))
+    return -1;
+
+  fill = mempcpy (path, vp->name, last - vp->name);
+  *fill = '\0';
+  return android_saf_rename_document (vp->tree_uri,
+                                     vp->document_id,
+                                     path,
+                                     dst_last + 1);
+}
+
+static int
+android_saf_tree_stat (struct android_vnode *vnode,
+                      struct stat *statb)
+{
+  struct android_saf_tree_vnode *vp;
+
+  vp = (struct android_saf_tree_vnode *) vnode;
+
+  return android_saf_stat (vp->tree_uri, vp->document_id,
+                          statb);
+}
+
+static int
+android_saf_tree_access (struct android_vnode *vnode, int mode)
+{
+  struct android_saf_tree_vnode *vp;
+
+  vp = (struct android_saf_tree_vnode *) vnode;
+
+  /* Validate MODE.  */
+
+  if (mode != F_OK && !(mode & (W_OK | X_OK | R_OK)))
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  return android_saf_access (vp->tree_uri, vp->document_id,
+                            mode & W_OK);
+}
+
+static int
+android_saf_tree_mkdir (struct android_vnode *vnode, mode_t mode)
+{
+  /* Since tree vnodes represent files that already exist, return
+     EEXIST.  */
+  errno = EEXIST;
+  return -1;
+}
+
+static int
+android_saf_tree_chmod (struct android_vnode *vnode, mode_t mode,
+                       int flags)
+{
+  /* Return EACCESS should MODE contain unusual bits besides the
+     standard file access permissions.  */
+
+  if (mode & ~0777)
+    {
+      errno = EACCES;
+      return -1;
+    }
+
+  /* Otherwise, no further action is necessary, as SAF nodes already
+     pretend to be S_IRUSR | S_IWUSR.  */
+  return 0;
+}
+
+/* Open a database Cursor containing each directory entry within the
+   supplied SAF tree vnode VP.
+
+   Value is NULL upon failure with errno set to a suitable value, a
+   local reference to the Cursor object otherwise.  */
+
+static jobject
+android_saf_tree_opendir_1 (struct android_saf_tree_vnode *vp)
+{
+  jobject uri, id, cursor;
+  jmethodID method;
+
+  if (inside_saf_critical_section)
+    {
+      errno = EIO;
+      return NULL;
+    }
+
+  /* Build strings for both URI and ID.  */
+  uri = (*android_java_env)->NewStringUTF (android_java_env,
+                                          vp->tree_uri);
+  android_exception_check ();
+
+  if (vp->document_id)
+    {
+      id = (*android_java_env)->NewStringUTF (android_java_env,
+                                             vp->document_id);
+      android_exception_check_1 (uri);
+    }
+  else
+    id = NULL;
+
+  /* Try to open the cursor.  */
+  method = service_class.open_document_directory;
+  inside_saf_critical_section = true;
+  cursor
+    = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env,
+                                                      emacs_service,
+                                                      service_class.class,
+                                                      method, uri, id);
+  inside_saf_critical_section = false;
+
+  if (id)
+    {
+      if (android_saf_exception_check (2, id, uri))
+       return NULL;
+
+      ANDROID_DELETE_LOCAL_REF (id);
+    }
+  else if (android_saf_exception_check (1, uri))
+    return NULL;
+
+  ANDROID_DELETE_LOCAL_REF (uri);
+
+  /* Return the resulting cursor.  */
+  return cursor;
+}
+
+static struct dirent *
+android_saf_tree_readdir (struct android_vdir *vdir)
+{
+  struct android_saf_tree_vdir *dir;
+  static struct dirent *dirent;
+  jobject entry, d_name;
+  jint d_type;
+  jmethodID method;
+  size_t length, size;
+  const char *chars;
+
+  dir = (struct android_saf_tree_vdir *) vdir;
+
+  /* Try to read one entry from the cursor.  */
+  method = service_class.read_directory_entry;
+  entry
+    = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env,
+                                                      emacs_service,
+                                                      service_class.class,
+                                                      method, dir->cursor);
+  android_exception_check ();
+
+  /* If ENTRY is NULL, we're at the end of the directory.  */
+
+  if (!entry)
+    {
+      xfree (entry);
+      entry = NULL;
+      return NULL;
+    }
+
+  /* Load both fields from ENTRY.  */
+  d_name = (*android_java_env)->GetObjectField (android_java_env, entry,
+                                               entry_class.d_name);
+  if (!d_name)
+    {
+      /* If an error transpires, d_name is set to NULL.  */
+      (*android_java_env)->ExceptionClear (android_java_env);
+      ANDROID_DELETE_LOCAL_REF (entry);
+
+      /* XXX: what would be a better error indication? */
+      errno = EIO;
+      return NULL;
+    }
+
+  /* d_type is 1 if this is a directory, and 0 if it's a regular
+     file.  */
+  d_type = (*android_java_env)->GetIntField (android_java_env, entry,
+                                            entry_class.d_type);
+  ANDROID_DELETE_LOCAL_REF (entry);
+
+  /* Copy the name of the directory over.  */
+  chars = (*android_java_env)->GetStringUTFChars (android_java_env,
+                                                 (jstring) d_name,
+                                                 NULL);
+  android_exception_check_nonnull ((void *) chars, d_name);
+
+  /* Figure out how large it is, and then resize dirent to fit.  */
+  length = strlen (chars) + 1;
+  size   = offsetof (struct dirent, d_name) + length;
+  dirent = xrealloc (dirent, size);
+
+  /* Clear dirent.  */
+  memset (dirent, 0, size);
+
+  /* Fill in the generic directory information and copy the string
+     over.  */
+  dirent->d_ino = 0;
+  dirent->d_off = 0;
+  dirent->d_reclen = size;
+  dirent->d_type = d_type ? DT_DIR : DT_UNKNOWN;
+  strcpy (dirent->d_name, chars);
+
+  /* Release the string data and the local reference to STRING.  */
+  (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+                                             (jstring) d_name,
+                                             chars);
+  ANDROID_DELETE_LOCAL_REF (d_name);
+  return dirent;
+}
+
+static void
+android_saf_tree_closedir (struct android_vdir *vdir)
+{
+  struct android_saf_tree_vdir *dir, **next, *tem;
+
+  dir = (struct android_saf_tree_vdir *) vdir;
+
+  /* dir->name is allocated by asprintf, which uses regular
+     malloc.  */
+  free (dir->name);
+
+  /* Yes, DIR->cursor is a local reference.  */
+  ANDROID_DELETE_LOCAL_REF (dir->cursor);
+
+  /* If the ``directory file descriptor'' has been opened, close
+     it.  */
+  if (dir->fd != -1)
+    close (dir->fd);
+
+  /* Now unlink this directory.  */
+
+  for (next = &all_saf_tree_vdirs; (tem = *next);)
+    {
+      if (tem == dir)
+       *next = dir->next;
+      else
+       next = &(*next)->next;
+    }
+
+  xfree (dir);
+}
+
+static int
+android_saf_tree_dirfd (struct android_vdir *vdir)
+{
+  struct android_saf_tree_vdir *dir;
+
+  dir = (struct android_saf_tree_vdir *) vdir;
+
+  /* Since `android_saf_tree_opendir' tries to avoid opening a file
+     descriptor if readdir isn't called, dirfd can fail if open fails.
+
+     open sets errno to a set of errors different from what POSIX
+     stipulates for dirfd, but for ease of implementation the open
+     errors are used instead.  */
+
+  if (dir->fd >= 0)
+    return dir->fd;
+
+  dir->fd = open ("/dev/null", O_RDONLY | O_CLOEXEC);
+  return dir->fd;
+}
+
+static struct android_vdir *
+android_saf_tree_opendir (struct android_vnode *vnode)
+{
+  struct android_saf_tree_vnode *vp;
+  struct android_saf_tree_vdir *dir;
+  char *fill, *end;
+  jobject cursor;
+  char component[PATH_MAX];
+
+  vp = (struct android_saf_tree_vnode *) vnode;
+
+  /* First, fill the directory stream with the right functions and
+     file name.  */
+
+  dir = xmalloc (sizeof *dir);
+  dir->vdir.readdir = android_saf_tree_readdir;
+  dir->vdir.closedir = android_saf_tree_closedir;
+  dir->vdir.dirfd = android_saf_tree_dirfd;
+
+  /* Find the authority from the URI.  */
+
+  fill = (char *) vp->tree_uri;
+
+  if (strncmp (fill, "content://", 10))
+    emacs_abort ();
+
+  /* Skip the content header.  */
+  fill += sizeof "content://" - 1;
+
+  /* The authority segment of the URI is between here and the
+     next slash.  */
+
+  end = strchr (fill, '/');
+
+  if (!end)
+    emacs_abort ();
+
+  if (end - fill >= PATH_MAX)
+    {
+      errno = ENAMETOOLONG;
+      xfree (dir);
+      return NULL;
+    }
+
+  /* Copy the authority over.  */
+
+  memcpy (component, fill, end - fill);
+  component[end - fill] = '\0';
+
+  if (asprintf (&dir->name, "/content/storage/%s/%s%s",
+               component, vp->tree_id, vp->name) < 0)
+    {
+      /* Out of memory.  */
+      xfree (dir);
+      memory_full (0);
+    }
+
+  /* Now open a cursor that iterates through each file in this
+     directory.  */
+
+  cursor = android_saf_tree_opendir_1 (vp);
+
+  if (!cursor)
+    {
+      xfree (dir);
+      xfree (dir->name);
+      return NULL;
+    }
+
+  dir->cursor = cursor;
+  dir->fd = -1;
+  dir->next = all_saf_tree_vdirs;
+  all_saf_tree_vdirs = dir;
+  return &dir->vdir;
+}
+
+/* Create a vnode designating the file NAME within a directory tree
+   whose identifier is TREE.  As with all other `name' functions, NAME
+   may be modified.
+
+   AUTHORITY is the name of the content provider authority that is
+   offering TREE.
+
+   Value is NULL and errno is set if no document tree or provider by
+   those names exists, or some other error takes place (for example,
+   if TREE and AUTHORITY aren't encoded correctly.)  */
+
+static struct android_vnode *
+android_saf_tree_from_name (char *name, const char *tree,
+                           const char *authority)
+{
+  struct android_saf_tree_vnode root;
+  jobject tree_string, authority_string, result;
+  jmethodID method;
+  const char *uri;
+  struct android_vnode *vp;
+
+  /* It's not a given that NAME and TREE are actually in the modified
+     UTF-8 format used by the JVM to encode strings, and the JVM
+     aborts when encountering a string that is not.  Make sure they
+     are valid before continuing.  */
+
+  if (android_verify_jni_string (name)
+      || android_verify_jni_string (authority))
+    {
+      errno = ENOENT;
+      return NULL;
+    }
+
+  tree_string = (*android_java_env)->NewStringUTF (android_java_env,
+                                                  tree);
+  android_exception_check ();
+
+  authority_string
+    = (*android_java_env)->NewStringUTF (android_java_env,
+                                        authority);
+  android_exception_check_1 (tree_string);
+
+  /* Now create the URI and detect if Emacs has the rights to access
+     it.  */
+
+  method = service_class.get_tree_uri;
+  result
+    = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env,
+                                                      emacs_service,
+                                                      service_class.class,
+                                                      method, tree_string,
+                                                      authority_string);
+  android_exception_check_2 (tree_string, authority_string);
+  ANDROID_DELETE_LOCAL_REF (tree_string);
+  ANDROID_DELETE_LOCAL_REF (authority_string);
+
+  /* If it doesn't, return NULL and set errno to ENOENT.  */
+
+  if (!result)
+    {
+      errno = ENOENT;
+      return NULL;
+    }
+
+  /* Otherwise, decode this string.  */
+  uri = (*android_java_env)->GetStringUTFChars (android_java_env, result,
+                                               NULL);
+  android_exception_check_nonnull ((void *) uri, result);
+
+  /* Fill in root.tree_uri with values that represent the root of this
+     document tree.  */
+
+  root.vnode.ops = &saf_tree_vfs_ops;
+  root.vnode.type = ANDROID_VNODE_SAF_TREE;
+  root.vnode.flags = 0;
+  root.tree_uri = uri;
+  root.tree_id = (char *) tree;
+  root.document_id = NULL;
+  root.name = (char *) "/";
+
+  vp = (*root.vnode.ops->name) (&root.vnode, name, strlen (name));
+  (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+                                             (jstring) result, uri);
+  ANDROID_DELETE_LOCAL_REF (result);
+  return vp;
+}
+
+/* Return any open SAF tree directory stream for which dirfd has
+   returned the file descriptor DIRFD.  Return NULL otherwise.  */
+
+static struct android_saf_tree_vdir *
+android_saf_tree_get_directory (int dirfd)
+{
+  struct android_saf_tree_vdir *dir;
+
+  for (dir = all_saf_tree_vdirs; dir; dir = dir->next)
+    {
+      if (dir->fd == dirfd && dirfd != -1)
+       return dir;
+    }
+
+  return NULL;
+}
+
+
+
+/* SAF file vnode.  The information used to uniquely identify a file
+   is identical to that used to identify an SAF directory, but the
+   vnode operations are different.  */
+
+/* Define `struct android_saf_file_vnode' to be identical to a file
+   vnode.  */
+
+#define android_saf_file_vnode android_saf_tree_vnode
+
+/* Structure describing an open ParcelFileDescriptor.  */
+
+struct android_parcel_fd
+{
+  /* The next open parcel file descriptor.  */
+  struct android_parcel_fd *next;
+
+  /* Global reference to this parcel file descriptor.  */
+  jobject descriptor;
+
+  /* The modification time of this parcel file descriptor, or
+     `invalid_timespec'.  */
+  struct timespec mtime;
+
+  /* The file descriptor itself.  */
+  int fd;
+};
+
+static struct android_vnode *android_saf_file_name (struct android_vnode *,
+                                                   char *, size_t);
+static int android_saf_file_open (struct android_vnode *, int,
+                                 mode_t, bool, int *, AAsset **);
+static int android_saf_file_unlink (struct android_vnode *);
+static int android_saf_file_rmdir (struct android_vnode *);
+static struct android_vdir *android_saf_file_opendir (struct android_vnode *);
+
+/* Vector of VFS operations associated with SAF tree VFS nodes.  */
+
+static struct android_vops saf_file_vfs_ops =
+  {
+    android_saf_file_name,
+    android_saf_file_open,
+    android_saf_tree_close,
+    android_saf_file_unlink,
+    android_saf_tree_symlink,
+    android_saf_file_rmdir,
+    android_saf_tree_rename,
+    android_saf_tree_stat,
+    android_saf_tree_access,
+    android_saf_tree_mkdir,
+    android_saf_tree_chmod,
+    android_saf_file_opendir,
+  };
+
+/* Chain of all parcel file descriptors currently open.  */
+static struct android_parcel_fd *open_parcel_fds;
+
+static struct android_vnode *
+android_saf_file_name (struct android_vnode *vnode, char *name,
+                      size_t length)
+{
+  struct android_saf_file_vnode *vp;
+
+  /* If LENGTH is empty, make a copy of this vnode and return it.  */
+
+  if (length < 1)
+    {
+      vp = xmalloc (sizeof *vp);
+      memcpy (vp, vnode, sizeof *vp);
+
+      /* Duplicate the information contained within VNODE.  */
+
+      vp->tree_uri = xstrdup (vp->tree_uri);
+      vp->tree_id = xstrdup (vp->tree_id);
+      vp->name = xstrdup (vp->name);
+      vp->document_id = xstrdup (vp->name);
+
+      return &vp->vnode;
+    }
+
+  /* A file vnode has no children of its own.  */
+  errno = ENOTDIR;
+  return NULL;
+}
+
+static int
+android_saf_file_open (struct android_vnode *vnode, int flags,
+                      mode_t mode, bool asset_p, int *fd_return,
+                      AAsset **asset)
+{
+  struct android_saf_file_vnode *vp;
+  jobject uri, id, descriptor;
+  jmethodID method;
+  jboolean trunc, write;
+  jint fd;
+  struct android_parcel_fd *info;
+  struct stat statb;
+
+  if (inside_saf_critical_section)
+    {
+      errno = EIO;
+      return -1;
+    }
+
+  /* Build strings for both the URI and ID.  */
+
+  vp = (struct android_saf_file_vnode *) vnode;
+  uri = (*android_java_env)->NewStringUTF (android_java_env,
+                                          vp->tree_uri);
+  android_exception_check ();
+  id = (*android_java_env)->NewStringUTF (android_java_env,
+                                         vp->document_id);
+  android_exception_check_1 (uri);
+
+  /* Open a parcel file descriptor according to flags.  */
+
+  method = service_class.open_document;
+  trunc  = (flags & O_TRUNC);
+  write  = (((flags & O_RDWR) == O_RDWR) || (flags & O_WRONLY));
+  inside_saf_critical_section = true;
+  descriptor
+    = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env,
+                                                      emacs_service,
+                                                      service_class.class,
+                                                      method, uri, id,
+                                                      write, trunc);
+  inside_saf_critical_section = false;
+
+  if (android_saf_exception_check (2, uri, id))
+    return -1;
+
+  ANDROID_DELETE_LOCAL_REF (uri);
+  ANDROID_DELETE_LOCAL_REF (id);
+
+  if (!descriptor)
+    {
+      /* Assume that permission has been denied if DESCRIPTOR cannot
+        be opened.  */
+      errno = EPERM;
+      return -1;
+    }
+
+  /* Allocate a record for this file descriptor.  Parcel file
+     descriptors should be closed using their own `close' function,
+     which takes care of notifying the source that it has been
+     closed.  */
+  info = xmalloc (sizeof *info);
+
+  /* Now obtain the file descriptor.  */
+  fd = (*android_java_env)->CallIntMethod (android_java_env,
+                                          descriptor,
+                                          fd_class.get_fd);
+  android_exception_check_1 (descriptor);
+
+  /* Create a global reference to descriptor.  */
+  info->descriptor
+    = (*android_java_env)->NewGlobalRef (android_java_env,
+                                        descriptor);
+
+  if (!info->descriptor)
+    {
+      /* If the global reference can't be created, delete
+        descriptor.  */
+      (*android_java_env)->ExceptionClear (android_java_env);
+      (*android_java_env)->CallVoidMethod (android_java_env,
+                                          descriptor,
+                                          fd_class.close);
+      (*android_java_env)->ExceptionClear (android_java_env);
+      ANDROID_DELETE_LOCAL_REF (descriptor);
+
+      /* Free INFO.  */
+      xfree (info);
+
+      /* Set errno to EMFILE and return.  */
+      errno = EMFILE;
+      return -1;
+    }
+
+  /* Delete the local ref to DESCRIPTOR.  */
+  ANDROID_DELETE_LOCAL_REF (descriptor);
+
+  /* Try to retrieve the modification time of this file from the
+     content provider.  */
+
+  if (!android_saf_stat (vp->tree_uri, vp->document_id,
+                        &statb))
+    info->mtime = get_stat_mtime (&statb);
+  else
+    info->mtime = invalid_timespec ();
+
+  /* Set info->fd and chain it onto the list.  */
+  info->fd = fd;
+  info->next = open_parcel_fds;
+  open_parcel_fds = info;
+
+  /* Return the file descriptor.  */
+  *fd_return = fd;
+  return 0;
+}
+
+static int
+android_saf_file_unlink (struct android_vnode *vnode)
+{
+  struct android_saf_file_vnode *vp;
+
+  vp = (struct android_saf_file_vnode *) vnode;
+  return android_saf_delete_document (vp->tree_uri,
+                                     vp->document_id,
+                                     vp->name);
+}
+
+static int
+android_saf_file_rmdir (struct android_vnode *vnode)
+{
+  errno = ENOTDIR;
+  return -1;
+}
+
+static struct android_vdir *
+android_saf_file_opendir (struct android_vnode *vnode)
+{
+  errno = ENOTDIR;
+  return NULL;
+}
+
+/* Close FD if it's a parcel file descriptor and return true.
+   If FD isn't, return false.
+
+   Such file descriptors need to be closed using a function
+   written in Java, to tell the sender that it has been
+   closed.  */
+
+static bool
+android_close_parcel_fd (int fd)
+{
+  struct android_parcel_fd *tem, **next, *temp;
+
+  for (next = &open_parcel_fds; (tem = *next);)
+    {
+      if (tem->fd == fd)
+       {
+         (*android_java_env)->CallVoidMethod (android_java_env,
+                                              tem->descriptor,
+                                              fd_class.close);
+
+         /* Ignore exceptions for the same reason EINTR errors from
+            `close' should be ignored.  */
+         (*android_java_env)->ExceptionClear (android_java_env);
+         (*android_java_env)->DeleteGlobalRef (android_java_env,
+                                               tem->descriptor);
+
+         temp = tem->next;
+         xfree (tem);
+         *next = temp;
+
+         return true;
+       }
+      else
+       next = &(*next)->next;
+    }
+
+  return false;
+}
+
+
+
+/* SAF ``new'' vnodes.  These nodes share their data structures
+   with tree and file vnodes, but represent files that don't actually
+   exist within a directory.  In them, the document ID represents not
+   the file designated by the vnode itself, but rather its parent
+   directory.
+
+   The only vops defined serve to create directories or files, at
+   which point the vnode becomes invalid.  */
+
+#define android_saf_new_vnode android_saf_tree_vnode
+
+static struct android_vnode *android_saf_new_name (struct android_vnode *,
+                                                  char *, size_t);
+static int android_saf_new_open (struct android_vnode *, int,
+                                mode_t, bool, int *, AAsset **);
+static int android_saf_new_unlink (struct android_vnode *);
+static int android_saf_new_symlink (const char *, struct android_vnode *);
+static int android_saf_new_rmdir (struct android_vnode *);
+static int android_saf_new_rename (struct android_vnode *,
+                                  struct android_vnode *, bool);
+static int android_saf_new_stat (struct android_vnode *, struct stat *);
+static int android_saf_new_access (struct android_vnode *, int);
+static int android_saf_new_mkdir (struct android_vnode *, mode_t);
+static int android_saf_new_chmod (struct android_vnode *, mode_t, int);
+static struct android_vdir *android_saf_new_opendir (struct android_vnode *);
+
+/* Vector of VFS operations associated with SAF new VFS nodes.  */
+
+static struct android_vops saf_new_vfs_ops =
+  {
+    android_saf_new_name,
+    android_saf_new_open,
+    android_saf_tree_close,
+    android_saf_new_unlink,
+    android_saf_new_symlink,
+    android_saf_new_rmdir,
+    android_saf_new_rename,
+    android_saf_new_stat,
+    android_saf_new_access,
+    android_saf_new_mkdir,
+    android_saf_new_chmod,
+    android_saf_new_opendir,
+  };
+
+static struct android_vnode *
+android_saf_new_name (struct android_vnode *vnode, char *name,
+                     size_t length)
+{
+  struct android_saf_new_vnode *vp;
+
+  /* If LENGTH is empty, make a copy of this vnode and return it.  */
+
+  if (length < 1)
+    {
+      vp = xmalloc (sizeof *vp);
+      memcpy (vp, vnode, sizeof *vp);
+
+      /* Duplicate the information contained within VNODE.  */
+
+      vp->tree_uri = xstrdup (vp->tree_uri);
+      vp->tree_id = xstrdup (vp->tree_id);
+      vp->name = xstrdup (vp->name);
+      vp->document_id = xstrdup (vp->name);
+
+      return &vp->vnode;
+    }
+
+  /* A nonexistent vnode has no children of its own.  */
+  errno = ENOTDIR;
+  return NULL;
+}
+
+static int
+android_saf_new_open (struct android_vnode *vnode, int flags,
+                     mode_t mode, bool asset_p, int *fd_return,
+                     AAsset **asset)
+{
+  struct android_saf_new_vnode *vp;
+  char *end;
+  jstring name, id, uri, new_id;
+  const char *new_doc_id;
+  jmethodID method;
+
+  /* If creating a file wasn't intended, return ENOENT.  */
+
+  if (!(flags & O_CREAT))
+    {
+      errno = ENOENT;
+      return -1;
+    }
+
+  /* If vp->name indicates that it's a directory, return ENOENT.  */
+
+  vp = (struct android_saf_new_vnode *) vnode;
+  end = strrchr (vp->name, '/');
+
+  /* VP->name must contain at least one directory separator.  */
+  eassert (end);
+
+  if (end[1] == '\0')
+    {
+      errno = ENOENT;
+      return -1;
+    }
+
+  /* Otherwise, try to create a new document.  First, build strings
+     for the name, ID and document URI.  */
+
+  name = (*android_java_env)->NewStringUTF (android_java_env,
+                                           end + 1);
+  android_exception_check ();
+  id = (*android_java_env)->NewStringUTF (android_java_env,
+                                         vp->document_id);
+  android_exception_check_1 (name);
+  uri = (*android_java_env)->NewStringUTF (android_java_env,
+                                          vp->tree_uri);
+  android_exception_check_2 (name, id);
+
+  /* Next, try to create a new document and retrieve its ID.  */
+
+  method = service_class.create_document;
+  new_id = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env,
+                                                           emacs_service,
+                                                           service_class.class,
+                                                           method, uri, id,
+                                                           name);
+
+  if (android_saf_exception_check (3, name, id, uri))
+    return -1;
+
+  /* Delete unused local references.  */
+  ANDROID_DELETE_LOCAL_REF (name);
+  ANDROID_DELETE_LOCAL_REF (id);
+  ANDROID_DELETE_LOCAL_REF (uri);
+
+  if (!new_id)
+    {
+      /* The file couldn't be created for some reason.  */
+      errno = EIO;
+      return -1;
+    }
+
+  /* Now, free VP->document_id and replace it with the service
+     document ID.  */
+
+  new_doc_id = (*android_java_env)->GetStringUTFChars (android_java_env,
+                                                      new_id, NULL);
+  android_exception_check_nonnull ((void *) new_doc_id, new_id);
+
+  xfree (vp->document_id);
+  vp->document_id = xstrdup (new_doc_id);
+
+  (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+                                             new_id, new_doc_id);
+  ANDROID_DELETE_LOCAL_REF (new_id);
+
+  /* Finally, transform this vnode into a file vnode and call its
+     `open' function.  */
+  vp->vnode.type = ANDROID_VNODE_SAF_FILE;
+  vp->vnode.ops = &saf_file_vfs_ops;
+  return (*vp->vnode.ops->open) (vnode, flags, mode, asset_p,
+                                fd_return, asset);
+}
+
+static int
+android_saf_new_unlink (struct android_vnode *vnode)
+{
+  errno = ENOENT;
+  return -1;
+}
+
+static int
+android_saf_new_symlink (const char *target, struct android_vnode *vnode)
+{
+  errno = EPERM;
+  return -1;
+}
+
+static int
+android_saf_new_rmdir (struct android_vnode *vnode)
+{
+  errno = ENOENT;
+  return -1;
+}
+
+static int
+android_saf_new_rename (struct android_vnode *src,
+                       struct android_vnode *dst,
+                       bool keep_existing)
+{
+  errno = ENOENT;
+  return -1;
+}
+
+static int
+android_saf_new_stat (struct android_vnode *vnode,
+                     struct stat *statb)
+{
+  errno = ENOENT;
+  return -1;
+}
+
+static int
+android_saf_new_access (struct android_vnode *vnode, int mode)
+{
+  if (mode != F_OK && !(mode & (W_OK | X_OK | R_OK)))
+    errno = EINVAL;
+  else
+    errno = ENOENT;
+
+  return -1;
+}
+
+static int
+android_saf_new_mkdir (struct android_vnode *vnode, mode_t mode)
+{
+  struct android_saf_new_vnode *vp;
+  jstring name, id, uri, new_id;
+  jmethodID method;
+  const char *new_doc_id;
+  char *end;
+
+  vp = (struct android_saf_tree_vnode *) vnode;
+
+  /* Find the last component of vp->name.  */
+  end = strrchr (vp->name, '/');
+
+  /* VP->name must contain at least one directory separator.  */
+  eassert (end);
+
+  if (end[1] == '\0')
+    {
+      /* There's a trailing directory separator.  Search
+        backwards.  */
+
+      end--;
+      while (end != vp->name && *end != '/')
+       end--;
+
+      /* vp->name[0] is always a directory separator.  */
+      eassert (*end == '/');
+    }
+
+  /* Otherwise, try to create a new document.  First, build strings
+     for the name, ID and document URI.  */
+
+  name = (*android_java_env)->NewStringUTF (android_java_env,
+                                           end + 1);
+  android_exception_check ();
+  id = (*android_java_env)->NewStringUTF (android_java_env,
+                                         vp->document_id);
+  android_exception_check_1 (name);
+  uri = (*android_java_env)->NewStringUTF (android_java_env,
+                                          vp->tree_uri);
+  android_exception_check_2 (name, id);
+
+  /* Next, try to create a new document and retrieve its ID.  */
+
+  method = service_class.create_directory;
+  new_id = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env,
+                                                           emacs_service,
+                                                           service_class.class,
+                                                           method, uri, id,
+                                                           name);
+
+  if (android_saf_exception_check (3, name, id, uri))
+    return -1;
+
+  /* Delete unused local references.  */
+  ANDROID_DELETE_LOCAL_REF (name);
+  ANDROID_DELETE_LOCAL_REF (id);
+  ANDROID_DELETE_LOCAL_REF (uri);
+
+  if (!new_id)
+    {
+      /* The file couldn't be created for some reason.  */
+      errno = EIO;
+      return -1;
+    }
+
+  /* Now, free VP->document_id and replace it with the service
+     document ID.  */
+
+  new_doc_id = (*android_java_env)->GetStringUTFChars (android_java_env,
+                                                      new_id, NULL);
+
+  if (android_saf_exception_check (3, name, id, uri))
+    return -1;
+
+  xfree (vp->document_id);
+  vp->document_id = xstrdup (new_doc_id);
+
+  (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+                                             new_id, new_doc_id);
+  ANDROID_DELETE_LOCAL_REF (new_id);
+
+  /* Finally, transform this vnode into a directory vnode.  */
+  vp->vnode.type = ANDROID_VNODE_SAF_TREE;
+  vp->vnode.ops = &saf_tree_vfs_ops;
+  return 0;
+}
+
+static int
+android_saf_new_chmod (struct android_vnode *vnode, mode_t mode,
+                      int flags)
+{
+  errno = ENOENT;
+  return -1;
+}
+
+static struct android_vdir *
+android_saf_new_opendir (struct android_vnode *vnode)
+{
+  errno = ENOENT;
+  return NULL;
+}
+
+
+
+/* Synchronization between SAF and Emacs.  Consult EmacsSafThread.java
+   for more details.  */
+
+/* Semaphore posted upon the completion of an SAF operation.  */
+static sem_t saf_completion_sem;
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#else /* GNUC */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+#endif /* __clang__ */
+
+JNIEXPORT jint JNICALL
+NATIVE_NAME (safSyncAndReadInput) (JNIEnv *env, jobject object)
+{
+  while (sem_wait (&saf_completion_sem) < 0)
+    {
+      if (input_blocked_p ())
+       continue;
+
+      process_pending_signals ();
+
+      if (!NILP (Vquit_flag))
+       {
+         __android_log_print (ANDROID_LOG_VERBOSE, __func__,
+                              "quitting from IO operation");
+         return 1;
+       }
+    }
+
+  return 0;
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (safSync) (JNIEnv *env, jobject object)
+{
+  while (sem_wait (&saf_completion_sem) < 0)
+    process_pending_signals ();
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (safPostRequest) (JNIEnv *env, jobject object)
+{
+  sem_post (&saf_completion_sem);
+}
+
+JNIEXPORT jboolean JNICALL
+NATIVE_NAME (ftruncate) (JNIEnv *env, jobject object, jint fd)
+{
+  return ftruncate (fd, 0) != -1;
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#else /* GNUC */
+#pragma GCC diagnostic pop
+#endif /* __clang__ */
+
+
+
+/* Root vnode.  This vnode represents the root inode, and is a regular
+   Unix vnode with modifications to `name' that make it return asset
+   vnodes.  */
+
+static struct android_vnode *android_root_name (struct android_vnode *,
+                                               char *, size_t);
+
+/* Vector of VFS operations associated with Unix root filesystem VFS
+   nodes.  */
+
+static struct android_vops root_vfs_ops =
+  {
+    android_root_name,
+    android_unix_open,
+    android_unix_close,
+    android_unix_unlink,
+    android_unix_symlink,
+    android_unix_rmdir,
+    android_unix_rename,
+    android_unix_stat,
+    android_unix_access,
+    android_unix_mkdir,
+    android_unix_chmod,
+    android_unix_opendir,
+  };
+
+/* Array of special named vnodes.  */
+
+static struct android_special_vnode special_vnodes[] =
+  {
+    { "assets",  6, android_afs_initial,       },
+    { "content", 7, android_content_initial,   },
+  };
+
+static struct android_vnode *
+android_root_name (struct android_vnode *vnode, char *name,
+                  size_t length)
+{
+  char *component_end;
+  struct android_special_vnode *special;
+  size_t i;
+
+  /* Skip any leading separator in NAME.  */
+
+  if (*name == '/')
+    name++, length--;
+
+  /* Look for the first directory separator.  */
+  component_end = strchr (name, '/');
+
+  /* If not there, use name + length.  */
+
+  if (!component_end)
+    component_end = name + length;
+  else
+    /* Move past the spearator character.  */
+    component_end++;
+
+  /* Now, find out if the first component is a special vnode; if so,
+     call its root lookup function with the rest of NAME there.  */
+
+  for (i = 0; i < ARRAYELTS (special_vnodes); ++i)
+    {
+      special = &special_vnodes[i];
+
+      if (component_end - name == special->length
+         && !memcmp (special->name, name, special->length))
+       return (*special->initial) (component_end,
+                                   length - special->length);
+
+      /* Detect the case where a special is named with a trailing
+        directory separator.  */
+
+      if (component_end - name == special->length + 1
+         && !memcmp (special->name, name, special->length)
+         && name[special->length] == '/')
+       /* Make sure to include the directory separator.  */
+       return (*special->initial) (component_end - 1,
+                                   length - special->length);
+    }
+
+  /* Otherwise, continue searching for a vnode normally.  */
+  return android_unix_name (vnode, name, length);
+}
+
+
+
+/* File system lookup.  */
+
+/* Look up the vnode that designates NAME, a file name that is at
+   least N bytes.
+
+   NAME may be either an absolute file name or a name relative to the
+   current working directory.  It must not be longer than PATH_MAX
+   bytes.
+
+   Value is NULL upon failure with errno set accordingly, or the
+   vnode.  */
+
+static struct android_vnode *
+android_name_file (const char *name)
+{
+  char buffer[PATH_MAX + 1], *head;
+  const char *end;
+  size_t len;
+  int nslash, c;
+  struct android_vnode *vp;
+
+  len = strlen (name);
+  if (len > PATH_MAX)
+    {
+      errno = ENAMETOOLONG;
+      return NULL;
+    }
+
+  /* Now, try to ``normalize'' the file name by removing consecutive
+     slash characters while copying it to BUFFER.  */
+
+  head = buffer;
+  nslash = 0;
+  for (end = name + len; name < end; ++name)
+    {
+      c = *name;
+
+      switch (c)
+       {
+       case '/':
+         /* This is a directory separator character.  Two consecutive
+            separator characters should be replaced by a single
+            character; more than three in a row means that the
+            section of the file name before the last slash character
+            should be discarded.  */
+
+         if (!nslash)
+           *head++ = '/';
+
+         nslash++;
+
+         if (nslash >= 3)
+           /* Return to the root directory.  */
+           head = buffer, *head++ = '/', nslash = 0;
+         break;
+
+       default:
+         /* Otherwise, copy the file name over.  */
+         nslash = 0;
+         *head++ = *name;
+         break;
+       }
+    }
+
+  /* Terminate the file name.  */
+  *head = '\0';
+
+  /* If HEAD is a relative file name, it can't reside inside the
+     virtual mounts; create a Unix vnode instead.  */
+
+  if (head == buffer || buffer[0] != '/')
+    return android_unix_vnode (buffer);
+
+  /* Start looking from the root vnode.  */
+  vp = &root_vnode.vnode;
+
+  /* If buffer is empty, this will create a duplicate of the root
+     vnode.  */
+  return (*vp->ops->name) (vp, buffer + 1, head - buffer - 1);
+}
+
+
+
+/* Initialize the virtual filesystem layer.  Load the directory tree
+   from the given asset MANAGER (which should be a local reference
+   within ENV) that will be used to access assets in the future, and
+   create the root vnode.
+
+   ENV should be a JNI environment valid for future calls to VFS
+   functions.  */
+
+void
+android_vfs_init (JNIEnv *env, jobject manager)
+{
+  jclass old;
+
+  android_init_assets (env, manager);
+
+  /* Create the root vnode, which is used to locate all other
+     vnodes.  */
+  root_vnode.vnode.ops = &root_vfs_ops;
+  root_vnode.vnode.type = ANDROID_VNODE_UNIX;
+  root_vnode.vnode.flags = 0;
+  root_vnode.name_length = 1;
+  root_vnode.name = (char *) "/";
+
+  /* Initialize some required classes.  */
+  java_string_class = (*env)->FindClass (env, "java/lang/String");
+  assert (java_string_class);
+
+  old = java_string_class;
+  java_string_class = (jclass) (*env)->NewGlobalRef (env,
+                                                    java_string_class);
+  assert (java_string_class);
+  (*env)->DeleteLocalRef (env, old);
+
+  /* And initialize those used on Android 5.0 and later.  */
+
+  if (android_get_current_api_level () < 21)
+    return;
+
+  android_init_cursor_class (env);
+  android_init_entry_class (env);
+  android_init_fd_class (env);
+
+  /* Initialize each of the exception classes used by
+     `android_saf_exception_check'.  */
+
+  old = (*env)->FindClass (env, "java/io/FileNotFoundException");
+  file_not_found_exception = (*env)->NewGlobalRef (env, old);
+  (*env)->DeleteLocalRef (env, old);
+  eassert (file_not_found_exception);
+
+  old = (*env)->FindClass (env, "java/lang/SecurityException");
+  security_exception = (*env)->NewGlobalRef (env, old);
+  (*env)->DeleteLocalRef (env, old);
+  eassert (security_exception);
+
+  old = (*env)->FindClass (env, "android/os/OperationCanceledException");
+  operation_canceled_exception = (*env)->NewGlobalRef (env, old);
+  (*env)->DeleteLocalRef (env, old);
+  eassert (operation_canceled_exception);
+
+  old = (*env)->FindClass (env, "java/lang/UnsupportedOperationException");
+  unsupported_operation_exception = (*env)->NewGlobalRef (env, old);
+  (*env)->DeleteLocalRef (env, old);
+  eassert (unsupported_operation_exception);
+
+  old = (*env)->FindClass (env, "java/lang/OutOfMemoryError");
+  out_of_memory_error = (*env)->NewGlobalRef (env, old);
+  (*env)->DeleteLocalRef (env, old);
+  eassert (out_of_memory_error);
+
+  /* Initialize the semaphore used to wait for SAF operations to
+     complete.  */
+
+  if (sem_init (&saf_completion_sem, 0, 0) < 0)
+    emacs_abort ();
+}
+
+/* The replacement functions that follow have several major
+   drawbacks:
+
+   The first is that CWD relative file names will always be Unix
+   vnodes, and looking up their parents will always return another
+   Unix vnode.  For example, with the working directory set to
+   /sdcard:
+
+     ../content/storage
+
+   will find /sdcard/../content/storage on the Unix filesystem,
+   opposed to /content/storage within the ``content'' VFS.
+
+   Emacs only uses file names expanded through `expand-file-name', so
+   this is unproblematic in practice.
+
+   The second is that `..' components do not usually check that their
+   preceding component is a directory.  This is a side effect of their
+   removal from file names as part of a pre-processing step before
+   they are opened.  So, even if:
+
+     /sdcard/foo.txt
+
+   is a file, opening the directory:
+
+     /sdcard/foo.txt/..
+
+   will be successful.
+
+   The third is that the handling of `..' components relative to
+   another vnode hasn't been tested and is only assumed to work
+   because the code has been written.  It does not pose a practical
+   problem, however, as Emacs only names files starting from the root
+   vnode.
+
+   The fourth is that errno values from vnode operations don't always
+   reflect what the Unix system calls they emulate can return: for
+   example, `open' may return EIO, while trying to `mkdir' within
+   /content will return ENOENT instead of EROFS.  This is a
+   consequence of how accessing a non-existent file may fail at vnode
+   lookup, instead of when a vop is used.  This problem hasn't made a
+   sufficient nuisance of itself to justify its fix yet.
+
+   The fifth is that trailing directory separators may be lost when
+   naming files relative to another vnode, as a consequence of an
+   optimization used to avoid allocating too much stack or heap
+   space.
+
+   The sixth is that flags and other argument checking is nowhere near
+   exhaustive on vnode types other than Unix vnodes.
+
+   The seventh is that certain vnode types may read async input and
+   return EINTR not upon the arrival of a signal itself, but instead
+   if subsequently read input causes Vquit_flag to be set.  These
+   vnodes may not be reentrant, but operating on them from within an
+   async input handler will at worst cause an error to be returned.
+
+   And the final drawback is that directories cannot be directly
+   opened.  Instead, `dirfd' must be called on a directory stream used
+   by `openat'.
+
+   Caveat emptor! */
+
+/* Open the VFS node designated by NAME, taking into account FLAGS and
+   MODE, both of which mean the same as they do in a call to `open'.
+
+   Value is -1 upon failure with errno set accordingly, and a file
+   descriptor otherwise.  */
+
+int
+android_open (const char *name, int flags, mode_t mode)
+{
+  struct android_vnode *vp;
+  int fd, rc;
+
+  vp = android_name_file (name);
+  if (!vp)
+    return -1;
+
+  rc = (*vp->ops->open) (vp, flags, mode, false, &fd, NULL);
+  (*vp->ops->close) (vp);
+
+  if (rc < 0)
+    return -1;
+
+  /* If rc is 1, then an asset file descriptor has been returned.
+     This is impossible, so assert that it doesn't transpire.  */
+  assert (rc != 1);
+  return fd;
+}
+
+/* Unlink the VFS node designated by the specified FILE.
+   Value is -1 upon failure with errno set, and 0 otherwise.  */
+
+int
+android_unlink (const char *name)
+{
+  struct android_vnode *vp;
+  int rc;
+
+  vp = android_name_file (name);
+  if (!vp)
+    return -1;
+
+  rc = (*vp->ops->unlink) (vp);
+  (*vp->ops->close) (vp);
+  return rc;
+}
+
+/* Symlink the VFS node designated by LINKPATH to TARGET.
+   Value is -1 upon failure with errno set, and 0 otherwise.  */
+
+int
+android_symlink (const char *target, const char *linkpath)
+{
+  struct android_vnode *vp;
+  int rc;
+
+  vp = android_name_file (linkpath);
+  if (!vp)
+    return -1;
+
+  rc = (*vp->ops->symlink) (target, vp);
+  (*vp->ops->close) (vp);
+  return rc;
+}
+
+/* Remove the empty directory at the VFS node designated by NAME.
+   Value is -1 upon failure with errno set, and 0 otherwise.  */
+
+int
+android_rmdir (const char *name)
+{
+  struct android_vnode *vp;
+  int rc;
+
+  vp = android_name_file (name);
+  if (!vp)
+    return -1;
+
+  rc = (*vp->ops->rmdir) (vp);
+  (*vp->ops->close) (vp);
+  return rc;
+}
+
+/* Create a directory at the VFS node designated by NAME and the given
+   access MODE.  Value is -1 upon failure with errno set, 0
+   otherwise.  */
+
+int
+android_mkdir (const char *name, mode_t mode)
+{
+  struct android_vnode *vp;
+  int rc;
+
+  vp = android_name_file (name);
+  if (!vp)
+    return -1;
+
+  rc = (*vp->ops->mkdir) (vp, mode);
+  (*vp->ops->close) (vp);
+  return rc;
+}
+
+/* Rename the vnode designated by SRC to the vnode designated by DST.
+   If DST already exists, return -1 and set errno to EEXIST.
+
+   SRCFD and DSTFD should be AT_FDCWD, or else value is -1 and errno
+   is ENOSYS.
+
+   If the filesystem or vnodes containing either DST or SRC does not
+   support rename operations that also check for a preexisting
+   destination, return -1 and set errno to ENOSYS.
+
+   Otherwise, value and errno are identical to that of Unix
+   `rename' with the same arguments.  */
+
+int
+android_renameat_noreplace (int srcfd, const char *src,
+                           int dstfd, const char *dst)
+{
+  struct android_vnode *vp, *vdst;
+  int rc;
+
+  if (srcfd != AT_FDCWD || dstfd != AT_FDCWD)
+    {
+      errno = ENOSYS;
+      return -1;
+    }
+
+  /* Find vnodes for both src and dst.  */
+
+  vp = android_name_file (src);
+  if (!vp)
+    goto error;
+
+  vdst = android_name_file (dst);
+  if (!vdst)
+    goto error1;
+
+  /* Now try to rename vp to vdst.  */
+  rc = (*vp->ops->rename) (vp, vdst, true);
+  (*vp->ops->close) (vp);
+  (*vdst->ops->close) (vdst);
+  return rc;
+
+ error1:
+  (*vp->ops->close) (vp);
+ error:
+  return -1;
+}
+
+/* Like `android_renameat_noreplace', but don't check for DST's
+   existence and don't accept placeholder SRCFD and DSTFD
+   arguments.  */
+
+int
+android_rename (const char *src, const char *dst)
+{
+  struct android_vnode *vp, *vdst;
+  int rc;
+
+  /* Find vnodes for both src and dst.  */
+
+  vp = android_name_file (src);
+  if (!vp)
+    goto error;
+
+  vdst = android_name_file (dst);
+  if (!vdst)
+    goto error1;
+
+  /* Now try to rename vp to vdst.  */
+  rc = (*vp->ops->rename) (vp, vdst, false);
+  (*vp->ops->close) (vp);
+  (*vdst->ops->close) (vdst);
+  return rc;
+
+ error1:
+  (*vp->ops->close) (vp);
+ error:
+  return -1;
+}
+
+
+
+/* fstat, fstatat, faccessat, close/fclose etc.  These functions are
+   somewhat tricky to wrap: they (at least partially) operate on file
+   descriptors, which sometimes provide a base directory for the
+   filesystem operations they perform.  VFS nodes aren't mapped to
+   file descriptors opened through them, which makes this troublesome.
+
+   openat is not wrapped at all; uses are defined out when Emacs is
+   being built for Android.  The other functions fall back to directly
+   making Unix system calls when their base directory arguments are
+   not AT_FDCWD and no directory stream returned from
+   `android_opendir' ever returned that file descriptor, which is
+   enough to satisfy Emacs's current requirements for those functions
+   when a directory file descriptor is supplied.
+
+   fclose and close are finally wrapped because they need to erase
+   information used to link file descriptors with file statistics from
+   their origins; fstat is also wrapped to take this information into
+   account, so that it can return correct file statistics for asset
+   directory files.  */
+
+/* Like fstat.  However, look up the asset corresponding to the file
+   descriptor.  If it exists, return the right information.  */
+
+int
+android_fstat (int fd, struct stat *statb)
+{
+  struct android_afs_open_fd *tem;
+  struct android_parcel_fd *parcel_fd;
+  int rc;
+
+  for (tem = afs_file_descriptors; tem; tem = tem->next)
+    {
+      if (tem->fd == fd)
+       {
+         memcpy (statb, &tem->statb, sizeof *statb);
+         return 0;
+       }
+    }
+
+  rc = fstat (fd, statb);
+
+  /* Now look for a matching parcel file descriptor and use its
+     mtime if available.  */
+
+  parcel_fd = open_parcel_fds;
+  for (; parcel_fd; parcel_fd = parcel_fd->next)
+    {
+      if (parcel_fd->fd == fd
+         && timespec_valid_p (parcel_fd->mtime))
+       {
+#ifdef STAT_TIMESPEC
+         STAT_TIMESPEC (statb, st_mtim) = parcel_fd->mtime;
+#else /* !STAT_TIMESPEC */
+         statb->st_mtime = parcel_fd->mtime.tv_sec;
+         statb->st_mtime_nsec = parcel_fd->mtime.tv_nsec;
+#endif /* STAT_TIMESPEC */
+         break;
+       }
+    }
+
+  return rc;
+}
+
+/* If DIRFD is a file descriptor returned by `android_readdir' for a
+   non-Unix file stream, return FILENAME relative to the file name of
+   the directory represented by that stream within BUFFER, a buffer
+   SIZE bytes long.
+
+   Value is 0 if a file name is returned, 1 otherwise.  */
+
+static int
+android_fstatat_1 (int dirfd, const char *filename,
+                  char *restrict buffer, size_t size)
+{
+  char *dir_name;
+  struct android_saf_root_vdir *vdir;
+  struct android_saf_tree_vdir *vdir1;
+
+  /* Now establish whether DIRFD is a file descriptor corresponding to
+     an open asset directory stream.  */
+
+  dir_name = android_afs_get_directory_name (dirfd);
+
+  if (dir_name)
+    {
+      /* Look for PATHNAME relative to this directory within an asset
+        vnode.  */
+      snprintf (buffer, size, "/assets%s%s", dir_name,
+               filename);
+      return 0;
+    }
+
+  /* Do the same, but for /content directories instead.  */
+
+  dir_name = android_content_get_directory_name (dirfd);
+
+  if (dir_name)
+    {
+      /* Look for PATHNAME relative to this directory within an asset
+        vnode.  */
+      snprintf (buffer, size, "%s/%s", dir_name,
+               filename);
+      return 0;
+    }
+
+  /* And for /content/storage.  */
+
+  vdir = android_saf_root_get_directory (dirfd);
+
+  if (vdir)
+    {
+      if (vdir->authority)
+       snprintf (buffer, size, "/content/storage/%s/%s",
+                 vdir->authority, filename);
+      else
+       snprintf (buffer, size, "/content/storage/%s",
+                 filename);
+
+      return 0;
+    }
+
+  /* /content/storage/foo/... */
+
+  vdir1 = android_saf_tree_get_directory (dirfd);
+
+  if (vdir1)
+    {
+      snprintf (buffer, size, "%s%s", vdir1->name, filename);
+      return 0;
+    }
+
+  return 1;
+}
+
+/* If DIRFD is AT_FDCWD or a file descriptor returned by
+   `android_dirfd', or PATHNAME is an absolute file name, return the
+   file status of the VFS node designated by PATHNAME relative to the
+   VFS node corresponding to DIRFD, or relative to the current working
+   directory if DIRFD is AT_FDCWD.
+
+   Otherwise, call `fstatat' with DIRFD, PATHNAME, STATBUF and
+   FLAGS.  */
+
+int
+android_fstatat (int dirfd, const char *restrict pathname,
+                struct stat *restrict statbuf, int flags)
+{
+  char buffer[PATH_MAX + 1];
+  struct android_vnode *vp;
+  int rc;
+
+  /* Emacs uses AT_SYMLINK_NOFOLLOW, but fortunately (?) DIRFD is
+     never known to Emacs or AT_FDCWD when it originates from a VFS
+     node representing a filesystem that supports symlinks.  */
+
+  if (dirfd == AT_FDCWD || pathname[0] == '/')
+    goto vfs;
+
+  /* Now establish whether DIRFD is a file descriptor corresponding to
+     an open VFS directory stream.  */
+
+  if (!android_fstatat_1 (dirfd, pathname, buffer, PATH_MAX + 1))
+    {
+      pathname = buffer;
+      goto vfs;
+    }
+
+  /* Fall back to fstatat.  */
+  return fstatat (dirfd, pathname, statbuf, flags);
+
+ vfs:
+  vp = android_name_file (pathname);
+  if (!vp)
+    return -1;
+
+  rc = (*vp->ops->stat) (vp, statbuf);
+  (*vp->ops->close) (vp);
+  return rc;
+}
+
+/* Like `android_fstatat', but check file accessibility instead of
+   status.  */
+
+int
+android_faccessat (int dirfd, const char *restrict pathname,
+                  int mode, int flags)
+{
+  char buffer[PATH_MAX + 1];
+  struct android_vnode *vp;
+  int rc;
+
+  /* Emacs uses AT_SYMLINK_NOFOLLOW, but fortunately (?) DIRFD is
+     never known to Emacs or AT_FDCWD when it originates from a VFS
+     node representing a filesystem that supports symlinks.  */
+
+  if (dirfd == AT_FDCWD || pathname[0] == '/')
+    goto vfs;
+
+  /* Now establish whether DIRFD is a file descriptor corresponding to
+     an open VFS directory stream.  */
+
+  if (!android_fstatat_1 (dirfd, pathname, buffer, PATH_MAX + 1))
+    {
+      pathname = buffer;
+      goto vfs;
+    }
+
+  /* Fall back to faccessat.  */
+  return faccessat (dirfd, pathname, mode, flags);
+
+ vfs:
+  vp = android_name_file (pathname);
+  if (!vp)
+    return -1;
+
+  rc = (*vp->ops->access) (vp, mode);
+  (*vp->ops->close) (vp);
+  return rc;
+}
+
+/* Like `android_fstatat', but set file modes instead of
+   checking file status and respect FLAGS.  */
+
+int
+android_fchmodat (int dirfd, const char *pathname, mode_t mode,
+                 int flags)
+{
+  char buffer[PATH_MAX + 1];
+  struct android_vnode *vp;
+  int rc;
+
+  if (dirfd == AT_FDCWD || pathname[0] == '/')
+    goto vfs;
+
+  /* Now establish whether DIRFD is a file descriptor corresponding to
+     an open VFS directory stream.  */
+
+  if (!android_fstatat_1 (dirfd, pathname, buffer, PATH_MAX + 1))
+    {
+      pathname = buffer;
+      goto vfs;
+    }
+
+  /* Fall back to fchmodat.  */
+  return fchmodat (dirfd, pathname, mode, flags);
+
+ vfs:
+  vp = android_name_file (pathname);
+  if (!vp)
+    return -1;
+
+  rc = (*vp->ops->chmod) (vp, mode, flags);
+  (*vp->ops->close) (vp);
+  return rc;
+}
+
+/* Like `fdopen', but if FD is a parcel file descriptor, ``detach'' it
+   from the original.
+
+   This is necessary because ownership over parcel file descriptors is
+   retained by the ParcelFileDescriptor objects that return them,
+   while file streams also require ownership over file descriptors
+   they are created on behalf of.
+
+   Detaching the parcel file descriptor linked to FD consequentially
+   prevents the owner from being notified when it is eventually
+   closed, but for now that hasn't been demonstrated to be problematic
+   yet, as Emacs doesn't write to file streams.  */
+
+FILE *
+android_fdopen (int fd, const char *mode)
+{
+  struct android_parcel_fd *tem, **next, *temp;
+  int new_fd;
+
+  for (next = &open_parcel_fds; (tem = *next);)
+    {
+      if (tem->fd == fd)
+       {
+         new_fd
+           = (*android_java_env)->CallIntMethod (android_java_env,
+                                                 tem->descriptor,
+                                                 fd_class.detach_fd);
+         temp = tem->next;
+         xfree (tem);
+         *next = temp;
+         android_exception_check ();
+
+         /* Assert that FD (returned from `getFd') is identical to
+            the file descriptor returned by `detachFd'.  */
+
+         if (fd != new_fd)
+           emacs_abort ();
+
+         break;
+       }
+      else
+       next = &(*next)->next;
+    }
+
+  return fdopen (fd, mode);
+}
+
+/* Like close.  However, remove the file descriptor from the asset
+   table as well.  */
+
+int
+android_close (int fd)
+{
+  struct android_afs_open_fd *tem, **next, *temp;
+
+  if (android_close_parcel_fd (fd))
+    return 0;
+
+  for (next = &afs_file_descriptors; (tem = *next);)
+    {
+      if (tem->fd == fd)
+       {
+         temp = tem->next;
+         xfree (tem);
+         *next = temp;
+
+         break;
+       }
+      else
+       next = &(*next)->next;
+    }
+
+  return close (fd);
+}
+
+/* Like fclose.  However, remove any information associated with
+   FILE's file descriptor from the asset table as well.  */
+
+int
+android_fclose (FILE *stream)
+{
+  int fd;
+  struct android_afs_open_fd *tem, **next, *temp;
+
+  fd = fileno (stream);
+
+  if (fd == -1)
+    goto skip;
+
+  for (next = &afs_file_descriptors; (tem = *next);)
+    {
+      if (tem->fd == fd)
+       {
+         temp = tem->next;
+         xfree (*next);
+         *next = temp;
+
+         break;
+       }
+      else
+       next = &(*next)->next;
+    }
+
+ skip:
+  return fclose (stream);
+}
+
+
+
+/* External asset management interface.  By using functions here
+   to read and write from files, Emacs can avoid opening a
+   shared memory file descriptor for each ``asset'' file.  */
+
+/* Like android_open.  However, return a structure that can
+   either directly hold an AAsset or a file descriptor.
+
+   Value is the structure upon success.  Upon failure, value
+   consists of an uninitialized file descriptor, but its asset
+   field is set to -1, and errno is set accordingly.  */
+
+struct android_fd_or_asset
+android_open_asset (const char *filename, int oflag, mode_t mode)
+{
+  struct android_fd_or_asset fd;
+  AAsset *asset;
+  int rc;
+  struct android_vnode *vp;
+
+  /* Now name this file.  */
+  vp = android_name_file (filename);
+  if (!vp)
+    goto failure;
+
+  rc = (*vp->ops->open) (vp, oflag, mode, true, &fd.fd,
+                        &asset);
+  (*vp->ops->close) (vp);
+
+  /* Upon failure, return fd with its asset field set to (void *)
+     -1.  */
+
+  if (rc < 0)
+    {
+    failure:
+      fd.asset = (void *) -1;
+      fd.fd = -1;
+      return fd;
+    }
+
+  if (rc == 1)
+    {
+      /* An asset file was returned.  Return the structure containing
+        an asset.  */
+      fd.asset = asset;
+      fd.fd = -1;
+      return fd;
+    }
+
+  /* Otherwise, a file descriptor has been returned.  Set fd.asset to
+     NULL, signifying that it is a file descriptor.  */
+  fd.asset = NULL;
+  return fd;
+}
+
+/* Like android_close.  However, it takes a ``file descriptor''
+   opened using android_open_asset.  */
+
+int
+android_close_asset (struct android_fd_or_asset asset)
+{
+  if (!asset.asset)
+    return android_close (asset.fd);
+
+  AAsset_close (asset.asset);
+  return 0;
+}
+
+/* Like `emacs_read_quit'.  However, it handles file descriptors
+   opened using `android_open_asset' as well.  */
+
+ssize_t
+android_asset_read_quit (struct android_fd_or_asset asset,
+                        void *buffer, size_t size)
+{
+  if (!asset.asset)
+    return emacs_read_quit (asset.fd, buffer, size);
+
+  /* It doesn't seem possible to quit from inside AAsset_read,
+     sadly.  */
+  return AAsset_read (asset.asset, buffer, size);
+}
+
+/* Like `read'.  However, it handles file descriptors opened
+   using `android_open_asset' as well.  */
+
+ssize_t
+android_asset_read (struct android_fd_or_asset asset,
+                   void *buffer, size_t size)
+{
+  if (!asset.asset)
+    return read (asset.fd, buffer, size);
+
+  /* It doesn't seem possible to quit from inside AAsset_read,
+     sadly.  */
+  return AAsset_read (asset.asset, buffer, size);
+}
+
+/* Like `lseek', but it handles ``file descriptors'' opened with
+   android_open_asset.  */
+
+off_t
+android_asset_lseek (struct android_fd_or_asset asset, off_t off,
+                    int whence)
+{
+  if (!asset.asset)
+    return lseek (asset.fd, off, whence);
+
+  return AAsset_seek (asset.asset, off, whence);
+}
+
+/* Like `fstat'.  */
+
+int
+android_asset_fstat (struct android_fd_or_asset asset,
+                    struct stat *statb)
+{
+  if (!asset.asset)
+    return android_fstat (asset.fd, statb);
+
+  /* Clear statb.  */
+  memset (statb, 0, sizeof *statb);
+
+  /* Set the mode.  */
+  statb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
+
+  /* Concoct a nonexistent device and an inode number.  */
+  statb->st_dev = -1;
+  statb->st_ino = 0;
+
+  /* Owned by root.  */
+  statb->st_uid = 0;
+  statb->st_gid = 0;
+
+  /* Size of the file.  */
+  statb->st_size = AAsset_getLength (asset.asset);
+  return 0;
+}
+
+
+
+/* Directory listing emulation.  */
+
+/* Open a directory stream from the VFS node designated by NAME.
+   Value is NULL upon failure with errno set accordingly.  `errno' may
+   be set to EINTR.
+
+   The directory stream returned holds local references to JNI objects
+   and shouldn't be used after the current local reference frame is
+   popped.  */
+
+struct android_vdir *
+android_opendir (const char *name)
+{
+  struct android_vnode *vp;
+  struct android_vdir *dir;
+
+  vp = android_name_file (name);
+  if (!vp)
+    return NULL;
+
+  dir = (*vp->ops->opendir) (vp);
+  (*vp->ops->close) (vp);
+  return dir;
+}
+
+/* Like dirfd.  However, value is not a real directory file descriptor
+   if DIR is an asset directory.  */
+
+int
+android_dirfd (struct android_vdir *dirp)
+{
+  return (*dirp->dirfd) (dirp);
+}
+
+/* Like readdir, but for VFS directory streams instead.  */
+
+struct dirent *
+android_readdir (struct android_vdir *dirp)
+{
+  return (*dirp->readdir) (dirp);
+}
+
+/* Like closedir, but for VFS directory streams instead.  */
+
+void
+android_closedir (struct android_vdir *dirp)
+{
+  return (*dirp->closedir) (dirp);
+}
diff --git a/src/buffer.c b/src/buffer.c
index 0c46b201586..9facc4b7ab8 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -4719,6 +4719,7 @@ init_buffer_once (void)
 #ifdef HAVE_TREE_SITTER
   XSETFASTINT (BVAR (&buffer_local_flags, ts_parser_list), idx); ++idx;
 #endif
+  XSETFASTINT (BVAR (&buffer_local_flags, text_conversion_style), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), 
idx); ++idx;
 
   /* buffer_local_flags contains no pointers, so it's safe to treat it
@@ -4790,6 +4791,7 @@ init_buffer_once (void)
 #ifdef HAVE_TREE_SITTER
   bset_ts_parser_list (&buffer_defaults, Qnil);
 #endif
+  bset_text_conversion_style (&buffer_defaults, Qnil);
   bset_cursor_in_non_selected_windows (&buffer_defaults, Qt);
 
   bset_enable_multibyte_characters (&buffer_defaults, Qt);
@@ -5866,6 +5868,26 @@ If t, displays a cursor related to the usual cursor type
 You can also specify the cursor type as in the `cursor-type' variable.
 Use Custom to set this variable and update the display.  */);
 
+  /* While this is defined here, each *term.c module must implement
+     the logic itself.  */
+
+  DEFVAR_PER_BUFFER ("text-conversion-style", &BVAR (current_buffer,
+                                                    text_conversion_style),
+                    Qnil,
+    doc: /* How the on screen keyboard's input method should insert in this 
buffer.
+When nil, the input method will be disabled and an ordinary keyboard
+will be displayed in its place.
+When the symbol `action', the input method will insert text directly, but
+will send `return' key events instead of inserting new line characters.
+Any other value means that the input method will insert text directly.
+
+If you need to make non-buffer local changes to this variable, use
+`overriding-text-conversion-style', which see.
+
+This variable does not take immediate effect when set; rather, it
+takes effect upon the next redisplay after the selected window or
+buffer changes.  */);
+
   DEFVAR_LISP ("kill-buffer-query-functions", Vkill_buffer_query_functions,
               doc: /* List of functions called with no args to query before 
killing a buffer.
 The buffer being killed will be current while the functions are running.
diff --git a/src/buffer.h b/src/buffer.h
index e700297a264..e71ffe28045 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -566,6 +566,11 @@ struct buffer
   /* A list of tree-sitter parsers for this buffer.  */
   Lisp_Object ts_parser_list_;
 #endif
+
+  /* What type of text conversion the input method should apply to
+     this buffer.  */
+  Lisp_Object text_conversion_style_;
+
   /* Cursor type to display in non-selected windows.
      t means to use hollow box cursor.
      See `cursor-type' for other values.  */
@@ -842,6 +847,12 @@ bset_width_table (struct buffer *b, Lisp_Object val)
   b->width_table_ = val;
 }
 
+INLINE void
+bset_text_conversion_style (struct buffer *b, Lisp_Object val)
+{
+  b->text_conversion_style_ = val;
+}
+
 /* BUFFER_CEILING_OF (resp. BUFFER_FLOOR_OF), when applied to n, return
    the max (resp. min) p such that
 
diff --git a/src/callint.c b/src/callint.c
index d8d2b278458..00e9a080654 100644
--- a/src/callint.c
+++ b/src/callint.c
@@ -537,7 +537,8 @@ invoke it (via an `interactive' spec that contains, for 
instance, an
                                make_fixnum (SCHARS (callint_message)),
                                Qface, Qminibuffer_prompt, callint_message);
            args[i] = Fread_key_sequence (callint_message,
-                                         Qnil, Qnil, Qnil, Qnil);
+                                         Qnil, Qnil, Qnil, Qnil,
+                                         Qnil);
            unbind_to (speccount1, Qnil);
            visargs[i] = Fkey_description (args[i], Qnil);
 
@@ -567,7 +568,8 @@ invoke it (via an `interactive' spec that contains, for 
instance, an
                                make_fixnum (SCHARS (callint_message)),
                                Qface, Qminibuffer_prompt, callint_message);
            args[i] = Fread_key_sequence_vector (callint_message,
-                                                Qnil, Qt, Qnil, Qnil);
+                                                Qnil, Qt, Qnil, Qnil,
+                                                Qnil);
            visargs[i] = Fkey_description (args[i], Qnil);
            unbind_to (speccount1, Qnil);
 
diff --git a/src/callproc.c b/src/callproc.c
index 6f3d4fad9be..0645c2c3e18 100644
--- a/src/callproc.c
+++ b/src/callproc.c
@@ -92,6 +92,10 @@ extern char **environ;
 #include "pgtkterm.h"
 #endif
 
+#ifdef HAVE_ANDROID
+#include "android.h"
+#endif /* HAVE_ANDROID */
+
 /* Pattern used by call-process-region to make temp files.  */
 static Lisp_Object Vtemp_file_name_pattern;
 
@@ -144,7 +148,11 @@ static CHILD_SETUP_TYPE child_setup (int, int, int, char 
**, char **,
    directory if it's unreachable.  If ENCODE is true, return as a string
    suitable for a system call; otherwise, return a string in its
    internal representation.  Signal an error if the result would not be
-   an accessible directory.  */
+   an accessible directory.
+
+   If the default directory lies inside a special directory which
+   cannot be made the current working directory, and ENCODE is also
+   set, simply return the home directory.  */
 
 Lisp_Object
 get_current_directory (bool encode)
@@ -157,6 +165,19 @@ get_current_directory (bool encode)
   if (NILP (dir))
     dir = build_string ("~");
 
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+
+  /* If DIR is an asset directory or a content directory, return
+     the home directory instead.  */
+
+  if (encode && (!strcmp (SSDATA (dir), "/assets")
+                || !strncmp (SSDATA (dir), "/assets/", 8)
+                || !strcmp (SSDATA (dir), "/content")
+                || !strncmp (SSDATA (dir), "/content/", 9)))
+    dir = build_string ("~");
+
+#endif /* HAVE_ANDROID && ANDROID_STUBIFY */
+
   dir = expand_and_dir_to_file (dir);
   Lisp_Object encoded_dir = ENCODE_FILE (remove_slash_colon (dir));
 
@@ -193,7 +214,7 @@ record_kill_process (struct Lisp_Process *p, Lisp_Object 
tempfile)
 static void
 delete_temp_file (Lisp_Object name)
 {
-  unlink (SSDATA (name));
+  emacs_unlink (SSDATA (name));
 }
 
 static void
@@ -499,7 +520,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int 
filefd,
     int ok;
 
     ok = openp (Vexec_path, args[0], Vexec_suffixes, &path,
-               make_fixnum (X_OK), false, false);
+               make_fixnum (X_OK), false, false, NULL);
     if (ok < 0)
       report_file_error ("Searching for program", args[0]);
   }
@@ -1421,6 +1442,18 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int 
std_err,
              const char *pty_name, bool pty_in, bool pty_out,
              const sigset_t *oldset)
 {
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  /* Android 10 and later don't allow directly executing programs
+     installed in the application data directory.  Emacs provides a
+     loader binary which replaces the `execve' system call for it and
+     all its children.  On these systems, rewrite the command line to
+     call that loader binary instead.  */
+
+  if (android_rewrite_spawn_argv ((const char ***) &argv))
+    return 1;
+#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
+
 #if USABLE_POSIX_SPAWN
   /* Prefer the simpler `posix_spawn' if available.  `posix_spawn'
      doesn't yet support setting up pseudoterminals, so we fall back
@@ -1988,7 +2021,12 @@ init_callproc (void)
     dir_warning ("arch-independent data dir", Vdata_directory);
 
   sh = getenv ("SHELL");
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  /* The Android shell is found under /system/bin, not /bin.  */
+  Vshell_file_name = build_string (sh ? sh : "/system/bin/sh");
+#else
   Vshell_file_name = build_string (sh ? sh : "/bin/sh");
+#endif
 
   Lisp_Object gamedir = Qnil;
   if (PATH_GAME)
@@ -2111,6 +2149,72 @@ use.
 See `setenv' and `getenv'.  */);
   Vprocess_environment = Qnil;
 
+  DEFVAR_LISP ("ctags-program-name", Vctags_program_name,
+    doc: /* Name of the `ctags' program distributed with Emacs.
+Use this instead of calling `ctags' directly, as `ctags' may have been
+renamed to comply with executable naming restrictions on the system.  */);
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+  Vctags_program_name = build_pure_c_string ("ctags");
+#else
+  Vctags_program_name = build_pure_c_string ("libctags.so");
+#endif
+
+  DEFVAR_LISP ("etags-program-name", Vetags_program_name,
+    doc: /* Name of the `etags' program distributed with Emacs.
+Use this instead of calling `etags' directly, as `etags' may have been
+renamed to comply with executable naming restrictions on the system.  */);
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+  Vetags_program_name = build_pure_c_string ("etags");
+#else
+  Vetags_program_name = build_pure_c_string ("libetags.so");
+#endif
+
+  DEFVAR_LISP ("hexl-program-name", Vhexl_program_name,
+    doc: /* Name of the `hexl' program distributed with Emacs.
+Use this instead of calling `hexl' directly, as `hexl' may have been
+renamed to comply with executable naming restrictions on the system.  */);
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+  Vhexl_program_name = build_pure_c_string ("hexl");
+#else
+  Vhexl_program_name = build_pure_c_string ("libhexl.so");
+#endif
+
+  DEFVAR_LISP ("emacsclient-program-name", Vemacsclient_program_name,
+    doc: /* Name of the `emacsclient' program distributed with Emacs.
+Use this instead of calling `emacsclient' directly, as `emacsclient'
+may have been renamed to comply with executable naming restrictions on
+the system.  */);
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+  Vemacsclient_program_name = build_pure_c_string ("emacsclient");
+#else
+  Vemacsclient_program_name = build_pure_c_string ("libemacsclient.so");
+#endif
+
+  DEFVAR_LISP ("movemail-program-name", Vmovemail_program_name,
+    doc: /* Name of the `movemail' program distributed with Emacs.
+Use this instead of calling `movemail' directly, as `movemail'
+may have been renamed to comply with executable naming restrictions on
+the system.  */);
+  /* Don't change the name of `movemail' if Emacs is being built to
+     use movemail from another source.  */
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY   \
+  || defined HAVE_MAILUTILS
+  Vmovemail_program_name = build_pure_c_string ("movemail");
+#else
+  Vmovemail_program_name = build_pure_c_string ("libmovemail.so");
+#endif
+
+  DEFVAR_LISP ("ebrowse-program-name", Vebrowse_program_name,
+    doc: /* Name of the `ebrowse' program distributed with Emacs.
+Use this instead of calling `ebrowse' directly, as `ebrowse'
+may have been renamed to comply with executable naming restrictions on
+the system.  */);
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+  Vebrowse_program_name = build_pure_c_string ("ebrowse");
+#else
+  Vebrowse_program_name = build_pure_c_string ("libebrowse.so");
+#endif
+
   defsubr (&Scall_process);
   defsubr (&Sgetenv_internal);
   defsubr (&Scall_process_region);
diff --git a/src/character.c b/src/character.c
index f4164360f21..2118b20a7c7 100644
--- a/src/character.c
+++ b/src/character.c
@@ -1117,6 +1117,14 @@ A char-table for width (columns) of each character.  */);
   char_table_set_range (Vchar_width_table, MAX_5_BYTE_CHAR + 1, MAX_CHAR,
                        make_fixnum (4));
 
+  DEFVAR_LISP ("ambiguous-width-chars", Vambiguous_width_chars,
+              doc: /*
+A char-table for characters whose width (columns) can be 1 or 2.
+
+The actual width depends on the language-environment and on the
+value of `cjk-ambiguous-chars-are-wide'.  */);
+  Vambiguous_width_chars = Fmake_char_table (Qnil, Qnil);
+
   DEFVAR_LISP ("printable-chars", Vprintable_chars,
               doc: /* A char-table for each printable character.  */);
   Vprintable_chars = Fmake_char_table (Qnil, Qnil);
diff --git a/src/charset.c b/src/charset.c
index 7987ffa0c5e..d5e42d038df 100644
--- a/src/charset.c
+++ b/src/charset.c
@@ -486,8 +486,9 @@ load_charset_map_from_file (struct charset *charset, 
Lisp_Object mapfile,
   specpdl_ref count = SPECPDL_INDEX ();
   record_unwind_protect_nothing ();
   specbind (Qfile_name_handler_alist, Qnil);
-  fd = openp (Vcharset_map_path, mapfile, suffixes, NULL, Qnil, false, false);
-  fp = fd < 0 ? 0 : fdopen (fd, "r");
+  fd = openp (Vcharset_map_path, mapfile, suffixes, NULL, Qnil, false, false,
+             NULL);
+  fp = fd < 0 ? 0 : emacs_fdopen (fd, "r");
   if (!fp)
     {
       int open_errno = errno;
@@ -544,7 +545,7 @@ load_charset_map_from_file (struct charset *charset, 
Lisp_Object mapfile,
       entries->entry[idx].c = c;
       n_entries++;
     }
-  fclose (fp);
+  emacs_fclose (fp);
   clear_unwind_protect (count);
 
   load_charset_map (charset, head, n_entries, control_flag);
diff --git a/src/coding.c b/src/coding.c
index 899f8fc7b4d..b7f4120dc8d 100644
--- a/src/coding.c
+++ b/src/coding.c
@@ -8494,7 +8494,7 @@ preferred_coding_system (void)
   return CODING_ID_NAME (id);
 }
 
-#if defined (WINDOWSNT) || defined (CYGWIN)
+#if defined (WINDOWSNT) || defined (CYGWIN) || defined HAVE_ANDROID
 
 Lisp_Object
 from_unicode (Lisp_Object str)
@@ -8512,10 +8512,31 @@ from_unicode (Lisp_Object str)
 Lisp_Object
 from_unicode_buffer (const wchar_t *wstr)
 {
+#if defined WINDOWSNT || defined CYGWIN
   /* We get one of the two final null bytes for free.  */
   ptrdiff_t len = 1 + sizeof (wchar_t) * wcslen (wstr);
   AUTO_STRING_WITH_LEN (str, (char *) wstr, len);
   return from_unicode (str);
+#else
+  /* This code is used only on Android, where little endian UTF-16
+     strings are extended to 32-bit wchar_t.  */
+
+  uint16_t *words;
+  size_t length, i;
+
+  length = wcslen (wstr) + 1;
+
+  USE_SAFE_ALLOCA;
+  SAFE_NALLOCA (words, sizeof *words, length);
+
+  for (i = 0; i < length - 1; ++i)
+    words[i] = wstr[i];
+
+  words[i] = '\0';
+  AUTO_STRING_WITH_LEN (str, (char *) words,
+                       (length - 1) * sizeof *words);
+  return unbind_to (sa_count, from_unicode (str));
+#endif
 }
 
 wchar_t *
@@ -8535,7 +8556,7 @@ to_unicode (Lisp_Object str, Lisp_Object *buf)
   return WCSDATA (*buf);
 }
 
-#endif /* WINDOWSNT || CYGWIN */
+#endif /* WINDOWSNT || CYGWIN || HAVE_ANDROID */
 
 
 /*** 8. Emacs Lisp library functions ***/
@@ -11745,7 +11766,7 @@ syms_of_coding (void)
   DEFSYM (Qutf_8_unix, "utf-8-unix");
   DEFSYM (Qutf_8_emacs, "utf-8-emacs");
 
-#if defined (WINDOWSNT) || defined (CYGWIN)
+#if defined (WINDOWSNT) || defined (CYGWIN) || defined HAVE_ANDROID
   /* No, not utf-16-le: that one has a BOM.  */
   DEFSYM (Qutf_16le, "utf-16le");
 #endif
diff --git a/src/coding.h b/src/coding.h
index b8d4f5f27e1..08c29c884a5 100644
--- a/src/coding.h
+++ b/src/coding.h
@@ -709,7 +709,7 @@ extern void encode_coding_object (struct coding_system *,
 /* Defined in this file.  */
 INLINE int surrogates_to_codepoint (int, int);
 
-#if defined (WINDOWSNT) || defined (CYGWIN)
+#if defined (WINDOWSNT) || defined (CYGWIN) || defined HAVE_ANDROID
 
 /* These functions use Lisp string objects to store the UTF-16LE
    strings that modern versions of Windows expect.  These strings are
@@ -732,7 +732,7 @@ extern Lisp_Object from_unicode (Lisp_Object str);
 /* Convert WSTR to an Emacs string.  */
 extern Lisp_Object from_unicode_buffer (const wchar_t *wstr);
 
-#endif /* WINDOWSNT || CYGWIN */
+#endif /* WINDOWSNT || CYGWIN || HAVE_ANDROID */
 
 /* Macros for backward compatibility.  */
 
diff --git a/src/conf_post.h b/src/conf_post.h
index 0d5f90a6910..f31e012dc6e 100644
--- a/src/conf_post.h
+++ b/src/conf_post.h
@@ -461,3 +461,13 @@ extern int emacs_setenv_TZ (char const *);
 #else
 # define UNINIT /* empty */
 #endif
+
+/* MB_CUR_MAX is often broken on systems which copy-paste LLVM
+   headers, so replace its definition with a working one if
+   necessary.  */
+
+#ifdef REPLACEMENT_MB_CUR_MAX
+#include <stdlib.h>
+#undef MB_CUR_MAX
+#define MB_CUR_MAX REPLACEMENT_MB_CUR_MAX
+#endif /* REPLACEMENT_MB_CUR_MAX */
diff --git a/src/dired.c b/src/dired.c
index 3f55c4c3830..c10531cdb16 100644
--- a/src/dired.c
+++ b/src/dired.c
@@ -44,6 +44,21 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "msdos.h"     /* for fstatat */
 #endif
 
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+typedef DIR emacs_dir;
+#define emacs_readdir readdir
+#define emacs_closedir closedir
+#else
+
+#include "android.h"
+
+/* The Android emulation of dirent stuff is required to be able to
+   list the /assets special directory.  */
+typedef struct android_vdir emacs_dir;
+#define emacs_readdir android_readdir
+#define emacs_closedir android_closedir
+#endif
+
 #ifdef WINDOWSNT
 extern int is_slow_fs (const char *);
 #endif
@@ -78,19 +93,42 @@ dirent_type (struct dirent *dp)
 #endif
 }
 
-static DIR *
+static emacs_dir *
 open_directory (Lisp_Object dirname, Lisp_Object encoded_dirname, int *fdp)
 {
   char *name = SSDATA (encoded_dirname);
-  DIR *d;
+  emacs_dir *d;
   int fd, opendir_errno;
 
-#ifdef DOS_NT
-  /* Directories cannot be opened.  The emulation assumes that any
-     file descriptor other than AT_FDCWD corresponds to the most
-     recently opened directory.  This hack is good enough for Emacs.  */
+#if defined DOS_NT || (defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+  /* On DOS_NT, directories cannot be opened.  The emulation assumes
+     that any file descriptor other than AT_FDCWD corresponds to the
+     most recently opened directory.  This hack is good enough for
+     Emacs.
+
+     This code is also used on Android for a different reason: a
+     special `assets' directory outside the normal file system is used
+     to open assets inside the Android application package, and must
+     be listed using the opendir-like interface provided in
+     android.h.  */
   fd = 0;
+#ifndef HAVE_ANDROID
   d = opendir (name);
+#else
+  /* `android_opendir' can return EINTR if DIRNAME designates a file
+     within a slow-to-respond document provider.  */
+
+ again:
+  d = android_opendir (name);
+
+  if (d)
+    fd = android_dirfd (d);
+  else if (errno == EINTR)
+    {
+      maybe_quit ();
+      goto again;
+    }
+#endif
   opendir_errno = errno;
 #else
   fd = emacs_open (name, O_RDONLY | O_DIRECTORY, 0);
@@ -125,7 +163,7 @@ directory_files_internal_w32_unwind (Lisp_Object arg)
 static void
 directory_files_internal_unwind (void *d)
 {
-  closedir (d);
+  emacs_closedir (d);
 }
 
 /* Return the next directory entry from DIR; DIR's name is DIRNAME.
@@ -133,12 +171,12 @@ directory_files_internal_unwind (void *d)
    Signal any unrecoverable errors.  */
 
 static struct dirent *
-read_dirent (DIR *dir, Lisp_Object dirname)
+read_dirent (emacs_dir *dir, Lisp_Object dirname)
 {
   while (true)
     {
       errno = 0;
-      struct dirent *dp = readdir (dir);
+      struct dirent *dp = emacs_readdir (dir);
       if (dp || errno == 0)
        return dp;
       if (! (errno == EAGAIN || errno == EINTR))
@@ -190,7 +228,10 @@ directory_files_internal (Lisp_Object directory, 
Lisp_Object full,
   Lisp_Object encoded_dirfilename = ENCODE_FILE (dirfilename);
 
   int fd;
-  DIR *d = open_directory (dirfilename, encoded_dirfilename, &fd);
+
+  /* Keep in mind that FD is not always a real file descriptor on
+     Android.  */
+  emacs_dir *d = open_directory (dirfilename, encoded_dirfilename, &fd);
 
   /* Unfortunately, we can now invoke expand-file-name and
      file-attributes on filenames, both of which can throw, so we must
@@ -300,7 +341,7 @@ directory_files_internal (Lisp_Object directory, 
Lisp_Object full,
       list = Fcons (attrs ? Fcons (finalname, fileattrs) : finalname, list);
     }
 
-  closedir (d);
+  emacs_closedir (d);
 #ifdef WINDOWSNT
   if (attrs)
     Vw32_get_true_file_attributes = w32_save;
@@ -514,7 +555,7 @@ file_name_completion (Lisp_Object file, Lisp_Object 
dirname, bool all_flag,
        }
     }
   int fd;
-  DIR *d = open_directory (dirname, encoded_dir, &fd);
+  emacs_dir *d = open_directory (dirname, encoded_dir, &fd);
   record_unwind_protect_ptr (directory_files_internal_unwind, d);
 
   /* Loop reading directory entries.  */
@@ -855,7 +896,9 @@ file_name_completion_dirp (int fd, struct dirent *dp, 
ptrdiff_t len)
   char *subdir_name = SAFE_ALLOCA (len + 2);
   memcpy (subdir_name, dp->d_name, len);
   strcpy (subdir_name + len, "/");
-  bool dirp = faccessat (fd, subdir_name, F_OK, AT_EACCESS) == 0;
+
+  bool dirp = sys_faccessat (fd, subdir_name,
+                            F_OK, AT_EACCESS) == 0;
   SAFE_FREE ();
   return dirp;
 }
@@ -979,14 +1022,15 @@ file_attributes (int fd, char const *name,
 
   int err = EINVAL;
 
-#if defined O_PATH && !defined HAVE_CYGWIN_O_PATH_BUG
+#if defined O_PATH && !defined HAVE_CYGWIN_O_PATH_BUG  \
+  && !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
   int namefd = emacs_openat (fd, name, O_PATH | O_CLOEXEC | O_NOFOLLOW, 0);
   if (namefd < 0)
     err = errno;
   else
     {
       record_unwind_protect_int (close_file_unwind, namefd);
-      if (fstat (namefd, &s) != 0)
+      if (sys_fstat (namefd, &s) != 0)
        {
          err = errno;
          /* The Linux kernel before version 3.6 does not support
diff --git a/src/dispextern.h b/src/dispextern.h
index ece128949f5..3a4d6095f73 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -53,8 +53,14 @@ typedef struct
   unsigned short red, green, blue;
 } Emacs_Color;
 
+#ifndef HAVE_ANDROID
 /* Accommodate X's usage of None as a null resource ID.  */
 #define No_Cursor (NULL)
+#else
+#define No_Cursor 0
+#endif
+
+#ifndef HAVE_ANDROID
 
 /* XRectangle-like struct used by non-X GUI code.  */
 typedef struct
@@ -63,6 +69,12 @@ typedef struct
   unsigned width, height;
 } Emacs_Rectangle;
 
+#else
+
+typedef struct android_rectangle Emacs_Rectangle;
+
+#endif
+
 /* XGCValues-like struct used by non-X GUI code.  */
 typedef struct
 {
@@ -144,6 +156,13 @@ typedef Emacs_Pixmap Emacs_Pix_Container;
 typedef Emacs_Pixmap Emacs_Pix_Context;
 #endif
 
+#ifdef HAVE_ANDROID
+#include "androidgui.h"
+typedef struct android_display_info Display_Info;
+typedef struct android_image *Emacs_Pix_Container;
+typedef struct android_image *Emacs_Pix_Context;
+#endif
+
 #ifdef HAVE_WINDOW_SYSTEM
 # include <time.h>
 # include "fontset.h"
@@ -157,6 +176,22 @@ typedef void *Emacs_Cursor;
 #define NativeRectangle int
 #endif
 
+#ifdef HAVE_WINDOW_SYSTEM
+
+/* ``box'' structure similar to that found in the X sample server,
+   meaning that X2 and Y2 are not actually the end of the box, but one
+   pixel past the end of the box, which makes checking for overlaps
+   less necessary.  This is convenient to use in every GUI port.  */
+
+struct gui_box
+{
+  /* Bounds of the box.  */
+  int x1, y1;
+  int x2, y2;
+};
+
+#endif
+
 /* Text cursor types.  */
 
 enum text_cursor_kinds
@@ -1401,6 +1436,8 @@ struct glyph_string
   /* The GC to use for drawing this glyph string.  */
 #if defined (HAVE_X_WINDOWS)
   GC gc;
+#elif defined HAVE_ANDROID
+  struct android_gc *gc;
 #endif
 #if defined (HAVE_NTGUI)
   Emacs_GC *gc;
@@ -1681,6 +1718,8 @@ struct face
      drawing the characters in this face.  */
 # ifdef HAVE_X_WINDOWS
   GC gc;
+# elif defined HAVE_ANDROID
+  struct android_gc *gc;
 # else
   Emacs_GC *gc;
 # endif
@@ -3057,8 +3096,9 @@ struct redisplay_interface
 
 #ifdef HAVE_WINDOW_SYSTEM
 
-# if (defined USE_CAIRO || defined HAVE_XRENDER \
-      || defined HAVE_NS || defined HAVE_NTGUI || defined HAVE_HAIKU)
+# if (defined USE_CAIRO || defined HAVE_XRENDER                                
\
+      || defined HAVE_NS || defined HAVE_NTGUI || defined HAVE_HAIKU   \
+      || defined HAVE_ANDROID)
 #  define HAVE_NATIVE_TRANSFORMS
 # endif
 
@@ -3094,6 +3134,13 @@ struct image
   int original_width, original_height;
 # endif
 #endif /* HAVE_X_WINDOWS */
+#ifdef HAVE_ANDROID
+  /* Android images of the image, corresponding to the above Pixmaps.
+     Non-NULL means it and its Pixmap counterpart may be out of sync
+     and the latter is outdated.  NULL means the X image has been
+     synchronized to Pixmap.  */
+  struct android_image *ximg, *mask_img;
+#endif /* HAVE_ANDROID */
 #ifdef HAVE_NTGUI
   XFORM xform;
 #endif
@@ -3316,9 +3363,13 @@ enum tool_bar_item_idx
   /* If we shall show the label only below the icon and not beside it.  */
   TOOL_BAR_ITEM_VERT_ONLY,
 
+  /* Whether or not this tool bar item is hidden and should cause
+     subsequent items to be displayed on a new line.  */
+  TOOL_BAR_ITEM_WRAP,
+
   /* Sentinel = number of slots in tool_bar_items occupied by one
      tool-bar item.  */
-  TOOL_BAR_ITEM_NSLOTS
+  TOOL_BAR_ITEM_NSLOTS,
 };
 
 
@@ -3480,6 +3531,7 @@ extern void get_glyph_string_clip_rect (struct 
glyph_string *,
                                         NativeRectangle *nr);
 extern Lisp_Object find_hot_spot (Lisp_Object, int, int);
 
+extern int get_tab_bar_item_kbd (struct frame *, int, int, int *, bool *);
 extern Lisp_Object handle_tab_bar_click (struct frame *,
                                         int, int, bool, int);
 extern void handle_tool_bar_click (struct frame *,
@@ -3491,6 +3543,9 @@ extern void expose_frame (struct frame *, int, int, int, 
int);
 extern bool gui_intersect_rectangles (const Emacs_Rectangle *,
                                       const Emacs_Rectangle *,
                                       Emacs_Rectangle *);
+extern void gui_union_rectangles (const Emacs_Rectangle *,
+                                 const Emacs_Rectangle *,
+                                 Emacs_Rectangle *);
 extern void gui_consider_frame_title (Lisp_Object);
 #endif /* HAVE_WINDOW_SYSTEM */
 
@@ -3499,9 +3554,11 @@ extern void gui_clear_window_mouse_face (struct window 
*);
 extern void cancel_mouse_face (struct frame *);
 extern bool clear_mouse_face (Mouse_HLInfo *);
 extern bool cursor_in_mouse_face_p (struct window *w);
+#ifndef HAVE_ANDROID
 extern void tty_draw_row_with_mouse_face (struct window *, struct glyph_row *,
                                          int, int, enum draw_glyphs_face);
 extern void display_tty_menu_item (const char *, int, int, int, int, bool);
+#endif
 extern struct glyph *x_y_to_hpos_vpos (struct window *, int, int, int *, int *,
                                       int *, int *, int *);
 /* Flags passed to try_window.  */
@@ -3563,7 +3620,7 @@ void prepare_image_for_display (struct frame *, struct 
image *);
 ptrdiff_t lookup_image (struct frame *, Lisp_Object, int);
 
 #if defined HAVE_X_WINDOWS || defined USE_CAIRO || defined HAVE_NS \
-  || defined HAVE_HAIKU
+  || defined HAVE_HAIKU || defined HAVE_ANDROID
 #define RGB_PIXEL_COLOR unsigned long
 #endif
 
@@ -3644,6 +3701,9 @@ void gamma_correct (struct frame *, COLORREF *);
 #ifdef HAVE_HAIKU
 void gamma_correct (struct frame *, Emacs_Color *);
 #endif
+#ifdef HAVE_ANDROID
+extern void gamma_correct (struct frame *, Emacs_Color *);
+#endif
 
 #ifdef HAVE_WINDOW_SYSTEM
 
diff --git a/src/dispnew.c b/src/dispnew.c
index 28f0eaeaa95..02388bcef2b 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -43,6 +43,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "xwidget.h"
 #include "pdumper.h"
 
+#ifdef HAVE_ANDROID
+#include "android.h"
+#endif
+
 #ifdef HAVE_WINDOW_SYSTEM
 #include TERM_HEADER
 #endif /* HAVE_WINDOW_SYSTEM */
@@ -788,7 +792,7 @@ clear_current_matrices (register struct frame *f)
   if (f->current_matrix)
     clear_glyph_matrix (f->current_matrix);
 
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined 
(USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
   /* Clear the matrix of the menu bar window, if such a window exists.
      The menu bar window is currently used to display menus on X when
      no toolkit support is compiled in.  */
@@ -822,7 +826,7 @@ clear_desired_matrices (register struct frame *f)
   if (f->desired_matrix)
     clear_glyph_matrix (f->desired_matrix);
 
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined 
(USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
   if (WINDOWP (f->menu_bar_window))
     clear_glyph_matrix (XWINDOW (f->menu_bar_window)->desired_matrix);
 #endif
@@ -1156,6 +1160,7 @@ prepare_desired_row (struct window *w, struct glyph_row 
*row, bool mode_line_p)
     }
 }
 
+#ifndef HAVE_ANDROID
 
 /* Return a hash code for glyph row ROW, which may
    be from current or desired matrix of frame F.  */
@@ -1248,6 +1253,7 @@ line_draw_cost (struct frame *f, struct glyph_matrix 
*matrix, int vpos)
   return len;
 }
 
+#endif
 
 /* Return true if the glyph rows A and B have equal contents.
    MOUSE_FACE_P means compare the mouse_face_p flags of A and B, too.  */
@@ -2160,7 +2166,7 @@ adjust_frame_glyphs_for_window_redisplay (struct frame *f)
   /* Allocate/reallocate window matrices.  */
   allocate_matrices_for_window_redisplay (XWINDOW (FRAME_ROOT_WINDOW (f)));
 
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined 
(USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
   /* Allocate/ reallocate matrices of the dummy window used to display
      the menu bar under X when no X toolkit support is available.  */
   {
@@ -2321,7 +2327,7 @@ free_glyphs (struct frame *f)
       if (!NILP (f->root_window))
         free_window_matrices (XWINDOW (f->root_window));
 
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined 
(USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
       /* Free the dummy window for menu bars without X toolkit and its
         glyph matrices.  */
       if (!NILP (f->menu_bar_window))
@@ -3194,6 +3200,7 @@ redraw_frame (struct frame *f)
      its redisplay done.  */
   mark_window_display_accurate (FRAME_ROOT_WINDOW (f), 0);
   set_window_update_flags (XWINDOW (FRAME_ROOT_WINDOW (f)), true);
+
   f->garbaged = false;
 }
 
@@ -3259,7 +3266,7 @@ update_frame (struct frame *f, bool force_p, bool 
inhibit_hairy_id_p)
         when pending input is detected.  */
       update_begin (f);
 
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined 
(USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
       /* Update the menu bar on X frames that don't have toolkit
         support.  */
       if (WINDOWP (f->menu_bar_window))
@@ -5096,6 +5103,10 @@ update_frame_1 (struct frame *f, bool force_p, bool 
inhibit_id_p,
 static bool
 scrolling (struct frame *frame)
 {
+  /* In fact this code should never be reached at all under
+     Android.  */
+
+#ifndef HAVE_ANDROID
   int unchanged_at_top, unchanged_at_bottom;
   int window_size;
   int changed_lines;
@@ -5186,6 +5197,7 @@ scrolling (struct frame *frame)
                 free_at_end_vpos - unchanged_at_top);
 
   SAFE_FREE ();
+#endif
   return false;
 }
 
@@ -5227,7 +5239,9 @@ count_match (struct glyph *str1, struct glyph *end1, 
struct glyph *str2, struct
 
 /* Char insertion/deletion cost vector, from term.c */
 
+#ifndef HAVE_ANDROID
 #define char_ins_del_cost(f) (&char_ins_del_vector[FRAME_TOTAL_COLS ((f))])
+#endif
 
 
 /* Perform a frame-based update on line VPOS in frame FRAME.  */
@@ -5432,7 +5446,10 @@ update_frame_line (struct frame *f, int vpos, bool 
updating_menu_p)
   tem = (nlen - nsp) - (olen - osp);
   if (endmatch && tem
       && (!FRAME_CHAR_INS_DEL_OK (f)
-          || endmatch <= char_ins_del_cost (f)[tem]))
+#ifndef HAVE_ANDROID
+          || endmatch <= char_ins_del_cost (f)[tem]
+#endif
+         ))
     endmatch = 0;
 
   /* nsp - osp is the distance to insert or delete.
@@ -5442,7 +5459,10 @@ update_frame_line (struct frame *f, int vpos, bool 
updating_menu_p)
 
   if (nsp != osp
       && (!FRAME_CHAR_INS_DEL_OK (f)
-         || begmatch + endmatch <= char_ins_del_cost (f)[nsp - osp]))
+#ifndef HAVE_ANDROID
+         || begmatch + endmatch <= char_ins_del_cost (f)[nsp - osp]
+#endif
+         ))
     {
       begmatch = 0;
       endmatch = 0;
@@ -6080,7 +6100,7 @@ FILE = nil means just close any termscript file currently 
open.  */)
   if (tty->termscript != 0)
     {
       block_input ();
-      fclose (tty->termscript);
+      emacs_fclose (tty->termscript);
       tty->termscript = 0;
       unblock_input ();
     }
@@ -6574,6 +6594,15 @@ init_display_interactive (void)
     }
 #endif /* HAVE_X_WINDOWS */
 
+#ifdef HAVE_ANDROID
+  if (!inhibit_window_system && android_init_gui)
+    {
+      Vinitial_window_system = Qandroid;
+      android_term_init ();
+      return;
+    }
+#endif
+
 #ifdef HAVE_NTGUI
   if (!inhibit_window_system)
     {
@@ -6628,6 +6657,7 @@ init_display_interactive (void)
       exit (1);
     }
 
+#ifndef HAVE_ANDROID
   {
     struct terminal *t;
     struct frame *f = XFRAME (selected_frame);
@@ -6670,6 +6700,11 @@ init_display_interactive (void)
                                    : Qnil));
     Fmodify_frame_parameters (selected_frame, tty_arg);
   }
+#else
+  fatal ("Could not establish a connection to the Android application.\n"
+        "Emacs does not work on text terminals when built to run as"
+        " part of an Android application package.");
+#endif
 
   {
     struct frame *sf = SELECTED_FRAME ();
diff --git a/src/doc.c b/src/doc.c
index 70831be69c2..b898182d683 100644
--- a/src/doc.c
+++ b/src/doc.c
@@ -37,6 +37,39 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "intervals.h"
 #include "keymap.h"
 
+
+
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY   \
+  || (__ANDROID_API__ < 9)
+#define doc_fd         int
+#define doc_fd_p(fd)   ((fd) >= 0)
+#define doc_open       emacs_open
+#define doc_read_quit  emacs_read_quit
+#define doc_lseek      lseek
+#else /* HAVE_ANDROID && !defined ANDROID_STUBIFY
+        && __ANDROID_API__ >= 9 */
+
+#include "android.h"
+
+/* Use an Android file descriptor under Android instead, as this
+   allows loading directly from asset files without loading each asset
+   into memory and creating a separate file descriptor every time.
+
+   However, lread requires the ability to seek inside asset files,
+   which is not provided under Android 2.2.  So when building for that
+   particular system, fall back to the usual file descriptor-based
+   code.  */
+
+#define doc_fd         struct android_fd_or_asset
+#define doc_fd_p(fd)   ((fd).asset != (void *) -1)
+#define doc_open       android_open_asset
+#define doc_read_quit  android_asset_read_quit
+#define doc_lseek      android_asset_lseek
+#define USE_ANDROID_ASSETS
+#endif /* !HAVE_ANDROID || ANDROID_STUBIFY || __ANDROID_API__ < 9 */
+
+
+
 /* Buffer used for reading from documentation file.  */
 static char *get_doc_string_buffer;
 static ptrdiff_t get_doc_string_buffer_size;
@@ -59,6 +92,22 @@ read_bytecode_char (bool unreadflag)
   return *read_bytecode_pointer++;
 }
 
+#ifdef USE_ANDROID_ASSETS
+
+/* Like `close_file_unwind'.  However, PTR is a pointer to an Android
+   file descriptor instead of a system file descriptor.  */
+
+static void
+close_file_unwind_android_fd (void *ptr)
+{
+  struct android_fd_or_asset *fd;
+
+  fd = ptr;
+  android_close_asset (*fd);
+}
+
+#endif /* USE_ANDROID_ASSETS */
+
 /* Extract a doc string from a file.  FILEPOS says where to get it.
    If it is an integer, use that position in the standard DOC file.
    If it is (FILE . INTEGER), use FILE as the file name
@@ -123,8 +172,8 @@ get_doc_string (Lisp_Object filepos, bool unibyte, bool 
definition)
   name = SAFE_ALLOCA (docdir_sizemax + SBYTES (file));
   lispstpcpy (lispstpcpy (name, docdir), file);
 
-  int fd = emacs_open (name, O_RDONLY, 0);
-  if (fd < 0)
+  doc_fd fd = doc_open (name, O_RDONLY, 0);
+  if (!doc_fd_p (fd))
     {
       if (will_dump_p ())
        {
@@ -132,9 +181,9 @@ get_doc_string (Lisp_Object filepos, bool unibyte, bool 
definition)
             So check in ../etc.  */
          lispstpcpy (stpcpy (name, sibling_etc), file);
 
-         fd = emacs_open (name, O_RDONLY, 0);
+         fd = doc_open (name, O_RDONLY, 0);
        }
-      if (fd < 0)
+      if (!doc_fd_p (fd))
        {
          if (errno != ENOENT && errno != ENOTDIR)
            report_file_error ("Read error on documentation file", file);
@@ -145,14 +194,18 @@ get_doc_string (Lisp_Object filepos, bool unibyte, bool 
definition)
          return concat3 (cannot_open, file, quote_nl);
        }
     }
+#ifndef USE_ANDROID_ASSETS
   record_unwind_protect_int (close_file_unwind, fd);
+#else /* USE_ANDROID_ASSETS */
+  record_unwind_protect_ptr (close_file_unwind_android_fd, &fd);
+#endif /* !USE_ANDROID_ASSETS */
 
   /* Seek only to beginning of disk block.  */
   /* Make sure we read at least 1024 bytes before `position'
      so we can check the leading text for consistency.  */
   int offset = min (position, max (1024, position % (8 * 1024)));
   if (TYPE_MAXIMUM (off_t) < position
-      || lseek (fd, position - offset, 0) < 0)
+      || doc_lseek (fd, position - offset, 0) < 0)
     error ("Position %"pI"d out of range in doc string file \"%s\"",
           position, name);
 
@@ -181,7 +234,7 @@ get_doc_string (Lisp_Object filepos, bool unibyte, bool 
definition)
          If we read the same block last time, maybe skip this?  */
       if (space_left > 1024 * 8)
        space_left = 1024 * 8;
-      int nread = emacs_read_quit (fd, p, space_left);
+      int nread = doc_read_quit (fd, p, space_left);
       if (nread < 0)
        report_file_error ("Read error on documentation file", file);
       p[nread] = 0;
@@ -504,7 +557,7 @@ That file is found in `../etc' now; later, when the dumped 
Emacs is run,
 the same file name is found in the `doc-directory'.  */)
   (Lisp_Object filename)
 {
-  int fd;
+  doc_fd fd;
   char buf[1024 + 1];
   int filled;
   EMACS_INT pos;
@@ -550,21 +603,25 @@ the same file name is found in the `doc-directory'.  */)
       Vbuild_files = Fpurecopy (Vbuild_files);
     }
 
-  fd = emacs_open (name, O_RDONLY, 0);
-  if (fd < 0)
+  fd = doc_open (name, O_RDONLY, 0);
+  if (!doc_fd_p (fd))
     {
       int open_errno = errno;
       report_file_errno ("Opening doc string file", build_string (name),
                         open_errno);
     }
+#ifndef USE_ANDROID_ASSETS
   record_unwind_protect_int (close_file_unwind, fd);
+#else /* USE_ANDROID_ASSETS */
+  record_unwind_protect_ptr (close_file_unwind_android_fd, &fd);
+#endif /* !USE_ANDROID_ASSETS */
   Vdoc_file_name = filename;
   filled = 0;
   pos = 0;
   while (true)
     {
       if (filled < 512)
-       filled += emacs_read_quit (fd, &buf[filled], sizeof buf - 1 - filled);
+       filled += doc_read_quit (fd, &buf[filled], sizeof buf - 1 - filled);
       if (!filled)
        break;
 
diff --git a/src/editfns.c b/src/editfns.c
index 1b6dee2acf0..5d32fd31c12 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -33,6 +33,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <sys/utsname.h>
 #endif
 
+#ifdef HAVE_ANDROID
+#include "android.h"
+#endif
+
 #include "lisp.h"
 
 #include <float.h>
@@ -1269,7 +1273,11 @@ is in general a comma-separated list.  */)
   if (!pw)
     return Qnil;
 
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  p = android_user_full_name (pw);
+#else
   p = USER_FULL_NAME;
+#endif
   /* Chop off everything after the first comma, since 'pw_gecos' is a
      comma-separated list. */
   q = strchr (p, ',');
diff --git a/src/emacs-module.c b/src/emacs-module.c
index 10699ec25d9..86360a0f225 100644
--- a/src/emacs-module.c
+++ b/src/emacs-module.c
@@ -206,7 +206,7 @@ static void module_non_local_exit_signal_1 (emacs_env *,
 static void module_non_local_exit_throw_1 (emacs_env *,
                                           Lisp_Object, Lisp_Object);
 static void module_out_of_memory (emacs_env *);
-static void module_reset_handlerlist (struct handler **);
+static void module_reset_handlerlist (struct handler *);
 static bool value_storage_contains_p (const struct emacs_value_storage *,
                                       emacs_value, ptrdiff_t *);
 
@@ -246,10 +246,6 @@ module_decode_utf_8 (const char *str, ptrdiff_t len)
    of `internal_condition_case' etc., and to avoid worrying about
    passing information to the handler functions.  */
 
-#if !HAS_ATTRIBUTE (cleanup)
- #error "__attribute__ ((cleanup)) not supported by this compiler; try GCC"
-#endif
-
 /* Place this macro at the beginning of a function returning a number
    or a pointer to handle non-local exits.  The function must have an
    ENV parameter.  The function will return the specified value if a
@@ -257,8 +253,8 @@ module_decode_utf_8 (const char *str, ptrdiff_t len)
 
 /* It is very important that pushing the handler doesn't itself raise
    a signal.  Install the cleanup only after the handler has been
-   pushed.  Use __attribute__ ((cleanup)) to avoid
-   non-local-exit-prone manual cleanup.
+   pushed.  All code following this point should use
+   MODULE_INTERNAL_CLEANUP before each return.
 
    The do-while forces uses of the macro to be followed by a semicolon.
    This macro cannot enclose its entire body inside a do-while, as the
@@ -278,17 +274,20 @@ module_decode_utf_8 (const char *str, ptrdiff_t len)
       return retval;                                                   \
     }                                                                  \
   struct handler *internal_cleanup                                      \
-    __attribute__ ((cleanup (module_reset_handlerlist)))                \
     = internal_handler;                                                 \
   if (sys_setjmp (internal_cleanup->jmp))                               \
     {                                                                  \
       module_handle_nonlocal_exit (env,                                 \
                                    internal_cleanup->nonlocal_exit,     \
                                    internal_cleanup->val);              \
+      module_reset_handlerlist (internal_cleanup);                     \
       return retval;                                                   \
     }                                                                  \
   do { } while (false)
 
+#define MODULE_INTERNAL_CLEANUP()              \
+  module_reset_handlerlist (internal_cleanup)
+
 
 /* Implementation of runtime and environment functions.
 
@@ -315,7 +314,10 @@ module_decode_utf_8 (const char *str, ptrdiff_t len)
       Emacs functions, by placing the macro
       MODULE_HANDLE_NONLOCAL_EXIT right after the above 2 tests.
 
-   5. Do NOT use 'eassert' for checking validity of user code in the
+   5. Finally, any code which expands MODULE_HANDLE_NONLOCAL_EXIT
+      should use MODULE_INTERNAL_CLEANUP prior to returning.
+
+   6. Do NOT use 'eassert' for checking validity of user code in the
       module.  Instead, make those checks part of the code, and if the
       check fails, call 'module_non_local_exit_signal_1' or
       'module_non_local_exit_throw_1' to report the error.  This is
@@ -438,6 +440,7 @@ module_make_global_ref (emacs_env *env, emacs_value value)
       bool overflow = ckd_add (&ref->refcount, ref->refcount, 1);
       if (overflow)
        overflow_error ();
+      MODULE_INTERNAL_CLEANUP ();
       return &ref->value;
     }
   else
@@ -450,6 +453,7 @@ module_make_global_ref (emacs_env *env, emacs_value value)
       Lisp_Object value;
       XSETPSEUDOVECTOR (value, ref, PVEC_OTHER);
       hash_put (h, new_obj, value, hashcode);
+      MODULE_INTERNAL_CLEANUP ();
       return &ref->value;
     }
 }
@@ -481,6 +485,8 @@ module_free_global_ref (emacs_env *env, emacs_value 
global_value)
       if (--ref->refcount == 0)
         hash_remove_from_table (h, obj);
     }
+
+  MODULE_INTERNAL_CLEANUP ();
 }
 
 static enum emacs_funcall_exit
@@ -574,6 +580,8 @@ static emacs_value
 module_make_function (emacs_env *env, ptrdiff_t min_arity, ptrdiff_t max_arity,
                      emacs_function func, const char *docstring, void *data)
 {
+  emacs_value value;
+
   MODULE_FUNCTION_BEGIN (NULL);
 
   if (! (0 <= min_arity
@@ -598,7 +606,9 @@ module_make_function (emacs_env *env, ptrdiff_t min_arity, 
ptrdiff_t max_arity,
   XSET_MODULE_FUNCTION (result, function);
   eassert (MODULE_FUNCTIONP (result));
 
-  return lisp_to_value (env, result);
+  value = lisp_to_value (env, result);
+  MODULE_INTERNAL_CLEANUP ();
+  return value;
 }
 
 static emacs_finalizer
@@ -607,6 +617,7 @@ module_get_function_finalizer (emacs_env *env, emacs_value 
arg)
   MODULE_FUNCTION_BEGIN (NULL);
   Lisp_Object lisp = value_to_lisp (arg);
   CHECK_MODULE_FUNCTION (lisp);
+  MODULE_INTERNAL_CLEANUP ();
   return XMODULE_FUNCTION (lisp)->finalizer;
 }
 
@@ -618,6 +629,7 @@ module_set_function_finalizer (emacs_env *env, emacs_value 
arg,
   Lisp_Object lisp = value_to_lisp (arg);
   CHECK_MODULE_FUNCTION (lisp);
   XMODULE_FUNCTION (lisp)->finalizer = fin;
+  MODULE_INTERNAL_CLEANUP ();
 }
 
 void
@@ -637,6 +649,7 @@ module_make_interactive (emacs_env *env, emacs_value 
function, emacs_value spec)
   /* Normalize (interactive nil) to (interactive). */
   XMODULE_FUNCTION (lisp_fun)->interactive_form
     = NILP (lisp_spec) ? list1 (Qinteractive) : list2 (Qinteractive, 
lisp_spec);
+  MODULE_INTERNAL_CLEANUP ();
 }
 
 Lisp_Object
@@ -670,21 +683,30 @@ module_funcall (emacs_env *env, emacs_value func, 
ptrdiff_t nargs,
     newargs[1 + i] = value_to_lisp (args[i]);
   emacs_value result = lisp_to_value (env, Ffuncall (nargs1, newargs));
   SAFE_FREE ();
+  MODULE_INTERNAL_CLEANUP ();
   return result;
 }
 
 static emacs_value
 module_intern (emacs_env *env, const char *name)
 {
+  emacs_value tem;
+
   MODULE_FUNCTION_BEGIN (NULL);
-  return lisp_to_value (env, intern (name));
+  tem = lisp_to_value (env, intern (name));
+  MODULE_INTERNAL_CLEANUP ();
+  return tem;
 }
 
 static emacs_value
 module_type_of (emacs_env *env, emacs_value arg)
 {
+  emacs_value tem;
+
   MODULE_FUNCTION_BEGIN (NULL);
-  return lisp_to_value (env, Ftype_of (value_to_lisp (arg)));
+  tem = lisp_to_value (env, Ftype_of (value_to_lisp (arg)));
+  MODULE_INTERNAL_CLEANUP ();
+  return tem;
 }
 
 static bool
@@ -710,14 +732,20 @@ module_extract_integer (emacs_env *env, emacs_value arg)
   intmax_t i;
   if (! integer_to_intmax (lisp, &i))
     xsignal1 (Qoverflow_error, lisp);
+  MODULE_INTERNAL_CLEANUP ();
   return i;
 }
 
 static emacs_value
 module_make_integer (emacs_env *env, intmax_t n)
 {
+  emacs_value value;
+
   MODULE_FUNCTION_BEGIN (NULL);
-  return lisp_to_value (env, make_int (n));
+  value = lisp_to_value (env, make_int (n));
+  MODULE_INTERNAL_CLEANUP ();
+
+  return value;
 }
 
 static double
@@ -726,14 +754,21 @@ module_extract_float (emacs_env *env, emacs_value arg)
   MODULE_FUNCTION_BEGIN (0);
   Lisp_Object lisp = value_to_lisp (arg);
   CHECK_TYPE (FLOATP (lisp), Qfloatp, lisp);
+  MODULE_INTERNAL_CLEANUP ();
+
   return XFLOAT_DATA (lisp);
 }
 
 static emacs_value
 module_make_float (emacs_env *env, double d)
 {
+  emacs_value value;
+
   MODULE_FUNCTION_BEGIN (NULL);
-  return lisp_to_value (env, make_float (d));
+  value = lisp_to_value (env, make_float (d));
+  MODULE_INTERNAL_CLEANUP ();
+
+  return value;
 }
 
 static bool
@@ -765,6 +800,7 @@ module_copy_string_contents (emacs_env *env, emacs_value 
value, char *buf,
   if (buf == NULL)
     {
       *len = required_buf_size;
+      MODULE_INTERNAL_CLEANUP ();
       return true;
     }
 
@@ -780,36 +816,51 @@ module_copy_string_contents (emacs_env *env, emacs_value 
value, char *buf,
   *len = required_buf_size;
   memcpy (buf, SDATA (lisp_str_utf8), raw_size + 1);
 
+  MODULE_INTERNAL_CLEANUP ();
   return true;
 }
 
 static emacs_value
 module_make_string (emacs_env *env, const char *str, ptrdiff_t len)
 {
+  emacs_value value;
+
   MODULE_FUNCTION_BEGIN (NULL);
   if (! (0 <= len && len <= STRING_BYTES_BOUND))
     overflow_error ();
   Lisp_Object lstr
     = len == 0 ? empty_multibyte_string : module_decode_utf_8 (str, len);
-  return lisp_to_value (env, lstr);
+  value = lisp_to_value (env, lstr);
+  MODULE_INTERNAL_CLEANUP ();
+  return value;
 }
 
 static emacs_value
 module_make_unibyte_string (emacs_env *env, const char *str, ptrdiff_t length)
 {
+  emacs_value value;
+
   MODULE_FUNCTION_BEGIN (NULL);
   if (! (0 <= length && length <= STRING_BYTES_BOUND))
     overflow_error ();
   Lisp_Object lstr
     = length == 0 ? empty_unibyte_string : make_unibyte_string (str, length);
-  return lisp_to_value (env, lstr);
+  value = lisp_to_value (env, lstr);
+  MODULE_INTERNAL_CLEANUP ();
+
+  return value;
 }
 
 static emacs_value
 module_make_user_ptr (emacs_env *env, emacs_finalizer fin, void *ptr)
 {
+  emacs_value value;
+
   MODULE_FUNCTION_BEGIN (NULL);
-  return lisp_to_value (env, make_user_ptr (fin, ptr));
+  value = lisp_to_value (env, make_user_ptr (fin, ptr));
+  MODULE_INTERNAL_CLEANUP ();
+
+  return value;
 }
 
 static void *
@@ -818,6 +869,8 @@ module_get_user_ptr (emacs_env *env, emacs_value arg)
   MODULE_FUNCTION_BEGIN (NULL);
   Lisp_Object lisp = value_to_lisp (arg);
   CHECK_USER_PTR (lisp);
+  MODULE_INTERNAL_CLEANUP ();
+
   return XUSER_PTR (lisp)->p;
 }
 
@@ -828,6 +881,7 @@ module_set_user_ptr (emacs_env *env, emacs_value arg, void 
*ptr)
   Lisp_Object lisp = value_to_lisp (arg);
   CHECK_USER_PTR (lisp);
   XUSER_PTR (lisp)->p = ptr;
+  MODULE_INTERNAL_CLEANUP ();
 }
 
 static emacs_finalizer
@@ -836,6 +890,7 @@ module_get_user_finalizer (emacs_env *env, emacs_value arg)
   MODULE_FUNCTION_BEGIN (NULL);
   Lisp_Object lisp = value_to_lisp (arg);
   CHECK_USER_PTR (lisp);
+  MODULE_INTERNAL_CLEANUP ();
   return XUSER_PTR (lisp)->finalizer;
 }
 
@@ -847,6 +902,7 @@ module_set_user_finalizer (emacs_env *env, emacs_value arg,
   Lisp_Object lisp = value_to_lisp (arg);
   CHECK_USER_PTR (lisp);
   XUSER_PTR (lisp)->finalizer = fin;
+  MODULE_INTERNAL_CLEANUP ();
 }
 
 static void
@@ -866,15 +922,21 @@ module_vec_set (emacs_env *env, emacs_value vector, 
ptrdiff_t index,
   Lisp_Object lisp = value_to_lisp (vector);
   check_vec_index (lisp, index);
   ASET (lisp, index, value_to_lisp (value));
+  MODULE_INTERNAL_CLEANUP ();
 }
 
 static emacs_value
 module_vec_get (emacs_env *env, emacs_value vector, ptrdiff_t index)
 {
+  emacs_value value;
+
   MODULE_FUNCTION_BEGIN (NULL);
   Lisp_Object lisp = value_to_lisp (vector);
   check_vec_index (lisp, index);
-  return lisp_to_value (env, AREF (lisp, index));
+  value = lisp_to_value (env, AREF (lisp, index));
+  MODULE_INTERNAL_CLEANUP ();
+
+  return value;
 }
 
 static ptrdiff_t
@@ -883,6 +945,8 @@ module_vec_size (emacs_env *env, emacs_value vector)
   MODULE_FUNCTION_BEGIN (0);
   Lisp_Object lisp = value_to_lisp (vector);
   CHECK_VECTOR (lisp);
+  MODULE_INTERNAL_CLEANUP ();
+
   return ASIZE (lisp);
 }
 
@@ -898,23 +962,37 @@ module_should_quit (emacs_env *env)
 static enum emacs_process_input_result
 module_process_input (emacs_env *env)
 {
+  enum emacs_process_input_result rc;
+
   MODULE_FUNCTION_BEGIN (emacs_process_input_quit);
   maybe_quit ();
-  return emacs_process_input_continue;
+  rc = emacs_process_input_continue;
+  MODULE_INTERNAL_CLEANUP ();
+  return rc;
 }
 
 static struct timespec
 module_extract_time (emacs_env *env, emacs_value arg)
 {
+  struct timespec value;
+
   MODULE_FUNCTION_BEGIN ((struct timespec) {0});
-  return lisp_time_argument (value_to_lisp (arg));
+  value = lisp_time_argument (value_to_lisp (arg));
+  MODULE_INTERNAL_CLEANUP ();
+
+  return value;
 }
 
 static emacs_value
 module_make_time (emacs_env *env, struct timespec time)
 {
+  emacs_value value;
+
   MODULE_FUNCTION_BEGIN (NULL);
-  return lisp_to_value (env, timespec_to_lisp (time));
+  value = lisp_to_value (env, timespec_to_lisp (time));
+  MODULE_INTERNAL_CLEANUP ();
+
+  return value;
 }
 
 /*
@@ -991,7 +1069,10 @@ module_extract_big_integer (emacs_env *env, emacs_value 
arg, int *sign,
       EMACS_INT x = XFIXNUM (o);
       *sign = (0 < x) - (x < 0);
       if (x == 0 || count == NULL)
-        return true;
+       {
+         MODULE_INTERNAL_CLEANUP ();
+         return true;
+       }
       /* As a simplification we don't check how many array elements
          are exactly required, but use a reasonable static upper
          bound.  For most architectures exactly one element should
@@ -1002,6 +1083,7 @@ module_extract_big_integer (emacs_env *env, emacs_value 
arg, int *sign,
       if (magnitude == NULL)
         {
           *count = required;
+         MODULE_INTERNAL_CLEANUP ();
           return true;
         }
       if (*count < required)
@@ -1020,12 +1102,16 @@ module_extract_big_integer (emacs_env *env, emacs_value 
arg, int *sign,
       verify (required * bits < PTRDIFF_MAX);
       for (ptrdiff_t i = 0; i < required; ++i)
         magnitude[i] = (emacs_limb_t) (u >> (i * bits));
+      MODULE_INTERNAL_CLEANUP ();
       return true;
     }
   const mpz_t *x = xbignum_val (o);
   *sign = mpz_sgn (*x);
   if (count == NULL)
-    return true;
+    {
+      MODULE_INTERNAL_CLEANUP ();
+      return true;
+    }
   size_t required_size = (mpz_sizeinbase (*x, 2) + numb - 1) / numb;
   eassert (required_size <= PTRDIFF_MAX);
   ptrdiff_t required = (ptrdiff_t) required_size;
@@ -1033,6 +1119,7 @@ module_extract_big_integer (emacs_env *env, emacs_value 
arg, int *sign,
   if (magnitude == NULL)
     {
       *count = required;
+      MODULE_INTERNAL_CLEANUP ();
       return true;
     }
   if (*count < required)
@@ -1045,6 +1132,7 @@ module_extract_big_integer (emacs_env *env, emacs_value 
arg, int *sign,
   size_t written;
   mpz_export (magnitude, &written, order, size, endian, nails, *x);
   eassert (written == required_size);
+  MODULE_INTERNAL_CLEANUP ();
   return true;
 }
 
@@ -1052,21 +1140,34 @@ static emacs_value
 module_make_big_integer (emacs_env *env, int sign,
                          ptrdiff_t count, const emacs_limb_t *magnitude)
 {
+  emacs_value value;
+
   MODULE_FUNCTION_BEGIN (NULL);
   if (sign == 0)
-    return lisp_to_value (env, make_fixed_natnum (0));
+    {
+      value = lisp_to_value (env, make_fixed_natnum (0));
+      MODULE_INTERNAL_CLEANUP ();
+      return value;
+    }
   enum { order = -1, size = sizeof *magnitude, endian = 0, nails = 0 };
   mpz_import (mpz[0], count, order, size, endian, nails, magnitude);
   if (sign < 0)
     mpz_neg (mpz[0], mpz[0]);
-  return lisp_to_value (env, make_integer_mpz ());
+  value = lisp_to_value (env, make_integer_mpz ());
+  MODULE_INTERNAL_CLEANUP ();
+  return value;
 }
 
 static int
 module_open_channel (emacs_env *env, emacs_value pipe_process)
 {
+  int rc;
+
   MODULE_FUNCTION_BEGIN (-1);
-  return open_channel_for_module (value_to_lisp (pipe_process));
+  rc = open_channel_for_module (value_to_lisp (pipe_process));
+  MODULE_INTERNAL_CLEANUP ();
+
+  return rc;
 }
 
 
@@ -1519,12 +1620,13 @@ finalize_runtime_unwind (void *raw_ert)
 /* Must be called after setting up a handler immediately before
    returning from the function.  See the comments in lisp.h and the
    code in eval.c for details.  The macros below arrange for this
-   function to be called automatically.  PHANDLERLIST points to a word
-   containing the handler list, for sanity checking.  */
+   function to be called automatically.  IHANDLERLIST points to the
+   handler list.  */
+
 static void
-module_reset_handlerlist (struct handler **phandlerlist)
+module_reset_handlerlist (struct handler *ihandlerlist)
 {
-  eassert (handlerlist == *phandlerlist);
+  eassert (handlerlist == ihandlerlist);
   handlerlist = handlerlist->next;
 }
 
diff --git a/src/emacs.c b/src/emacs.c
index d95a26dee99..796ff294042 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -33,6 +33,14 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "lisp.h"
 #include "sysstdio.h"
 
+#ifdef HAVE_ANDROID
+#include "androidterm.h"
+#endif
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#include "sfntfont.h"
+#endif
+
 #ifdef WINDOWSNT
 #include <fcntl.h>
 #include <sys/socket.h>
@@ -137,6 +145,10 @@ extern char etext;
 #include <sys/resource.h>
 #endif
 
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#include "android.h"
+#endif
+
 /* We don't guard this with HAVE_TREE_SITTER because treesit.o is
    always compiled (to provide treesit-available-p).  */
 #include "treesit.h"
@@ -411,7 +423,15 @@ using_utf8 (void)
      the result is known in advance anyway...  */
 #if defined HAVE_WCHAR_H && !defined WINDOWSNT
   wchar_t wc;
+#ifndef HAVE_ANDROID
   mbstate_t mbs = { 0 };
+#else
+  mbstate_t mbs;
+
+  /* Not sure how mbstate works on Android, but this seems to be
+     required.  */
+  memset (&mbs, 0, sizeof mbs);
+#endif
   return mbrtowc (&wc, "\xc4\x80", 2, &mbs) == 2 && wc == 0x100;
 #else
   return false;
@@ -511,7 +531,8 @@ init_cmdargs (int argc, char **argv, int skip_args, char 
const *original_pwd)
     {
       Lisp_Object found;
       int yes = openp (Vexec_path, Vinvocation_name, Vexec_suffixes,
-                      &found, make_fixnum (X_OK), false, false);
+                      &found, make_fixnum (X_OK), false, false,
+                      NULL);
       if (yes == 1)
        {
          /* Add /: to the front of the name
@@ -724,6 +745,8 @@ argmatch (char **argv, int argc, const char *sstr, const 
char *lstr,
     }
 }
 
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+
 /* Find a name (absolute or relative) of the Emacs executable whose
    name (as passed into this program) is ARGV0.  Called early in
    initialization by portable dumper loading code, so avoid Lisp and
@@ -823,6 +846,8 @@ find_emacs_executable (char const *argv0, ptrdiff_t 
*candidate_size)
 #endif /* !WINDOWSNT */
 }
 
+#endif
+
 #ifdef HAVE_PDUMPER
 
 static const char *
@@ -851,10 +876,38 @@ dump_error_to_string (int result)
     }
 }
 
-/* This function returns the Emacs executable.  */
+/* This function returns the Emacs executable.  DUMP_FILE is ignored
+   outside of Android.  Otherwise, it is the name of the dump file to
+   use, or NULL if Emacs should look for a ``--dump-file'' argument
+   instead.  */
+
 static char *
-load_pdump (int argc, char **argv)
+load_pdump (int argc, char **argv, char *dump_file)
 {
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  int skip_args = 0, result;
+
+  while (skip_args < argc - 1)
+    {
+      if (argmatch (argv, argc, "-dump-file", "--dump-file",
+                   6, &dump_file, &skip_args)
+         || argmatch (argv, argc, "--", NULL, 2, NULL,
+                      &skip_args))
+       break;
+      skip_args++;
+    }
+
+  if (!dump_file)
+    return argv[0];
+
+  result = pdumper_load (dump_file, argv[0]);
+
+  if (result != PDUMPER_LOAD_SUCCESS)
+    fatal ("could not load dump file \"%s\": %s",
+          dump_file, dump_error_to_string (result));
+  return argv[0];
+#else
+
   const char *const suffix = ".pdmp";
   int result;
   char *emacs_executable = argv[0];
@@ -885,7 +938,7 @@ load_pdump (int argc, char **argv)
 
   /* Look for an explicitly-specified dump file.  */
   const char *path_exec = PATH_EXEC;
-  char *dump_file = NULL;
+  dump_file = NULL;
   int skip_args = 0;
   while (skip_args < argc - 1)
     {
@@ -1047,6 +1100,7 @@ load_pdump (int argc, char **argv)
   xfree (dump_file);
 
   return emacs_executable;
+#endif
 }
 #endif /* HAVE_PDUMPER */
 
@@ -1123,7 +1177,7 @@ load_seccomp (const char *file)
       goto out;
     }
   struct stat stat;
-  if (fstat (fd, &stat) != 0)
+  if (sys_fstat (fd, &stat) != 0)
     {
       emacs_perror ("fstat");
       goto out;
@@ -1225,12 +1279,24 @@ maybe_load_seccomp (int argc, char **argv)
 
 #endif  /* SECCOMP_USABLE */
 
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+int
+android_emacs_init (int argc, char **argv, char *dump_file)
+#else
 int
 main (int argc, char **argv)
+#endif
 {
   /* Variable near the bottom of the stack, and aligned appropriately
      for pointers.  */
   void *stack_bottom_variable;
+  int old_argc;
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+  char *dump_file;
+
+  /* This is just a dummy argument used to avoid extra defines.  */
+  dump_file = NULL;
+#endif
 
   /* First, check whether we should apply a seccomp filter.  This
      should come at the very beginning to allow the filter to protect
@@ -1360,7 +1426,7 @@ main (int argc, char **argv)
 
 #ifdef HAVE_PDUMPER
   if (attempt_load_pdump)
-    initial_emacs_executable = load_pdump (argc, argv);
+    initial_emacs_executable = load_pdump (argc, argv, dump_file);
 #else
   ptrdiff_t bufsize;
   initial_emacs_executable = find_emacs_executable (argv[0], &bufsize);
@@ -1425,8 +1491,9 @@ main (int argc, char **argv)
 
   bool only_version = false;
   sort_args (argc, argv);
-  argc = 0;
-  while (argv[argc]) argc++;
+  old_argc = argc, argc = 0;
+  /* Don't allow going past argv.  */
+  while (argc < old_argc && argv[argc]) argc++;
 
   skip_args = 0;
   if (argmatch (argv, argc, "-version", "--version", 3, NULL, &skip_args))
@@ -1937,6 +2004,9 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
 #ifdef HAVE_WINDOW_SYSTEM
       init_fringe_once ();     /* Swap bitmaps if necessary.  */
 #endif /* HAVE_WINDOW_SYSTEM */
+#ifdef HAVE_TEXT_CONVERSION
+      syms_of_textconv ();
+#endif
     }
 
   init_alloc ();
@@ -2379,6 +2449,18 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
 #endif
       syms_of_fontset ();
 #endif /* HAVE_HAIKU */
+#ifdef HAVE_ANDROID
+      syms_of_androidterm ();
+      syms_of_androidfns ();
+      syms_of_androidmenu ();
+      syms_of_fontset ();
+#if !defined ANDROID_STUBIFY
+      syms_of_androidfont ();
+      syms_of_androidselect ();
+      syms_of_sfntfont ();
+      syms_of_sfntfont_android ();
+#endif /* !ANDROID_STUBIFY */
+#endif /* HAVE_ANDROID */
 
       syms_of_gnutls ();
 
@@ -2479,6 +2561,17 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
   init_window ();
   init_font ();
 
+#ifdef HAVE_ANDROID
+  init_androidmenu ();
+#endif
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  init_androidfont ();
+  init_androidselect ();
+  init_sfntfont ();
+  init_sfntfont_android ();
+#endif
+
   if (!initialized)
     {
       char *file;
@@ -2533,6 +2626,16 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
   safe_run_hooks (Qafter_pdump_load_hook);
 #endif
 
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY && 0
+  /* This comes very late in the startup process because it requires
+     most of lisp/international to be loaded.  This approach doesn't
+     work because normal-top-level runs and creates the initial frame
+     before fonts are initialized.  So this is done in
+     normal-top-level instead.  */
+  Vtop_level = list3 (Qprogn, Vtop_level,
+                     list1 (Qandroid_enumerate_fonts));
+#endif
+
   /* Enter editor command loop.  This never returns.  */
   set_initial_minibuffer_mode ();
   Frecursive_edit ();
@@ -2858,7 +2961,14 @@ killed.  */
 #ifndef WINDOWSNT
   /* Do some checking before shutting down Emacs, because errors
      can't be meaningfully reported afterwards.  */
-  if (!NILP (restart))
+  if (!NILP (restart)
+      /* Don't perform the following checks when Emacs is running as
+        an Android GUI application, because there the system is
+        relied on to restart Emacs.  */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+      && !android_init_gui
+#endif
+      )
     {
       /* This is very unlikely, but it's possible to execute a binary
         (on some systems) with no argv.  */
@@ -2910,7 +3020,7 @@ killed.  */
     {
       Lisp_Object listfile;
       listfile = Fexpand_file_name (Vauto_save_list_file_name, Qnil);
-      unlink (SSDATA (listfile));
+      emacs_unlink (SSDATA (listfile));
     }
 
 #ifdef HAVE_NATIVE_COMP
@@ -2920,6 +3030,13 @@ killed.  */
   if (!NILP (restart))
     {
       turn_on_atimers (false);
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+      /* Re-executing the Emacs process created by the system doesn't
+        work.  Instead, schedule a restart for a few hundered
+        milliseconds and exit Emacs.  */
+      if (android_init_gui)
+       android_restart_emacs ();
+#endif
 #ifdef WINDOWSNT
       if (w32_reexec_emacs (initial_cmdline, initial_wd) < 0)
 #else
@@ -2960,31 +3077,38 @@ shut_down_emacs (int sig, Lisp_Object stuff)
   Vinhibit_redisplay = Qt;
 
   /* If we are controlling the terminal, reset terminal modes.  */
-#ifndef DOS_NT
+#if !defined DOS_NT && !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
   pid_t tpgrp = tcgetpgrp (STDIN_FILENO);
   if (tpgrp != -1 && tpgrp == getpgrp ())
     {
       reset_all_sys_modes ();
       if (sig && sig != SIGTERM)
        {
-         static char const fmt[] = "Fatal error %d: %n%s\n";
 #ifdef HAVE_HAIKU
          if (haiku_debug_on_fatal_error)
            debugger ("Fatal error in Emacs");
 #endif
-         char buf[max ((sizeof fmt - sizeof "%d%n%s\n"
+         /* Output a "Fatal error NUM: DESC\n" diagnostic with a single write,
+            but use multiple writes if the diagnosic is absurdly long
+            and likely couldn't be written atomically anyway.  */
+         static char const fmt[] = "Fatal error %d: ";
+         char buf[max ((sizeof fmt - sizeof "%d"
                         + INT_STRLEN_BOUND (int) + 1),
                        min (PIPE_BUF, MAX_ALLOCA))];
          char const *sig_desc = safe_strsignal (sig);
-         int nlen;
-         int buflen = snprintf (buf, sizeof buf, fmt, sig, &nlen, sig_desc);
-         if (0 <= buflen && buflen < sizeof buf)
-           emacs_write (STDERR_FILENO, buf, buflen);
+         size_t sig_desclen = strlen (sig_desc);
+         int nlen = sprintf (buf, fmt, sig);
+         if (nlen + sig_desclen < sizeof buf - 1)
+           {
+             char *p = mempcpy (buf + nlen, sig_desc, sig_desclen);
+             *p++ = '\n';
+             emacs_write (STDERR_FILENO, buf, p - buf);
+           }
          else
            {
              emacs_write (STDERR_FILENO, buf, nlen);
-             emacs_write (STDERR_FILENO, sig_desc, strlen (sig_desc));
-             emacs_write (STDERR_FILENO, fmt + sizeof fmt - 2, 1);
+             emacs_write (STDERR_FILENO, sig_desc, sig_desclen);
+             emacs_write (STDERR_FILENO, "\n", 1);
            }
        }
     }
@@ -3479,6 +3603,7 @@ Special values:
   `windows-nt'   compiled as a native W32 application.
   `cygwin'       compiled using the Cygwin library.
   `haiku'        compiled for a Haiku system.
+  `android'     compiled for Android.
 Anything else (in Emacs 26, the possibilities are: aix, berkeley-unix,
 hpux, usg-unix-v) indicates some sort of Unix system.  */);
   Vsystem_type = intern_c_string (SYSTEM_TYPE);
diff --git a/src/epaths.in b/src/epaths.in
index b290f0243bc..afddd57763a 100644
--- a/src/epaths.in
+++ b/src/epaths.in
@@ -18,6 +18,7 @@ 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/>.  */
 
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
 
 /* Together with PATH_SITELOADSEARCH, this gives the default value of
    load-path, which is the search path for the Lisp function "load".
@@ -79,3 +80,24 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 /* Where Emacs should look for the application default file. */
 #define PATH_X_DEFAULTS 
"/usr/lib/X11/%L/%T/%N%C%S:/usr/lib/X11/%l/%T/%N%C%S:/usr/lib/X11/%T/%N%C%S:/usr/lib/X11/%L/%T/%N%S:/usr/lib/X11/%l/%T/%N%S:/usr/lib/X11/%T/%N%S"
+
+#else
+
+/* Replace the defines above with links to files in the assets
+   pseudo-directory.  Preserve the extra spaces, or epaths.in will not
+   be generated correctly.  */
+
+ # define  PATH_EXEC                   (android_lib_dir)
+ # define  PATH_LOADSEARCH             "/assets/lisp/"
+ # define  PATH_SITELOADSEARCH         (android_site_load_path)
+ # define  PATH_DUMPLOADSEARCH         "/assets/lisp/"
+ # define  PATH_DATA                   "/assets/etc/"
+ # define  PATH_DOC                    "/assets/etc/"
+ # define  PATH_INFO                   "/assets/info/"
+ # define  PATH_GAME                   ""
+ # define  PATH_BITMAPS                        ""
+
+extern char *android_site_load_path;
+extern char *android_lib_dir;
+
+#endif
diff --git a/src/fileio.c b/src/fileio.c
index 663d89b9dfc..290481c2b5d 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -56,6 +56,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "region-cache.h"
 #include "frame.h"
 
+#ifdef HAVE_ANDROID
+#include "android.h"
+#endif /* HAVE_ANDROID */
+
 #ifdef HAVE_LINUX_FS_H
 # include <sys/ioctl.h>
 # include <linux/fs.h>
@@ -109,6 +113,42 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 #include "commands.h"
 
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+
+/* Type describing a file descriptor used by functions such as
+   `insert-file-contents'.  */
+
+typedef int emacs_fd;
+
+/* Function used to read and open from such a file descriptor.  */
+
+#define emacs_fd_open          emacs_open
+#define emacs_fd_close         emacs_close
+#define emacs_fd_read          emacs_read_quit
+#define emacs_fd_lseek         lseek
+#define emacs_fd_fstat         sys_fstat
+#define emacs_fd_valid_p(fd)   ((fd) >= 0)
+
+/* This is not used on MS Windows.  */
+
+#ifndef WINDOWSNT
+#define emacs_fd_to_int(fds)   (fds)
+#endif /* WINDOWSNT */
+
+#else /* HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
+typedef struct android_fd_or_asset emacs_fd;
+
+#define emacs_fd_open          android_open_asset
+#define emacs_fd_close         android_close_asset
+#define emacs_fd_read          android_asset_read_quit
+#define emacs_fd_lseek         android_asset_lseek
+#define emacs_fd_fstat         android_asset_fstat
+#define emacs_fd_valid_p(fd)   ((fd).asset != ((void *) -1))
+#define emacs_fd_to_int(fds)   ((fds).asset ? -1 : (fds).fd)
+
+#endif /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
+
 /* True during writing of auto-save files.  */
 static bool auto_saving;
 
@@ -135,13 +175,32 @@ static dev_t timestamp_file_system;
 static Lisp_Object Vwrite_region_annotation_buffers;
 
 static Lisp_Object emacs_readlinkat (int, char const *);
-static Lisp_Object file_name_directory (Lisp_Object);
 static bool a_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t,
                     Lisp_Object *, struct coding_system *);
 static bool e_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t,
                     struct coding_system *);
 
 
+
+/* Establish that ENCODED is not contained within a special directory
+   whose contents are not eligible for Unix VFS operations.  Signal a
+   `file-error' with REASON if it does.  */
+
+static void
+check_vfs_filename (Lisp_Object encoded, const char *reason)
+{
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  const char *name;
+
+  name = SSDATA (encoded);
+
+  if (android_is_special_directory (name, "/assets")
+      || android_is_special_directory (name, "/content"))
+    xsignal2 (Qfile_error, build_string (reason), encoded);
+#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+}
+
+
 /* Test whether FILE is accessible for AMODE.
    Return true if successful, false (setting errno) otherwise.  */
 
@@ -160,7 +219,7 @@ file_access_p (char const *file, int amode)
     }
 #endif
 
-  if (faccessat (AT_FDCWD, file, amode, AT_EACCESS) == 0)
+  if (sys_faccessat (AT_FDCWD, file, amode, AT_EACCESS) == 0)
     return true;
 
 #ifdef CYGWIN
@@ -264,11 +323,20 @@ close_file_unwind (int fd)
   emacs_close (fd);
 }
 
+static void
+close_file_unwind_emacs_fd (void *ptr)
+{
+  emacs_fd *fd;
+
+  fd = ptr;
+  emacs_fd_close (*fd);
+}
+
 void
 fclose_unwind (void *arg)
 {
   FILE *stream = arg;
-  fclose (stream);
+  emacs_fclose (stream);
 }
 
 /* Restore point, having saved it as a marker.  */
@@ -370,7 +438,7 @@ Given a Unix syntax file name, returns a string ending in 
slash.  */)
 /* Return the directory component of FILENAME, or nil if FILENAME does
    not contain a directory component.  */
 
-static Lisp_Object
+Lisp_Object
 file_name_directory (Lisp_Object filename)
 {
   char *beg = SSDATA (filename);
@@ -889,6 +957,10 @@ user_homedir (char const *name)
   p[length] = 0;
   struct passwd *pw = getpwnam (p);
   SAFE_FREE ();
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  if (pw && !pw->pw_dir && pw->pw_uid == getuid ())
+    return (char *) android_get_home_directory ();
+#endif
   if (!pw || (pw->pw_dir && !IS_ABSOLUTE_FILE_NAME (pw->pw_dir)))
     return NULL;
   return pw->pw_dir;
@@ -1879,6 +1951,11 @@ get_homedir (void)
        pw = getpwuid (getuid ());
       if (pw)
        home = pw->pw_dir;
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+      if (!home && pw && pw->pw_uid == getuid ())
+       return android_get_home_directory ();
+#endif
       if (!home)
        return "";
     }
@@ -2177,7 +2254,8 @@ permissions.  */)
 #else
   bool already_exists = false;
   mode_t new_mask;
-  int ifd, ofd;
+  emacs_fd ifd;
+  int ofd;
   struct stat st;
 #endif
 
@@ -2220,22 +2298,24 @@ permissions.  */)
       report_file_error ("Copying permissions to", newname);
     }
 #else /* not WINDOWSNT */
-  ifd = emacs_open (SSDATA (encoded_file), O_RDONLY | O_NONBLOCK, 0);
+  ifd = emacs_fd_open (SSDATA (encoded_file), O_RDONLY | O_NONBLOCK, 0);
 
-  if (ifd < 0)
+  if (!emacs_fd_valid_p (ifd))
     report_file_error ("Opening input file", file);
 
-  record_unwind_protect_int (close_file_unwind, ifd);
+  record_unwind_protect_ptr (close_file_unwind_emacs_fd, &ifd);
 
-  if (fstat (ifd, &st) != 0)
+  if (emacs_fd_fstat (ifd, &st) != 0)
     report_file_error ("Input file status", file);
 
   if (!NILP (preserve_permissions))
     {
 #if HAVE_LIBSELINUX
-      if (is_selinux_enabled ())
+      if (is_selinux_enabled ()
+         && emacs_fd_to_int (ifd) != -1)
        {
-         conlength = fgetfilecon (ifd, &con);
+         conlength = fgetfilecon (emacs_fd_to_int (ifd),
+                                  &con);
          if (conlength == -1)
            report_file_error ("Doing fgetfilecon", file);
        }
@@ -2273,7 +2353,7 @@ permissions.  */)
   if (already_exists)
     {
       struct stat out_st;
-      if (fstat (ofd, &out_st) != 0)
+      if (sys_fstat (ofd, &out_st) != 0)
        report_file_error ("Output file status", newname);
       if (st.st_dev == out_st.st_dev && st.st_ino == out_st.st_ino)
        report_file_errno ("Input and output files are the same",
@@ -2284,7 +2364,8 @@ permissions.  */)
 
   maybe_quit ();
 
-  if (clone_file (ofd, ifd))
+  if (emacs_fd_to_int (ifd) != -1
+      && clone_file (ofd, emacs_fd_to_int (ifd)))
     newsize = st.st_size;
   else
     {
@@ -2292,30 +2373,38 @@ permissions.  */)
       ssize_t copied;
 
 #ifndef MSDOS
-      for (newsize = 0; newsize < insize; newsize += copied)
+      newsize = 0;
+
+      if (emacs_fd_to_int (ifd) != -1)
        {
-         /* Copy at most COPY_MAX bytes at a time; this is min
-            (PTRDIFF_MAX, SIZE_MAX) truncated to a value that is
-            surely aligned well.  */
-         ssize_t ssize_max = TYPE_MAXIMUM (ssize_t);
-         ptrdiff_t copy_max = min (ssize_max, SIZE_MAX) >> 30 << 30;
-         off_t intail = insize - newsize;
-         ptrdiff_t len = min (intail, copy_max);
-         copied = copy_file_range (ifd, NULL, ofd, NULL, len, 0);
-         if (copied <= 0)
-           break;
-         maybe_quit ();
+         for (; newsize < insize; newsize += copied)
+           {
+             /* Copy at most COPY_MAX bytes at a time; this is min
+                (PTRDIFF_MAX, SIZE_MAX) truncated to a value that is
+                surely aligned well.  */
+             ssize_t ssize_max = TYPE_MAXIMUM (ssize_t);
+             ptrdiff_t copy_max = min (ssize_max, SIZE_MAX) >> 30 << 30;
+             off_t intail = insize - newsize;
+             ptrdiff_t len = min (intail, copy_max);
+             copied = copy_file_range (emacs_fd_to_int (ifd), NULL,
+                                       ofd, NULL, len, 0);
+             if (copied <= 0)
+               break;
+             maybe_quit ();
+           }
        }
 #endif /* MSDOS */
 
       /* Fall back on read+write if copy_file_range failed, or if the
-        input is empty and so could be a /proc file.  read+write will
-        either succeed, or report an error more precisely than
-        copy_file_range would.  */
+        input is empty and so could be a /proc file, or if ifd is an
+        invention of android.c.  read+write will either succeed, or
+        report an error more precisely than copy_file_range
+        would.  */
       if (newsize != insize || insize == 0)
        {
          char buf[MAX_ALLOCA];
-         for (; (copied = emacs_read_quit (ifd, buf, sizeof buf));
+
+         for (; (copied = emacs_fd_read (ifd, buf, sizeof buf));
               newsize += copied)
            {
              if (copied < 0)
@@ -2363,8 +2452,10 @@ permissions.  */)
          }
       }
 
-    switch (!NILP (preserve_permissions)
-           ? qcopy_acl (SSDATA (encoded_file), ifd,
+    switch ((!NILP (preserve_permissions)
+            && emacs_fd_to_int (ifd) != -1)
+           ? qcopy_acl (SSDATA (encoded_file),
+                        emacs_fd_to_int (ifd),
                         SSDATA (encoded_newname), ofd,
                         preserved_permissions)
            : (already_exists
@@ -2396,7 +2487,17 @@ permissions.  */)
       struct timespec ts[2];
       ts[0] = get_stat_atime (&st);
       ts[1] = get_stat_mtime (&st);
-      if (futimens (ofd, ts) != 0)
+      if (futimens (ofd, ts) != 0
+         /* Various versions of the Android C library are missing
+            futimens, which leads a gnulib fallback to be installed
+            that uses fdutimens instead.  However, fdutimens is not
+            supported on many Android kernels, so just silently fail
+            if errno is ENOTSUP or ENOSYS.  */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+         && errno != ENOTSUP
+         && errno != ENOSYS
+#endif
+         )
        xsignal2 (Qfile_date_error,
                  build_string ("Cannot set file date"), newname);
     }
@@ -2404,7 +2505,9 @@ permissions.  */)
   if (emacs_close (ofd) < 0)
     report_file_error ("Write error", newname);
 
-  emacs_close (ifd);
+  /* Note that ifd is not closed twice because unwind_protects are
+     discarded at the end of this function.  */
+  emacs_fd_close (ifd);
 
 #ifdef MSDOS
   /* In DJGPP v2.0 and later, fstat usually returns true file mode bits,
@@ -2437,7 +2540,7 @@ DEFUN ("make-directory-internal", 
Fmake_directory_internal,
 
   dir = SSDATA (encoded_dir);
 
-  if (mkdir (dir, 0777 & ~auto_saving_dir_umask) != 0)
+  if (emacs_mkdir (dir, 0777 & ~auto_saving_dir_umask) != 0)
     report_file_error ("Creating directory", directory);
 
   return Qnil;
@@ -2456,47 +2559,25 @@ DEFUN ("delete-directory-internal", 
Fdelete_directory_internal,
   encoded_dir = ENCODE_FILE (directory);
   dir = SSDATA (encoded_dir);
 
-  if (rmdir (dir) != 0)
+  if (emacs_rmdir (dir) != 0)
     report_file_error ("Removing directory", directory);
 
   return Qnil;
 }
 
-DEFUN ("delete-file", Fdelete_file, Sdelete_file, 1, 2,
-       "(list (read-file-name \
-                (if (and delete-by-moving-to-trash (null current-prefix-arg)) \
-                    \"Move file to trash: \" \"Delete file: \") \
-                nil default-directory (confirm-nonexistent-file-or-buffer)) \
-              (null current-prefix-arg))",
-       doc: /* Delete file named FILENAME.  If it is a symlink, remove the 
symlink.
-If file has multiple names, it continues to exist with the other names.
-TRASH non-nil means to trash the file instead of deleting, provided
-`delete-by-moving-to-trash' is non-nil.
-
-When called interactively, TRASH is t if no prefix argument is given.
-With a prefix argument, TRASH is nil.  */)
-  (Lisp_Object filename, Lisp_Object trash)
+DEFUN ("delete-file-internal", Fdelete_file_internal, Sdelete_file_internal, 
1, 1, 0,
+       doc: /* Delete file named FILENAME; internal use only.
+If it is a symlink, remove the symlink.
+If file has multiple names, it continues to exist with the other names. */)
+  (Lisp_Object filename)
 {
-  Lisp_Object handler;
   Lisp_Object encoded_file;
 
-  if (!NILP (Ffile_directory_p (filename))
-      && NILP (Ffile_symlink_p (filename)))
-    xsignal2 (Qfile_error,
-             build_string ("Removing old name: is a directory"),
-             filename);
   filename = Fexpand_file_name (filename, Qnil);
-
-  handler = Ffind_file_name_handler (filename, Qdelete_file);
-  if (!NILP (handler))
-    return call3 (handler, Qdelete_file, filename, trash);
-
-  if (delete_by_moving_to_trash && !NILP (trash))
-    return call1 (Qmove_file_to_trash, filename);
-
   encoded_file = ENCODE_FILE (filename);
 
-  if (unlink (SSDATA (encoded_file)) != 0 && errno != ENOENT)
+  if (emacs_unlink (SSDATA (encoded_file)) != 0
+      && errno != ENOENT)
     report_file_error ("Removing old name", filename);
   return Qnil;
 }
@@ -2517,7 +2598,7 @@ internal_delete_file (Lisp_Object filename)
 {
   Lisp_Object tem;
 
-  tem = internal_condition_case_2 (Fdelete_file, filename, Qnil,
+  tem = internal_condition_case_2 (Fdelete_file_internal, filename,
                                   Qt, internal_delete_file_1);
   return NILP (tem);
 }
@@ -2659,8 +2740,10 @@ This is what happens in interactive use with M-x.  */)
   int rename_errno UNINIT;
   if (!plain_rename)
     {
-      if (renameat_noreplace (AT_FDCWD, SSDATA (encoded_file),
-                             AT_FDCWD, SSDATA (encoded_newname))
+      if (emacs_renameat_noreplace (AT_FDCWD,
+                                   SSDATA (encoded_file),
+                                   AT_FDCWD,
+                                   SSDATA (encoded_newname))
          == 0)
        return Qnil;
 
@@ -2682,7 +2765,8 @@ This is what happens in interactive use with M-x.  */)
 
   if (plain_rename)
     {
-      if (rename (SSDATA (encoded_file), SSDATA (encoded_newname)) == 0)
+      if (emacs_rename (SSDATA (encoded_file),
+                       SSDATA (encoded_newname)) == 0)
        return Qnil;
       rename_errno = errno;
       /* Don't prompt again.  */
@@ -2725,7 +2809,7 @@ This is what happens in interactive use with M-x.  */)
   if (dirp)
     call2 (Qdelete_directory, file, Qt);
   else
-    Fdelete_file (file, Qnil);
+    call2 (Qdelete_file, file, Qnil);
   return unbind_to (count, Qnil);
 }
 
@@ -2763,6 +2847,10 @@ This is what happens in interactive use with M-x.  */)
 
   encoded_file = ENCODE_FILE (file);
   encoded_newname = ENCODE_FILE (newname);
+  check_vfs_filename (encoded_file, "Trying to create hard link to "
+                     "file within special directory");
+  check_vfs_filename (encoded_newname, "Trying to create hard link"
+                     " within special directory");
 
   if (link (SSDATA (encoded_file), SSDATA (encoded_newname)) == 0)
     return Qnil;
@@ -2773,7 +2861,7 @@ This is what happens in interactive use with M-x.  */)
          || FIXNUMP (ok_if_already_exists))
        barf_or_query_if_file_exists (newname, true, "make it a new name",
                                      FIXNUMP (ok_if_already_exists), false);
-      unlink (SSDATA (newname));
+      emacs_unlink (SSDATA (newname));
       if (link (SSDATA (encoded_file), SSDATA (encoded_newname)) == 0)
        return Qnil;
     }
@@ -2817,7 +2905,8 @@ This happens for interactive use with M-x.  */)
   encoded_target = ENCODE_FILE (target);
   encoded_linkname = ENCODE_FILE (linkname);
 
-  if (symlink (SSDATA (encoded_target), SSDATA (encoded_linkname)) == 0)
+  if (emacs_symlink (SSDATA (encoded_target),
+                    SSDATA (encoded_linkname)) == 0)
     return Qnil;
 
   if (errno == ENOSYS)
@@ -2830,8 +2919,9 @@ This happens for interactive use with M-x.  */)
          || FIXNUMP (ok_if_already_exists))
        barf_or_query_if_file_exists (linkname, true, "make it a link",
                                      FIXNUMP (ok_if_already_exists), false);
-      unlink (SSDATA (encoded_linkname));
-      if (symlink (SSDATA (encoded_target), SSDATA (encoded_linkname)) == 0)
+      emacs_unlink (SSDATA (encoded_linkname));
+      if (emacs_symlink (SSDATA (encoded_target),
+                        SSDATA (encoded_linkname)) == 0)
        return Qnil;
     }
 
@@ -2971,7 +3061,8 @@ If there is no error, returns nil.  */)
 
   encoded_filename = ENCODE_FILE (absname);
 
-  if (faccessat (AT_FDCWD, SSDATA (encoded_filename), R_OK, AT_EACCESS) != 0)
+  if (sys_faccessat (AT_FDCWD, SSDATA (encoded_filename), R_OK,
+                    AT_EACCESS) != 0)
     report_file_error (SSDATA (string), filename);
 
   return Qnil;
@@ -3073,12 +3164,13 @@ file_directory_p (Lisp_Object file)
 {
 #ifdef DOS_NT
   /* This is cheaper than 'stat'.  */
-  bool retval = faccessat (AT_FDCWD, SSDATA (file), D_OK, AT_EACCESS) == 0;
+  bool retval = sys_faccessat (AT_FDCWD, SSDATA (file),
+                              D_OK, AT_EACCESS) == 0;
   if (!retval && errno == EACCES)
     errno = ENOTDIR;   /* like the non-DOS_NT branch below does */
   return retval;
 #else
-# ifdef O_PATH
+# if defined O_PATH && !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
   /* Use O_PATH if available, as it avoids races and EOVERFLOW issues.  */
   int fd = emacs_openat (AT_FDCWD, SSDATA (file),
                         O_PATH | O_CLOEXEC | O_DIRECTORY, 0);
@@ -3187,7 +3279,11 @@ file_accessible_directory_p (Lisp_Object file)
      There are three exceptions: "", "/", and "//".  Leave "" alone,
      as it's invalid.  Append only "." to the other two exceptions as
      "/" and "//" are distinct on some platforms, whereas "/", "///",
-     "////", etc. are all equivalent.  */
+     "////", etc. are all equivalent.
+
+     Android has a special directory named "/assets".  There is no "."
+     directory there, but appending a "/" is sufficient to check
+     whether or not it is a directory.  */
   if (! len)
     dir = data;
   else
@@ -3197,6 +3293,7 @@ file_accessible_directory_p (Lisp_Object file)
         special cases "/" and "//", and it's a safe optimization
         here.  After appending '.', append another '/' to work around
         a macOS bug (Bug#30350).  */
+
       static char const appended[] = "/./";
       char *buf = SAFE_ALLOCA (len + sizeof appended);
       memcpy (buf, data, len);
@@ -3521,6 +3618,8 @@ Interactively, prompt for FILENAME, and read MODE with
 command from GNU Coreutils.  */)
   (Lisp_Object filename, Lisp_Object mode, Lisp_Object flag)
 {
+  Lisp_Object encoded;
+
   CHECK_FIXNUM (mode);
   int nofollow = symlink_nofollow_flag (flag);
   Lisp_Object absname = Fexpand_file_name (filename,
@@ -3532,9 +3631,10 @@ command from GNU Coreutils.  */)
   if (!NILP (handler))
     return call4 (handler, Qset_file_modes, absname, mode, flag);
 
-  char *fname = SSDATA (ENCODE_FILE (absname));
+  encoded = ENCODE_FILE (absname);
+  char *fname = SSDATA (encoded);
   mode_t imode = XFIXNUM (mode) & 07777;
-  if (fchmodat (AT_FDCWD, fname, imode, nofollow) != 0)
+  if (emacs_fchmodat (AT_FDCWD, fname, imode, nofollow) != 0)
     report_file_error ("Doing chmod", absname);
 
   return Qnil;
@@ -3551,7 +3651,9 @@ in the permissions of newly created files will be 
disabled.
 
 Note that when `write-region' creates a file, it resets the
 execute bit, even if the mask set by this function allows that bit
-by having the corresponding bit in the mask reset.  */)
+by having the corresponding bit in the mask reset.
+
+See also `with-file-modes'.  */)
   (Lisp_Object mode)
 {
   mode_t oldrealmask, oldumask, newumask;
@@ -3604,6 +3706,8 @@ TIMESTAMP is in the format of `current-time'. */)
     return call4 (handler, Qset_file_times, absname, timestamp, flag);
 
   Lisp_Object encoded_absname = ENCODE_FILE (absname);
+  check_vfs_filename (encoded_absname, "Trying to set access times of"
+                     " file within special directory");
 
   if (utimensat (AT_FDCWD, SSDATA (encoded_absname), ts, nofollow) != 0)
     {
@@ -3636,6 +3740,7 @@ otherwise, if FILE2 does not exist, the answer is t.  */)
   (Lisp_Object file1, Lisp_Object file2)
 {
   struct stat st1, st2;
+  Lisp_Object encoded;
 
   CHECK_STRING (file1);
   CHECK_STRING (file2);
@@ -3652,8 +3757,10 @@ otherwise, if FILE2 does not exist, the answer is t.  */)
   if (!NILP (handler))
     return call3 (handler, Qfile_newer_than_file_p, absname1, absname2);
 
+  encoded = ENCODE_FILE (absname1);
+
   int err1;
-  if (emacs_fstatat (AT_FDCWD, SSDATA (ENCODE_FILE (absname1)), &st1, 0) == 0)
+  if (emacs_fstatat (AT_FDCWD, SSDATA (encoded), &st1, 0) == 0)
     err1 = 0;
   else
     {
@@ -3742,7 +3849,7 @@ union read_non_regular
 {
   struct
   {
-    int fd;
+    emacs_fd fd;
     ptrdiff_t inserted, trytry;
   } s;
   GCALIGNED_UNION_MEMBER
@@ -3753,10 +3860,10 @@ static Lisp_Object
 read_non_regular (Lisp_Object state)
 {
   union read_non_regular *data = XFIXNUMPTR (state);
-  int nbytes = emacs_read_quit (data->s.fd,
-                               ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
-                                + data->s.inserted),
-                               data->s.trytry);
+  int nbytes = emacs_fd_read (data->s.fd,
+                             ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
+                              + data->s.inserted),
+                             data->s.trytry);
   return make_fixnum (nbytes);
 }
 
@@ -3905,7 +4012,7 @@ by calling `format-decode', which see.  */)
 {
   struct stat st;
   struct timespec mtime;
-  int fd;
+  emacs_fd fd;
   ptrdiff_t inserted = 0;
   int unprocessed;
   specpdl_ref count = SPECPDL_INDEX ();
@@ -3983,8 +4090,8 @@ by calling `format-decode', which see.  */)
   orig_filename = filename;
   filename = ENCODE_FILE (filename);
 
-  fd = emacs_open (SSDATA (filename), O_RDONLY, 0);
-  if (fd < 0)
+  fd = emacs_fd_open (SSDATA (filename), O_RDONLY, 0);
+  if (!emacs_fd_valid_p (fd))
     {
       save_errno = errno;
       if (NILP (visit))
@@ -4002,7 +4109,7 @@ by calling `format-decode', which see.  */)
     }
 
   specpdl_ref fd_index = SPECPDL_INDEX ();
-  record_unwind_protect_int (close_file_unwind, fd);
+  record_unwind_protect_ptr (close_file_unwind_emacs_fd, &fd);
 
   /* Replacement should preserve point as it preserves markers.  */
   if (!NILP (replace))
@@ -4012,7 +4119,7 @@ by calling `format-decode', which see.  */)
                             XCAR (XCAR (window_markers)));
     }
 
-  if (fstat (fd, &st) != 0)
+  if (emacs_fd_fstat (fd, &st) != 0)
     report_file_error ("Input file status", orig_filename);
   mtime = get_stat_mtime (&st);
 
@@ -4033,7 +4140,7 @@ by calling `format-decode', which see.  */)
        xsignal2 (Qfile_error,
                  build_string ("not a regular file"), orig_filename);
 
-      seekable = lseek (fd, 0, SEEK_CUR) < 0;
+      seekable = emacs_fd_lseek (fd, 0, SEEK_CUR) < 0;
       if (!NILP (beg) && !seekable)
        xsignal2 (Qfile_error,
                  build_string ("cannot use a start position in a non-seekable 
file/device"),
@@ -4112,17 +4219,17 @@ by calling `format-decode', which see.  */)
              int nread;
 
              if (st.st_size <= (1024 * 4))
-               nread = emacs_read_quit (fd, read_buf, 1024 * 4);
+               nread = emacs_fd_read (fd, read_buf, 1024 * 4);
              else
                {
-                 nread = emacs_read_quit (fd, read_buf, 1024);
+                 nread = emacs_fd_read (fd, read_buf, 1024);
                  if (nread == 1024)
                    {
                      int ntail;
-                     if (lseek (fd, st.st_size - 1024 * 3, SEEK_CUR) < 0)
+                     if (emacs_fd_lseek (fd, st.st_size - 1024 * 3, SEEK_CUR) 
< 0)
                        report_file_error ("Setting file position",
                                           orig_filename);
-                     ntail = emacs_read_quit (fd, read_buf + nread, 1024 * 3);
+                     ntail = emacs_fd_read (fd, read_buf + nread, 1024 * 3);
                      nread = ntail < 0 ? ntail : nread + ntail;
                    }
                }
@@ -4163,7 +4270,7 @@ by calling `format-decode', which see.  */)
                  specpdl_ptr--;
 
                  /* Rewind the file for the actual read done later.  */
-                 if (lseek (fd, 0, SEEK_SET) < 0)
+                 if (emacs_fd_lseek (fd, 0, SEEK_SET) < 0)
                    report_file_error ("Setting file position", orig_filename);
                }
            }
@@ -4222,7 +4329,7 @@ by calling `format-decode', which see.  */)
 
       if (beg_offset != 0)
        {
-         if (lseek (fd, beg_offset, SEEK_SET) < 0)
+         if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0)
            report_file_error ("Setting file position", orig_filename);
        }
 
@@ -4230,7 +4337,7 @@ by calling `format-decode', which see.  */)
         match the text at the beginning of the buffer.  */
       while (true)
        {
-         int nread = emacs_read_quit (fd, read_buf, sizeof read_buf);
+         int nread = emacs_fd_read (fd, read_buf, sizeof read_buf);
          if (nread < 0)
            report_file_error ("Read error", orig_filename);
          else if (nread == 0)
@@ -4265,7 +4372,7 @@ by calling `format-decode', which see.  */)
         there's no need to replace anything.  */
       if (same_at_start - BEGV_BYTE == end_offset - beg_offset)
        {
-         emacs_close (fd);
+         emacs_fd_close (fd);
          clear_unwind_protect (fd_index);
 
          /* Truncate the buffer to the size of the file.  */
@@ -4288,14 +4395,14 @@ by calling `format-decode', which see.  */)
            break;
          /* How much can we scan in the next step?  */
          trial = min (curpos, sizeof read_buf);
-         if (lseek (fd, curpos - trial, SEEK_SET) < 0)
+         if (emacs_fd_lseek (fd, curpos - trial, SEEK_SET) < 0)
            report_file_error ("Setting file position", orig_filename);
 
          total_read = nread = 0;
          while (total_read < trial)
            {
-             nread = emacs_read_quit (fd, read_buf + total_read,
-                                      trial - total_read);
+             nread = emacs_fd_read (fd, read_buf + total_read,
+                                    trial - total_read);
              if (nread < 0)
                report_file_error ("Read error", orig_filename);
              else if (nread == 0)
@@ -4415,7 +4522,7 @@ by calling `format-decode', which see.  */)
       /* First read the whole file, performing code conversion into
         CONVERSION_BUFFER.  */
 
-      if (lseek (fd, beg_offset, SEEK_SET) < 0)
+      if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0)
        report_file_error ("Setting file position", orig_filename);
 
       inserted = 0;            /* Bytes put into CONVERSION_BUFFER so far.  */
@@ -4426,8 +4533,8 @@ by calling `format-decode', which see.  */)
          /* Read at most READ_BUF_SIZE bytes at a time, to allow
             quitting while reading a huge file.  */
 
-         this = emacs_read_quit (fd, read_buf + unprocessed,
-                                 READ_BUF_SIZE - unprocessed);
+         this = emacs_fd_read (fd, read_buf + unprocessed,
+                               READ_BUF_SIZE - unprocessed);
          if (this <= 0)
            break;
 
@@ -4442,7 +4549,7 @@ by calling `format-decode', which see.  */)
 
       if (this < 0)
        report_file_error ("Read error", orig_filename);
-      emacs_close (fd);
+      emacs_fd_close (fd);
       clear_unwind_protect (fd_index);
 
       if (unprocessed > 0)
@@ -4591,7 +4698,7 @@ by calling `format-decode', which see.  */)
 
   if (beg_offset != 0 || !NILP (replace))
     {
-      if (lseek (fd, beg_offset, SEEK_SET) < 0)
+      if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0)
        report_file_error ("Setting file position", orig_filename);
     }
 
@@ -4644,10 +4751,10 @@ by calling `format-decode', which see.  */)
            /* Allow quitting out of the actual I/O.  We don't make text
               part of the buffer until all the reading is done, so a C-g
               here doesn't do any harm.  */
-           this = emacs_read_quit (fd,
-                                   ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
-                                    + inserted),
-                                   trytry);
+           this = emacs_fd_read (fd,
+                                 ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
+                                  + inserted),
+                                 trytry);
          }
 
        if (this <= 0)
@@ -4674,7 +4781,7 @@ by calling `format-decode', which see.  */)
   else
     Fset (Qdeactivate_mark, Qt);
 
-  emacs_close (fd);
+  emacs_fd_close (fd);
   clear_unwind_protect (fd_index);
 
   if (read_quit < 0)
@@ -4853,8 +4960,10 @@ by calling `format-decode', which see.  */)
        }
     }
 
-  /* Decode file format.  */
-  if (inserted > 0)
+  /* Decode file format.  Don't do this if Qformat_decode is not
+     bound, which can happen when called early during loadup.  */
+
+  if (inserted > 0 && !NILP (Ffboundp (Qformat_decode)))
     {
       /* Don't run point motion or modification hooks when decoding.  */
       specpdl_ref count1 = SPECPDL_INDEX ();
@@ -5299,6 +5408,7 @@ write_region (Lisp_Object start, Lisp_Object end, 
Lisp_Object filename,
     }
 
   encoded_filename = ENCODE_FILE (filename);
+
   fn = SSDATA (encoded_filename);
   open_flags = O_WRONLY | O_CREAT;
   open_flags |= EQ (mustbenew, Qexcl) ? O_EXCL : !NILP (append) ? 0 : O_TRUNC;
@@ -5385,7 +5495,7 @@ write_region (Lisp_Object start, Lisp_Object end, 
Lisp_Object filename,
   modtime = invalid_timespec ();
   if (visiting)
     {
-      if (fstat (desc, &st) == 0)
+      if (sys_fstat (desc, &st) == 0)
        modtime = get_stat_mtime (&st);
       else
        ok = 0, save_errno = errno;
@@ -5423,7 +5533,7 @@ write_region (Lisp_Object start, Lisp_Object end, 
Lisp_Object filename,
       if (desc1 >= 0)
        {
          struct stat st1;
-         if (fstat (desc1, &st1) == 0
+         if (sys_fstat (desc1, &st1) == 0
              && st.st_dev == st1.st_dev && st.st_ino == st1.st_ino)
            {
              /* Use the heuristic if it appears to be valid.  With neither
@@ -5800,7 +5910,6 @@ See Info node `(elisp)Modification Time' for more 
details.  */)
     return call2 (handler, Qverify_visited_file_modtime, buf);
 
   filename = ENCODE_FILE (BVAR (b, filename));
-
   mtime = (emacs_fstatat (AT_FDCWD, SSDATA (filename), &st, 0) == 0
           ? get_stat_mtime (&st)
           : time_error_value (errno));
@@ -5860,7 +5969,7 @@ in `current-time' or an integer flag as returned by 
`visited-file-modtime'.  */)
     error ("An indirect buffer does not have a visited file");
   else
     {
-      register Lisp_Object filename;
+      register Lisp_Object filename, encoded;
       struct stat st;
       Lisp_Object handler;
 
@@ -5873,7 +5982,9 @@ in `current-time' or an integer flag as returned by 
`visited-file-modtime'.  */)
        /* The handler can find the file name the same way we did.  */
        return call2 (handler, Qset_visited_file_modtime, Qnil);
 
-      if (emacs_fstatat (AT_FDCWD, SSDATA (ENCODE_FILE (filename)), &st, 0)
+      encoded = ENCODE_FILE (filename);
+
+      if (emacs_fstatat (AT_FDCWD, SSDATA (encoded), &st, 0)
          == 0)
         {
          current_buffer->modtime = get_stat_mtime (&st);
@@ -5946,7 +6057,7 @@ do_auto_save_unwind (void *arg)
   if (stream != NULL)
     {
       block_input ();
-      fclose (stream);
+      emacs_fclose (stream);
       unblock_input ();
     }
 }
@@ -6272,6 +6383,11 @@ effect except for flushing STREAM's data.  */)
 
 #ifndef DOS_NT
 
+#if defined STAT_STATFS2_BSIZE || defined STAT_STATFS2_FRSIZE  \
+  || defined STAT_STATFS2_FSIZE || defined STAT_STATFS3_OSF1   \
+  || defined STAT_STATFS4 || defined STAT_STATVFS              \
+  || defined STAT_STATVFS64
+
 /* Yield a Lisp number equal to BLOCKSIZE * BLOCKS, with the result
    negated if NEGATE.  */
 static Lisp_Object
@@ -6286,6 +6402,8 @@ blocks_to_bytes (uintmax_t blocksize, uintmax_t blocks, 
bool negate)
   return CALLN (Ftimes, bs, make_uint (blocks));
 }
 
+#endif
+
 DEFUN ("file-system-info", Ffile_system_info, Sfile_system_info, 1, 1, 0,
        doc: /* Return storage information about the file system FILENAME is on.
 Value is a list of numbers (TOTAL FREE AVAIL), where TOTAL is the total
@@ -6307,6 +6425,11 @@ If the underlying system call fails, value is nil.  */)
       error ("Invalid handler in `file-name-handler-alist'");
     }
 
+  /* Try to detect whether or not fsusage.o is actually built.  */
+#if defined STAT_STATFS2_BSIZE || defined STAT_STATFS2_FRSIZE  \
+  || defined STAT_STATFS2_FSIZE || defined STAT_STATFS3_OSF1   \
+  || defined STAT_STATFS4 || defined STAT_STATVFS              \
+  || defined STAT_STATVFS64
   struct fs_usage u;
   if (get_fs_usage (SSDATA (ENCODE_FILE (filename)), NULL, &u) != 0)
     return errno == ENOSYS ? Qnil : file_attribute_errno (filename, errno);
@@ -6314,6 +6437,9 @@ If the underlying system call fails, value is nil.  */)
                blocks_to_bytes (u.fsu_blocksize, u.fsu_bfree, false),
                blocks_to_bytes (u.fsu_blocksize, u.fsu_bavail,
                                 u.fsu_bavail_top_bit_set));
+#else
+  return Qnil;
+#endif
 }
 
 #endif /* !DOS_NT */
@@ -6344,7 +6470,7 @@ syms_of_fileio (void)
   DEFSYM (Qcopy_file, "copy-file");
   DEFSYM (Qmake_directory_internal, "make-directory-internal");
   DEFSYM (Qmake_directory, "make-directory");
-  DEFSYM (Qdelete_file, "delete-file");
+  DEFSYM (Qdelete_file_internal, "delete-file-internal");
   DEFSYM (Qfile_name_case_insensitive_p, "file-name-case-insensitive-p");
   DEFSYM (Qrename_file, "rename-file");
   DEFSYM (Qadd_name_to_file, "add-name-to-file");
@@ -6608,6 +6734,9 @@ This includes interactive calls to `delete-file' and
   delete_by_moving_to_trash = 0;
   DEFSYM (Qdelete_by_moving_to_trash, "delete-by-moving-to-trash");
 
+  /* Lisp function for interactive file delete with trashing */
+  DEFSYM (Qdelete_file, "delete-file");
+
   /* Lisp function for moving files to trash.  */
   DEFSYM (Qmove_file_to_trash, "move-file-to-trash");
 
@@ -6639,7 +6768,7 @@ This includes interactive calls to `delete-file' and
   defsubr (&Scopy_file);
   defsubr (&Smake_directory_internal);
   defsubr (&Sdelete_directory_internal);
-  defsubr (&Sdelete_file);
+  defsubr (&Sdelete_file_internal);
   defsubr (&Sfile_name_case_insensitive_p);
   defsubr (&Srename_file);
   defsubr (&Sadd_name_to_file);
diff --git a/src/filelock.c b/src/filelock.c
index be9f8f488d9..fea0044e219 100644
--- a/src/filelock.c
+++ b/src/filelock.c
@@ -65,10 +65,20 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #define BOOT_TIME_FILE "/var/run/random-seed"
 #endif
 
+/* Boot time is not available on Android.  */
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#undef BOOT_TIME
+#endif
+
 #if !defined WTMP_FILE && !defined WINDOWSNT && defined BOOT_TIME
 #define WTMP_FILE "/var/log/wtmp"
 #endif
 
+#ifdef HAVE_ANDROID
+#include "android.h" /* For `android_is_special_directory'.  */
+#endif /* HAVE_ANDROID */
+
 /* Normally use a symbolic link to represent a lock.
    The strategy: to lock a file FN, create a symlink .#FN in FN's
    directory, with link data USER@HOST.PID:BOOT.  This avoids a single
@@ -218,7 +228,7 @@ get_boot_time (void)
        {
          get_boot_time_1 (SSDATA (filename), 1);
          if (delete_flag)
-           unlink (SSDATA (filename));
+           emacs_unlink (SSDATA (filename));
        }
     }
 
@@ -313,11 +323,12 @@ rename_lock_file (char const *old, char const *new, bool 
force)
     {
       struct stat st;
 
-      int r = renameat_noreplace (AT_FDCWD, old, AT_FDCWD, new);
+      int r = emacs_renameat_noreplace (AT_FDCWD, old,
+                                       AT_FDCWD, new);
       if (! (r < 0 && errno == ENOSYS))
        return r;
       if (link (old, new) == 0)
-       return unlink (old) == 0 || errno == ENOENT ? 0 : -1;
+       return emacs_unlink (old) == 0 || errno == ENOENT ? 0 : -1;
       if (errno != ENOSYS && errno != LINKS_MIGHT_NOT_WORK)
        return -1;
 
@@ -337,7 +348,7 @@ rename_lock_file (char const *old, char const *new, bool 
force)
        return -1;
     }
 
-  return rename (old, new);
+  return emacs_rename (old, new);
 #endif
 }
 
@@ -355,13 +366,13 @@ create_lock_file (char *lfname, char *lock_info_str, bool 
force)
      pretending that 'symlink' does not work.  */
   int err = ENOSYS;
 #else
-  int err = symlink (lock_info_str, lfname) == 0 ? 0 : errno;
+  int err = emacs_symlink (lock_info_str, lfname) == 0 ? 0 : errno;
 #endif
 
   if (err == EEXIST && force)
     {
-      unlink (lfname);
-      err = symlink (lock_info_str, lfname) == 0 ? 0 : errno;
+      emacs_unlink (lfname);
+      err = emacs_symlink (lock_info_str, lfname) == 0 ? 0 : errno;
     }
 
   if (err == ENOSYS || err == LINKS_MIGHT_NOT_WORK || err == ENAMETOOLONG)
@@ -399,7 +410,7 @@ create_lock_file (char *lfname, char *lock_info_str, bool 
force)
          if (!err && rename_lock_file (nonce, lfname, force) != 0)
            err = errno;
          if (err)
-           unlink (nonce);
+           emacs_unlink (nonce);
        }
 
       SAFE_FREE ();
@@ -612,7 +623,7 @@ current_lock_owner (lock_info_type *owner, Lisp_Object 
lfname)
       /* The owner process is dead or has a strange pid, so try to
          zap the lockfile.  */
       else
-        return unlink (SSDATA (lfname)) < 0 ? errno : 0;
+        return emacs_unlink (SSDATA (lfname)) < 0 ? errno : 0;
     }
   else
     { /* If we wanted to support the check for stale locks on remote machines,
@@ -653,8 +664,27 @@ lock_if_free (lock_info_type *clasher, Lisp_Object lfname)
 static Lisp_Object
 make_lock_file_name (Lisp_Object fn)
 {
-  Lisp_Object lock_file_name = call1 (Qmake_lock_file_name,
-                                     Fexpand_file_name (fn, Qnil));
+  Lisp_Object lock_file_name;
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  char *name;
+#endif
+
+  fn = Fexpand_file_name (fn, Qnil);
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  /* Files in /assets and /contents can't have lock files on Android
+     as these directories are fabrications of android.c, and backed by
+     read only data.  */
+
+  name = SSDATA (fn);
+
+  if (android_is_special_directory (name, "/assets")
+      || android_is_special_directory (name, "/content"))
+  return Qnil;
+#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
+  lock_file_name = call1 (Qmake_lock_file_name, fn);
+
   return !NILP (lock_file_name) ? ENCODE_FILE (lock_file_name) : Qnil;
 }
 
@@ -741,7 +771,8 @@ unlock_file (Lisp_Object fn)
   int err = current_lock_owner (0, lfname);
   if (! (err == 0 || err == ANOTHER_OWNS_IT
         || (err == I_OWN_IT
-            && (unlink (SSDATA (lfname)) == 0 || (err = errno) == ENOENT))))
+            && (emacs_unlink (SSDATA (lfname)) == 0
+                || (err = errno) == ENOENT))))
     report_file_errno ("Unlocking file", fn, err);
 
   return Qnil;
diff --git a/src/fns.c b/src/fns.c
index 25f16b58eeb..ee2c68e46ae 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -3560,6 +3560,10 @@ The data read from the system are decoded using 
`locale-coding-system'.  */)
   (Lisp_Object item)
 {
   char *str = NULL;
+
+  /* STR is apparently unused on Android.  */
+  ((void) str);
+
 #ifdef HAVE_LANGINFO_CODESET
   if (EQ (item, Qcodeset))
     {
diff --git a/src/font.c b/src/font.c
index 832fc7358e2..99247efb001 100644
--- a/src/font.c
+++ b/src/font.c
@@ -177,9 +177,35 @@ font_make_entity (void)
        allocate_pseudovector (VECSIZE (struct font_entity),
                              FONT_ENTITY_MAX, FONT_ENTITY_MAX, PVEC_FONT));
   XSETFONT (font_entity, entity);
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  entity->is_android = false;
+#endif
+
+  return font_entity;
+}
+
+#ifdef HAVE_ANDROID
+
+Lisp_Object
+font_make_entity_android (int size)
+{
+  Lisp_Object font_entity;
+  struct font_entity *entity
+    = ((struct font_entity *)
+       allocate_pseudovector (size, FONT_ENTITY_MAX, FONT_ENTITY_MAX,
+                             PVEC_FONT));
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  entity->is_android = true;
+#endif
+
+  XSETFONT (font_entity, entity);
   return font_entity;
 }
 
+#endif
+
 /* Create a font-object whose structure size is SIZE.  If ENTITY is
    not nil, copy properties from ENTITY to the font-object.  If
    PIXELSIZE is positive, set the `size' property to PIXELSIZE.  */
@@ -318,7 +344,7 @@ font_pixel_size (struct frame *f, Lisp_Object spec)
       if (FIXNUMP (val))
        dpi = XFIXNUM (val);
       else
-       dpi = FRAME_RES_Y (f);
+       dpi = FRAME_RES (f);
       pixel_size = POINT_TO_PIXEL (point_size, dpi);
       return pixel_size;
     }
@@ -2993,7 +3019,7 @@ font_find_for_lface (struct frame *f, Lisp_Object *attrs, 
Lisp_Object spec, int
     {
       double pt = XFIXNUM (attrs[LFACE_HEIGHT_INDEX]);
 
-      pixel_size = POINT_TO_PIXEL (pt / 10, FRAME_RES_Y (f));
+      pixel_size = POINT_TO_PIXEL (pt / 10, FRAME_RES (f));
       if (pixel_size < 1)
        pixel_size = 1;
     }
@@ -3145,13 +3171,13 @@ font_open_for_lface (struct frame *f, Lisp_Object 
entity, Lisp_Object *attrs, Li
            }
 
          pt /= 10;
-         size = POINT_TO_PIXEL (pt, FRAME_RES_Y (f));
+         size = POINT_TO_PIXEL (pt, FRAME_RES (f));
 #ifdef HAVE_NS
          if (size == 0)
            {
              Lisp_Object ffsize = get_frame_param (f, Qfontsize);
              size = (NUMBERP (ffsize)
-                     ? POINT_TO_PIXEL (XFLOATINT (ffsize), FRAME_RES_Y (f))
+                     ? POINT_TO_PIXEL (XFLOATINT (ffsize), FRAME_RES (f))
                      : 0);
            }
 #endif
@@ -4052,7 +4078,7 @@ are to be displayed on.  If omitted, the selected frame 
is used.  */)
   if (FIXNUMP (val))
     {
       Lisp_Object font_dpi = AREF (font, FONT_DPI_INDEX);
-      int dpi = FIXNUMP (font_dpi) ? XFIXNUM (font_dpi) : FRAME_RES_Y (f);
+      int dpi = FIXNUMP (font_dpi) ? XFIXNUM (font_dpi) : FRAME_RES (f);
       plist[n++] = QCheight;
       plist[n++] = make_fixnum (PIXEL_TO_POINT (XFIXNUM (val) * 10, dpi));
     }
@@ -4956,7 +4982,7 @@ DEFUN ("open-font", Fopen_font, Sopen_font, 1, 3, 0,
     {
       CHECK_NUMBER (size);
       if (FLOATP (size))
-       isize = POINT_TO_PIXEL (XFLOAT_DATA (size), FRAME_RES_Y (f));
+       isize = POINT_TO_PIXEL (XFLOAT_DATA (size), FRAME_RES (f));
       else if (! integer_to_intmax (size, &isize))
        args_out_of_range (font_entity, size);
       if (! (INT_MIN <= isize && isize <= INT_MAX))
diff --git a/src/font.h b/src/font.h
index 492c2e58b09..ed3b17db994 100644
--- a/src/font.h
+++ b/src/font.h
@@ -260,6 +260,11 @@ struct font_entity
 {
   union vectorlike_header header;
   Lisp_Object props[FONT_ENTITY_MAX];
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  /* Whether or not this is an Android font entity.  */
+  bool is_android;
+#endif
 };
 
 /* A value which may appear in the member `encoding' of struct font
@@ -547,8 +552,14 @@ CHECK_FONT_GET_OBJECT (Lisp_Object x)
   return XFONT_OBJECT (x);
 }
 
+#ifndef HAVE_ANDROID
 /* Number of pt per inch (from the TeXbook).  */
 #define PT_PER_INCH 72.27
+#else
+/* Android uses this value instead to compensate for different device
+   dimensions.  */
+#define PT_PER_INCH 160.00
+#endif
 
 /* Return a pixel size (integer) corresponding to POINT size (double)
    on resolution DPI.  */
@@ -823,6 +834,9 @@ extern Lisp_Object copy_font_spec (Lisp_Object);
 extern Lisp_Object merge_font_spec (Lisp_Object, Lisp_Object);
 
 extern Lisp_Object font_make_entity (void);
+#ifdef HAVE_ANDROID
+extern Lisp_Object font_make_entity_android (int);
+#endif
 extern Lisp_Object font_make_object (int, Lisp_Object, int);
 #if defined (HAVE_XFT) || defined (HAVE_FREETYPE) || defined (HAVE_NS)
 extern Lisp_Object font_build_object (int, Lisp_Object, Lisp_Object, double);
diff --git a/src/fontset.c b/src/fontset.c
index c0e00cfa346..139dcb6eb89 100644
--- a/src/fontset.c
+++ b/src/fontset.c
@@ -667,8 +667,35 @@ fontset_find_font (Lisp_Object fontset, int c, struct face 
*face,
            }
          font_object = font_open_for_lface (f, font_entity, face->lface,
                                             FONT_DEF_SPEC (font_def));
+
+         /* If the font registry is not the same as explicitly
+            specified in the font spec, do not cache the font.
+            TrueType fonts have contrived character map selection
+            semantics which makes determining the repertory at font
+            spec matching time unduly expensive.  */
+
+         {
+           Lisp_Object spec;
+
+           spec = FONT_DEF_SPEC (font_def);
+
+           if (!NILP (font_object)
+               && !NILP (AREF (spec, FONT_REGISTRY_INDEX))
+               && !NILP (AREF (font_object, FONT_REGISTRY_INDEX))
+               && !EQ (AREF (spec, FONT_REGISTRY_INDEX),
+                       AREF (font_object, FONT_REGISTRY_INDEX))
+               /* See sfntfont_registries_compatible_p in
+                  sfntfont.c.  */
+               && !(EQ (AREF (spec, FONT_REGISTRY_INDEX),
+                        Qiso8859_1)
+                    && EQ (AREF (font_object, FONT_REGISTRY_INDEX),
+                           Qiso10646_1)))
+             goto strangeness;
+         }
+
          if (NILP (font_object))
            {
+           strangeness:
              /* Something strange happened, perhaps because of a
                 Font-backend problem.  To avoid crashing, record
                 that this spec is unusable.  It may be better to find
diff --git a/src/frame.c b/src/frame.c
index 83925d4742a..da00cbf4bce 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -228,6 +228,7 @@ Value is:
  `pc' for a direct-write MS-DOS frame,
  `pgtk' for an Emacs frame running on pure GTK.
  `haiku' for an Emacs frame running in Haiku.
+ `android' for an Emacs frame running in Android.
 See also `frame-live-p'.  */)
   (Lisp_Object object)
 {
@@ -250,6 +251,8 @@ See also `frame-live-p'.  */)
       return Qpgtk;
     case output_haiku:
       return Qhaiku;
+    case output_android:
+      return Qandroid;
     default:
       emacs_abort ();
     }
@@ -279,6 +282,7 @@ The value is a symbol:
  `pc' for a direct-write MS-DOS frame.
  `pgtk' for an Emacs frame using pure GTK facilities.
  `haiku' for an Emacs frame running in Haiku.
+ `android' for an Emacs frame running in Android/
 
 FRAME defaults to the currently selected frame.
 
@@ -982,6 +986,7 @@ make_frame (bool mini_p)
   f->last_tab_bar_item = -1;
 #ifndef HAVE_EXT_TOOL_BAR
   f->last_tool_bar_item = -1;
+  f->tool_bar_wraps_p = false;
 #endif
 #ifdef NS_IMPL_COCOA
   f->ns_appearance = ns_appearance_system_default;
@@ -991,6 +996,16 @@ make_frame (bool mini_p)
   f->select_mini_window_flag = false;
   /* This one should never be zero.  */
   f->change_stamp = 1;
+
+#ifdef HAVE_TEXT_CONVERSION
+  f->conversion.compose_region_start = Qnil;
+  f->conversion.compose_region_end = Qnil;
+  f->conversion.compose_region_overlay = Qnil;
+  f->conversion.batch_edit_count = 0;
+  f->conversion.batch_edit_flags = 0;
+  f->conversion.actions = NULL;
+#endif
+
   root_window = make_window ();
   rw = XWINDOW (root_window);
   if (mini_p)
@@ -1226,6 +1241,7 @@ make_initial_frame (void)
   return f;
 }
 
+#ifndef HAVE_ANDROID
 
 static struct frame *
 make_terminal_frame (struct terminal *terminal)
@@ -1315,6 +1331,8 @@ get_future_frame_param (Lisp_Object parameter,
   return result;
 }
 
+#endif
+
 DEFUN ("make-terminal-frame", Fmake_terminal_frame, Smake_terminal_frame,
        1, 1, 0,
        doc: /* Create an additional terminal frame, possibly on another 
terminal.
@@ -1334,6 +1352,10 @@ Note that changing the size of one terminal frame 
automatically
 affects all frames on the same terminal device.  */)
   (Lisp_Object parms)
 {
+#ifdef HAVE_ANDROID
+  error ("Text terminals are not supported on this platform");
+  return Qnil;
+#else
   struct frame *f;
   struct terminal *t = NULL;
   Lisp_Object frame;
@@ -1434,6 +1456,7 @@ affects all frames on the same terminal device.  */)
   f->after_make_frame = true;
 
   return frame;
+#endif
 }
 
 
@@ -2292,6 +2315,13 @@ delete_frame (Lisp_Object frame, Lisp_Object force)
     f->terminal = 0;             /* Now the frame is dead.  */
     unblock_input ();
 
+  /* Clear markers and overlays set by F on behalf of an input
+     method.  */
+#ifdef HAVE_TEXT_CONVERSION
+  if (FRAME_WINDOW_P (f))
+    reset_frame_state (f);
+#endif
+
     /* If needed, delete the terminal that this frame was on.
        (This must be done after the frame is killed.)  */
     terminal->reference_count--;
@@ -5343,16 +5373,23 @@ gui_display_get_resource (Display_Info *dpyinfo, 
Lisp_Object attribute,
   *nz++ = '.';
   lispstpcpy (nz, attribute);
 
-  const char *value =
-    dpyinfo->terminal->get_string_resource_hook (&dpyinfo->rdb,
-                                                 name_key,
-                                                 class_key);
-  SAFE_FREE();
+#ifndef HAVE_ANDROID
+  const char *value
+    = dpyinfo->terminal->get_string_resource_hook (&dpyinfo->rdb,
+                                                  name_key,
+                                                  class_key);
+
+  SAFE_FREE ();
 
   if (value && *value)
     return build_string (value);
   else
     return Qnil;
+#else
+
+  SAFE_FREE ();
+  return Qnil;
+#endif
 }
 
 
@@ -5687,6 +5724,8 @@ On Nextstep, this just calls `ns-parse-geometry'.  */)
   int x UNINIT, y UNINIT;
   unsigned int width, height;
 
+  width = height = 0;
+
   CHECK_STRING (string);
 
 #ifdef HAVE_NS
@@ -6159,8 +6198,11 @@ make_monitor_attribute_list (struct MonitorInfo 
*monitors,
                         mi->work.width, mi->work.height);
       geometry = list4i (mi->geom.x, mi->geom.y,
                         mi->geom.width, mi->geom.height);
-      attributes = Fcons (Fcons (Qsource, build_string (source)),
-                          attributes);
+
+      if (source)
+       attributes = Fcons (Fcons (Qsource, build_string (source)),
+                           attributes);
+
       attributes = Fcons (Fcons (Qframes, AREF (monitor_frames, i)),
                          attributes);
 #ifdef HAVE_PGTK
@@ -6258,6 +6300,7 @@ syms_of_frame (void)
   DEFSYM (Qns, "ns");
   DEFSYM (Qpgtk, "pgtk");
   DEFSYM (Qhaiku, "haiku");
+  DEFSYM (Qandroid, "android");
   DEFSYM (Qvisible, "visible");
   DEFSYM (Qbuffer_predicate, "buffer-predicate");
   DEFSYM (Qbuffer_list, "buffer-list");
@@ -6446,7 +6489,7 @@ Setting this variable does not affect existing frames, 
only new ones.  */);
 
   DEFVAR_LISP ("default-frame-scroll-bars", Vdefault_frame_scroll_bars,
               doc: /* Default position of vertical scroll bars on this 
window-system.  */);
-#ifdef HAVE_WINDOW_SYSTEM
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_ANDROID
 #if defined (HAVE_NTGUI) || defined (NS_IMPL_COCOA) || (defined (USE_GTK) && 
defined (USE_TOOLKIT_SCROLL_BARS))
   /* MS-Windows, macOS, and GTK have scroll bars on the right by
      default.  */
@@ -6454,9 +6497,9 @@ Setting this variable does not affect existing frames, 
only new ones.  */);
 #else
   Vdefault_frame_scroll_bars = Qleft;
 #endif
-#else
+#else /* !HAVE_WINDOW_SYSTEM || HAVE_ANDROID */
   Vdefault_frame_scroll_bars = Qnil;
-#endif
+#endif /* HAVE_WINDOW_SYSTEM && !HAVE_ANDROID */
 
   DEFVAR_BOOL ("scroll-bar-adjust-thumb-portion",
                scroll_bar_adjust_thumb_portion_p,
@@ -6664,7 +6707,7 @@ implicitly when there's no window system support.
 Note that when a frame is not large enough to accommodate a change of
 any of the parameters listed above, Emacs may try to enlarge the frame
 even if this option is non-nil.  */);
-#if defined (HAVE_WINDOW_SYSTEM)
+#if defined (HAVE_WINDOW_SYSTEM) && !defined (HAVE_ANDROID)
 #if defined (USE_GTK) || defined (HAVE_NS)
   frame_inhibit_implied_resize = list1 (Qtab_bar_lines);
 #else
diff --git a/src/frame.h b/src/frame.h
index 12a3c460a5f..c85df378da6 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -76,6 +76,65 @@ enum ns_appearance_type
 #endif
 #endif /* HAVE_WINDOW_SYSTEM */
 
+#ifdef HAVE_TEXT_CONVERSION
+
+enum text_conversion_operation
+  {
+    TEXTCONV_START_BATCH_EDIT,
+    TEXTCONV_END_BATCH_EDIT,
+    TEXTCONV_COMMIT_TEXT,
+    TEXTCONV_FINISH_COMPOSING_TEXT,
+    TEXTCONV_SET_COMPOSING_TEXT,
+    TEXTCONV_SET_COMPOSING_REGION,
+    TEXTCONV_SET_POINT_AND_MARK,
+    TEXTCONV_DELETE_SURROUNDING_TEXT,
+    TEXTCONV_REQUEST_POINT_UPDATE,
+    TEXTCONV_BARRIER,
+  };
+
+/* Structure describing a single edit being performed by the input
+   method that should be executed in the context of
+   kbd_buffer_get_event.  */
+
+struct text_conversion_action
+{
+  /* The next text conversion action.  */
+  struct text_conversion_action *next;
+
+  /* Any associated data.  */
+  Lisp_Object data;
+
+  /* The operation being performed.  */
+  enum text_conversion_operation operation;
+
+  /* Counter value.  */
+  unsigned long counter;
+};
+
+/* Structure describing the text conversion state associated with a
+   frame.  */
+
+struct text_conversion_state
+{
+  /* List of text conversion actions associated with this frame.  */
+  struct text_conversion_action *actions;
+
+  /* Markers representing the composing region.  */
+  Lisp_Object compose_region_start, compose_region_end;
+
+  /* Overlay representing the composing region.  */
+  Lisp_Object compose_region_overlay;
+
+  /* The number of ongoing ``batch edits'' that are causing point
+     reporting to be delayed.  */
+  int batch_edit_count;
+
+  /* Mask containing what must be updated after batch edits end.  */
+  int batch_edit_flags;
+};
+
+#endif
+
 /* The structure representing a frame.  */
 
 struct frame
@@ -181,7 +240,7 @@ struct frame
      most recently buried buffer is first.  For last-buffer.  */
   Lisp_Object buried_buffer_list;
 
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined 
(USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
   /* A dummy window used to display menu bars under X when no X
      toolkit support is available.  */
   Lisp_Object menu_bar_window;
@@ -285,6 +344,10 @@ struct frame
   /* Set to true to minimize tool-bar height even when
      auto-resize-tool-bar is set to grow-only.  */
   bool_bf minimize_tool_bar_window_p : 1;
+
+  /* Whether or not the tool bar contains a ``new line'' item.  If
+     true, tool bar rows will be allowed to differ in height.  */
+  bool_bf tool_bar_wraps_p : 1;
 #endif
 
 #ifdef HAVE_EXT_TOOL_BAR
@@ -375,7 +438,7 @@ struct frame
   /* The output method says how the contents of this frame are
      displayed.  It could be using termcap, or using an X window.
      This must be the same as the terminal->type. */
-  ENUM_BF (output_method) output_method : 3;
+  ENUM_BF (output_method) output_method : 4;
 
 #ifdef HAVE_WINDOW_SYSTEM
   /* True if this frame is a tooltip frame.  */
@@ -584,20 +647,22 @@ struct frame
      well.  */
   union output_data
   {
-    struct tty_output *tty;     /* From termchar.h.  */
-    struct x_output *x;         /* From xterm.h.  */
-    struct w32_output *w32;     /* From w32term.h.  */
-    struct ns_output *ns;       /* From nsterm.h.  */
-    struct pgtk_output *pgtk; /* From pgtkterm.h. */
-    struct haiku_output *haiku; /* From haikuterm.h. */
+    struct tty_output *tty;            /* From termchar.h.  */
+    struct x_output *x;                        /* From xterm.h.  */
+    struct w32_output *w32;            /* From w32term.h.  */
+    struct ns_output *ns;              /* From nsterm.h.  */
+    struct pgtk_output *pgtk;          /* From pgtkterm.h. */
+    struct haiku_output *haiku;                /* From haikuterm.h. */
+    struct android_output *android;    /* From androidterm.h.  */
   }
   output_data;
 
   /* List of font-drivers available on the frame.  */
   struct font_driver_list *font_driver_list;
 
-#if defined (HAVE_X_WINDOWS)
-  /* Used by x_wait_for_event when watching for an X event on this frame.  */
+#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID
+  /* Used by x_wait_for_event when watching for an X event on this
+     frame.  */
   int wait_event_type;
 #endif
 
@@ -660,6 +725,11 @@ struct frame
   enum ns_appearance_type ns_appearance;
   bool_bf ns_transparent_titlebar;
 #endif
+
+#ifdef HAVE_TEXT_CONVERSION
+  /* Text conversion state used by certain input methods.  */
+  struct text_conversion_state conversion;
+#endif
 } GCALIGNED_STRUCT;
 
 /* Most code should use these functions to set Lisp fields in struct frame.  */
@@ -711,7 +781,7 @@ fset_menu_bar_vector (struct frame *f, Lisp_Object val)
 {
   f->menu_bar_vector = val;
 }
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined 
(USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
 INLINE void
 fset_menu_bar_window (struct frame *f, Lisp_Object val)
 {
@@ -872,6 +942,11 @@ default_pixels_per_inch_y (void)
 #else
 #define FRAME_HAIKU_P(f) ((f)->output_method == output_haiku)
 #endif
+#ifndef HAVE_ANDROID
+#define FRAME_ANDROID_P(f) false
+#else
+#define FRAME_ANDROID_P(f) ((f)->output_method == output_android)
+#endif
 
 /* FRAME_WINDOW_P tests whether the frame is a graphical window system
    frame.  */
@@ -890,6 +965,9 @@ default_pixels_per_inch_y (void)
 #ifdef HAVE_HAIKU
 #define FRAME_WINDOW_P(f) FRAME_HAIKU_P (f)
 #endif
+#ifdef HAVE_ANDROID
+#define FRAME_WINDOW_P(f) FRAME_ANDROID_P (f)
+#endif
 #ifndef FRAME_WINDOW_P
 #define FRAME_WINDOW_P(f) ((void) (f), false)
 #endif
@@ -903,12 +981,26 @@ default_pixels_per_inch_y (void)
 #define FRAME_RES_Y(f)                                         \
   (eassert (FRAME_WINDOW_P (f)), FRAME_DISPLAY_INFO (f)->resy)
 
+#ifdef HAVE_ANDROID
+
+/* Android systems use a font scaling factor independent from the
+   display DPI.  */
+
+#define FRAME_RES(f)                                           \
+  (eassert (FRAME_WINDOW_P (f)),                               \
+   FRAME_DISPLAY_INFO (f)->font_resolution)
+
+#else /* !HAVE_ANDROID */
+#define FRAME_RES(f) (FRAME_RES_Y (f))
+#endif /* HAVE_ANDROID */
+
 #else /* !HAVE_WINDOW_SYSTEM */
 
 /* Defaults when no window system available.  */
 
-#define FRAME_RES_X(f) default_pixels_per_inch_x ()
-#define FRAME_RES_Y(f) default_pixels_per_inch_y ()
+#define FRAME_RES_X(f) default_pixels_per_inch_x ()
+#define FRAME_RES_Y(f) default_pixels_per_inch_y ()
+#define FRAME_RES(f)   default_pixels_per_inch_y ()
 
 #endif /* HAVE_WINDOW_SYSTEM */
 
@@ -917,11 +1009,17 @@ default_pixels_per_inch_y (void)
    frame F.  We need to define two versions because a TTY-only build
    does not have FRAME_DISPLAY_INFO.  */
 #ifdef HAVE_WINDOW_SYSTEM
+#ifndef HAVE_ANDROID
 # define MOUSE_HL_INFO(F)                                      \
-  (FRAME_WINDOW_P(F)                                           \
+  (FRAME_WINDOW_P (F)                                          \
    ? &FRAME_DISPLAY_INFO(F)->mouse_highlight                   \
    : &(F)->output_data.tty->display_info->mouse_highlight)
 #else
+/* There is no "struct tty_output" on Android at all.  */
+# define MOUSE_HL_INFO(F)                                      \
+  (&FRAME_DISPLAY_INFO(F)->mouse_highlight)
+#endif
+#else
 # define MOUSE_HL_INFO(F)                                      \
   (&(F)->output_data.tty->display_info->mouse_highlight)
 #endif
diff --git a/src/fringe.c b/src/fringe.c
index ed257c073b9..3452c8503f0 100644
--- a/src/fringe.c
+++ b/src/fringe.c
@@ -1422,25 +1422,30 @@ If BITMAP overrides a standard fringe bitmap, the 
original bitmap is restored.
    On X, we bit-swap the built-in bitmaps and reduce bitmap
    from short to char array if width is <= 8 bits.
 
+   The Android port tries to follow X as closely as possible, so do
+   that there too.
+
    On MAC with big-endian CPU, we need to byte-swap each short.
 
    On W32 and MAC (little endian), there's no need to do this.
 */
 
-#if defined (HAVE_X_WINDOWS) || defined (HAVE_PGTK)
-static const unsigned char swap_nibble[16] = {
-  0x0, 0x8, 0x4, 0xc,           /* 0000 1000 0100 1100 */
-  0x2, 0xa, 0x6, 0xe,           /* 0010 1010 0110 1110 */
-  0x1, 0x9, 0x5, 0xd,           /* 0001 1001 0101 1101 */
-  0x3, 0xb, 0x7, 0xf};          /* 0011 1011 0111 1111 */
-#endif                          /* HAVE_X_WINDOWS */
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_PGTK) || defined (HAVE_ANDROID)
+static const unsigned char swap_nibble[16] =
+  {
+    0x0, 0x8, 0x4, 0xc,           /* 0000 1000 0100 1100 */
+    0x2, 0xa, 0x6, 0xe,           /* 0010 1010 0110 1110 */
+    0x1, 0x9, 0x5, 0xd,           /* 0001 1001 0101 1101 */
+    0x3, 0xb, 0x7, 0xf,           /* 0011 1011 0111 1111 */
+  };
+#endif
 
 static void
 init_fringe_bitmap (int which, struct fringe_bitmap *fb, int once_p)
 {
   if (once_p || fb->dynamic)
     {
-#if defined (HAVE_X_WINDOWS)
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_ANDROID)
       unsigned short *bits = fb->bits;
       int j;
 
@@ -1488,7 +1493,7 @@ init_fringe_bitmap (int which, struct fringe_bitmap *fb, 
int once_p)
            }
        }
 #endif /* not USE_CAIRO */
-#endif /* HAVE_X_WINDOWS */
+#endif /* HAVE_X_WINDOWS || HAVE_ANDROID */
 
 #if !defined(HAVE_X_WINDOWS) && defined (HAVE_PGTK)
       unsigned short *bits = fb->bits;
diff --git a/src/image.c b/src/image.c
index b48bd672425..5eaafdf2af3 100644
--- a/src/image.c
+++ b/src/image.c
@@ -175,6 +175,31 @@ typedef struct haiku_bitmap_record Bitmap_Record;
 
 #endif
 
+#ifdef HAVE_ANDROID
+#include "androidterm.h"
+
+typedef struct android_bitmap_record Bitmap_Record;
+
+typedef struct android_image XImage;
+typedef android_pixmap Pixmap;
+
+#define GET_PIXEL(ximg, x, y)          android_get_pixel (ximg, x, y)
+#define PUT_PIXEL(ximg, x, y, pixel)    android_put_pixel (ximg, x, y, pixel)
+#define NO_PIXMAP                      0
+
+#define PIX_MASK_RETAIN        0
+#define PIX_MASK_DRAW  1
+
+#define RGB_TO_ULONG(r, g, b) (((r) << 16) | ((g) << 8) | (b))
+#define RED_FROM_ULONG(color)  (((color) >> 16) & 0xff)
+#define GREEN_FROM_ULONG(color)        (((color) >> 8) & 0xff)
+#define BLUE_FROM_ULONG(color) ((color) & 0xff)
+#define RED16_FROM_ULONG(color)                (RED_FROM_ULONG (color) * 0x101)
+#define GREEN16_FROM_ULONG(color)      (GREEN_FROM_ULONG (color) * 0x101)
+#define BLUE16_FROM_ULONG(color)       (BLUE_FROM_ULONG (color) * 0x101)
+
+#endif
+
 static void image_disable_image (struct frame *, struct image *);
 static void image_edge_detection (struct frame *, struct image *, Lisp_Object,
                                   Lisp_Object);
@@ -555,6 +580,18 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
     return -1;
 #endif /* HAVE_X_WINDOWS */
 
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  android_pixmap bitmap;
+
+  bitmap = android_create_bitmap_from_data (bits, width, height);
+
+  if (!bitmap)
+    return -1;
+#elif defined HAVE_ANDROID
+  ((void) dpyinfo);
+  emacs_abort ();
+#endif /* HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
 #ifdef HAVE_NTGUI
   Lisp_Object frame UNINIT;    /* The value is not used.  */
   Emacs_Pixmap bitmap;
@@ -628,14 +665,16 @@ image_create_bitmap_from_data (struct frame *f, char 
*bits,
   dpyinfo->bitmaps[id - 1].width = width;
   dpyinfo->bitmaps[id - 1].refcount = 1;
 
-#ifdef HAVE_X_WINDOWS
+#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID
+#ifndef ANDROID_STUBIFY
   dpyinfo->bitmaps[id - 1].pixmap = bitmap;
+#endif /* ANDROID_STUBIFY */
   dpyinfo->bitmaps[id - 1].have_mask = false;
   dpyinfo->bitmaps[id - 1].depth = 1;
 #ifdef USE_CAIRO
   dpyinfo->bitmaps[id - 1].stipple = NULL;
 #endif /* USE_CAIRO */
-#endif /* HAVE_X_WINDOWS */
+#endif /* HAVE_X_WINDOWS || HAVE_ANDROID */
 
 #ifdef HAVE_NTGUI
   dpyinfo->bitmaps[id - 1].pixmap = bitmap;
@@ -646,9 +685,20 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
   return id;
 }
 
-#if defined HAVE_HAIKU || defined HAVE_NS || defined HAVE_PGTK
-static char *slurp_file (int, ptrdiff_t *);
-static Lisp_Object image_find_image_fd (Lisp_Object, int *);
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#include "android.h"
+
+/* This abstraction allows directly loading images from assets without
+   copying them to a file descriptor first.  */
+typedef struct android_fd_or_asset image_fd;
+#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
+typedef int image_fd;
+#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
+#if defined HAVE_HAIKU || defined HAVE_NS || defined HAVE_PGTK \
+  || defined HAVE_ANDROID
+static char *slurp_file (image_fd, ptrdiff_t *);
+static Lisp_Object image_find_image_fd (Lisp_Object, image_fd *);
 static bool xbm_read_bitmap_data (struct frame *, char *, char *,
                                  int *, int *, char **, bool);
 #endif
@@ -767,7 +817,7 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object 
file)
 
   /* Search bitmap-file-path for the file, if appropriate.  */
   if (openp (Vx_bitmap_file_path, file, Qnil, &found,
-            make_fixnum (R_OK), false, false)
+            make_fixnum (R_OK), false, false, NULL)
       < 0)
     return -1;
 
@@ -816,7 +866,7 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object 
file)
 
   /* Search bitmap-file-path for the file, if appropriate.  */
   if (openp (Vx_bitmap_file_path, file, Qnil, &found,
-            make_fixnum (R_OK), false, false)
+            make_fixnum (R_OK), false, false, NULL)
       < 0)
     return -1;
 
@@ -877,6 +927,73 @@ image_create_bitmap_from_file (struct frame *f, 
Lisp_Object file)
   xfree (contents);
   return id;
 #endif
+
+#ifdef HAVE_ANDROID
+#ifdef ANDROID_STUBIFY
+  ((void) dpyinfo);
+
+  /* This function should never be called when building stubs.  */
+  emacs_abort ();
+#else
+  ptrdiff_t id, size;
+  int width, height, rc;
+  image_fd fd;
+  char *contents, *data;
+  Lisp_Object found;
+  android_pixmap bitmap;
+
+  /* Look for an existing bitmap with the same name.  */
+  for (id = 0; id < dpyinfo->bitmaps_last; ++id)
+    {
+      if (dpyinfo->bitmaps[id].refcount
+         && dpyinfo->bitmaps[id].file
+         && !strcmp (dpyinfo->bitmaps[id].file, SSDATA (file)))
+       {
+         ++dpyinfo->bitmaps[id].refcount;
+         return id + 1;
+       }
+    }
+
+  /* Search bitmap-file-path for the file, if appropriate.  */
+  if (openp (Vx_bitmap_file_path, file, Qnil, &found,
+            make_fixnum (R_OK), false, false, NULL)
+      < 0)
+    return -1;
+
+  if (!STRINGP (image_find_image_fd (file, &fd))
+      && !STRINGP (image_find_image_fd (found, &fd)))
+    return -1;
+
+  contents = slurp_file (fd, &size);
+
+  if (!contents)
+    return -1;
+
+  rc = xbm_read_bitmap_data (f, contents, contents + size,
+                            &width, &height, &data, 0);
+
+  if (!rc)
+    {
+      xfree (contents);
+      return -1;
+    }
+
+  xfree (contents);
+  bitmap = android_create_bitmap_from_data (data, width, height);
+  xfree (data);
+
+  id = image_allocate_bitmap_record (f);
+  dpyinfo->bitmaps[id - 1].pixmap = bitmap;
+  dpyinfo->bitmaps[id - 1].have_mask = false;
+  dpyinfo->bitmaps[id - 1].refcount = 1;
+  dpyinfo->bitmaps[id - 1].file = xlispstrdup (file);
+  dpyinfo->bitmaps[id - 1].depth = 1;
+  dpyinfo->bitmaps[id - 1].height = height;
+  dpyinfo->bitmaps[id - 1].width = width;
+
+  return id;
+#endif
+#endif
 }
 
 /* Free bitmap B.  */
@@ -902,6 +1019,13 @@ free_bitmap_record (Display_Info *dpyinfo, Bitmap_Record 
*bm)
 #endif /* USE_CAIRO */
 #endif /* HAVE_X_WINDOWS */
 
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  android_free_pixmap (bm->pixmap);
+
+  if (bm->have_mask)
+    android_free_pixmap (bm->pixmap);
+#endif
+
 #ifdef HAVE_NTGUI
   DeleteObject (bm->pixmap);
 #endif /* HAVE_NTGUI */
@@ -989,7 +1113,7 @@ static void image_unget_x_image (struct image *, bool, 
Emacs_Pix_Container);
   image_unget_x_image (img, mask_p, ximg)
 #endif
 
-#ifdef HAVE_X_WINDOWS
+#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID
 
 #ifndef USE_CAIRO
 static void image_sync_to_pixmaps (struct frame *, struct image *);
@@ -1003,6 +1127,8 @@ static bool x_create_x_image_and_pixmap (struct frame *, 
int, int, int,
                                         XImage **, Pixmap *);
 static void x_destroy_x_image (XImage *);
 
+#if defined HAVE_X_WINDOWS
+
 /* Create a mask of a bitmap. Note is this not a perfect mask.
    It's nicer with some borders in this context */
 
@@ -1099,7 +1225,9 @@ x_create_bitmap_mask (struct frame *f, ptrdiff_t id)
   x_destroy_x_image (mask_img);
 }
 
-#endif /* HAVE_X_WINDOWS */
+#endif
+
+#endif /* HAVE_X_WINDOWS || defined HAVE_ANDROID*/
 
 /***********************************************************************
                            Image types
@@ -1133,7 +1261,7 @@ struct image_type
 #if defined HAVE_RSVG || defined HAVE_PNG || defined HAVE_GIF || \
   defined HAVE_TIFF || defined HAVE_JPEG || defined HAVE_XPM || \
   defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK || \
-  defined HAVE_WEBP
+  defined HAVE_WEBP || defined HAVE_ANDROID
 # ifdef WINDOWSNT
 #  define IMAGE_TYPE_INIT(f) f
 # else
@@ -1637,7 +1765,7 @@ prepare_image_for_display (struct frame *f, struct image 
*img)
        }
       unblock_input ();
     }
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
   if (!img->load_failed_p)
     {
       block_input ();
@@ -1844,7 +1972,7 @@ image_clear_image_1 (struct frame *f, struct image *img, 
int flags)
          /* NOTE (HAVE_NS): background color is NOT an indexed color! */
          img->background_valid = 0;
        }
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
       if (img->ximg)
        {
          image_destroy_x_image (img->ximg);
@@ -1862,7 +1990,7 @@ image_clear_image_1 (struct frame *f, struct image *img, 
int flags)
          img->mask = NO_PIXMAP;
          img->background_transparent_valid = 0;
        }
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
       if (img->mask_img)
        {
          image_destroy_x_image (img->mask_img);
@@ -2234,11 +2362,11 @@ image_size_in_bytes (struct image *img)
   if (msk)
     size += msk->height * msk->bytes_per_line;
 
-#elif defined HAVE_X_WINDOWS
-  /* Use a nominal depth of 24 bpp for pixmap and 1 bpp for mask,
-     to avoid having to query the server. */
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
+  /* Use a nominal depth of 24 and a bpp of 32 for pixmap and 1 bpp
+     for mask, to avoid having to query the server. */
   if (img->pixmap != NO_PIXMAP)
-    size += img->width * img->height * 3;
+    size += img->width * img->height * 4;
   if (img->mask != NO_PIXMAP)
     size += img->width * img->height / 8;
 
@@ -2573,11 +2701,11 @@ compute_image_size (double width, double height,
    finally move the origin back to the top left of the image, which
    may now be a different corner.
 
-   Note that different GUI backends (X, Cairo, w32, NS, Haiku) want
-   the transform matrix defined as transform from the original image
-   to the transformed image, while others want the matrix to describe
-   the transform of the space, which boils down to inverting the
-   matrix.
+   Note that different GUI backends (X, Cairo, w32, NS, Haiku,
+   Android) want the transform matrix defined as transform from the
+   original image to the transformed image, while others want the
+   matrix to describe the transform of the space, which boils down to
+   inverting the matrix.
 
    It's possible to pre-calculate the matrix multiplications and just
    generate one transform matrix that will do everything we need in a
@@ -2619,6 +2747,96 @@ compute_image_rotation (struct image *img, double 
*rotation)
     *rotation = XFIXNUM (reduced_angle);
 }
 
+#ifdef HAVE_ANDROID
+
+static void
+matrix_identity (matrix3x3 matrix)
+{
+  memset (matrix, 0, sizeof (matrix3x3));
+
+  matrix[0][0] = 1.0;
+  matrix[1][1] = 1.0;
+  matrix[2][2] = 1.0;
+}
+
+/* Translate the matrix TRANSFORM to X, Y, and then perform clockwise
+   rotation by the given angle THETA in radians and translate back.
+   As the transform is being performed in a coordinate system where Y
+   grows downwards, the given angle describes a clockwise
+   rotation.  */
+
+static void
+matrix_rotate (matrix3x3 transform, double theta, double x, double y)
+{
+  matrix3x3 temp, copy;
+
+  /* 1. Translate the matrix so X and Y are in the center.  */
+
+  matrix_identity (temp);
+  memcpy (copy, transform, sizeof copy);
+
+  temp[0][2] = x;
+  temp[1][2] = y;
+
+  matrix3x3_mult (copy, temp, transform);
+  matrix_identity (temp);
+  memcpy (copy, transform, sizeof copy);
+
+  /* 2. Rotate the matrix counter-clockwise, assuming a coordinate
+     system where Y grows downwards.  */
+
+  temp[0][0] = cos (theta);
+  temp[0][1] = -sin (theta);
+  temp[1][0] = sinf (theta);
+  temp[1][1] = cosf (theta);
+
+  matrix3x3_mult (copy, temp, transform);
+  matrix_identity (temp);
+  memcpy (copy, transform, sizeof copy);
+
+  /* 3. Translate back.  */
+
+  temp[0][2] = -x;
+  temp[1][2] = -y;
+
+  matrix3x3_mult (copy, temp, transform);
+}
+
+/* Scale the matrix TRANSFORM by -1, and then apply a TX of width, in
+   effect flipping the image horizontally.  */
+
+static void
+matrix_mirror_horizontal (matrix3x3 transform, double width)
+{
+  matrix3x3 temp, copy;
+
+  matrix_identity (temp);
+  memcpy (copy, transform, sizeof copy);
+
+  temp[0][0] = -1.0f;
+  temp[0][2] = width;
+
+  matrix3x3_mult (copy, temp, transform);
+}
+
+static void
+matrix_translate (matrix3x3 transform, float tx, float ty)
+{
+  matrix3x3 temp, copy;
+
+  matrix_identity (temp);
+  memcpy (copy, transform, sizeof copy);
+
+  /* Set the tx and ty.  */
+  temp[0][2] = tx;
+  temp[1][2] = ty;
+
+  /* Multiply it with the transform.  */
+  matrix3x3_mult (copy, temp, transform);
+}
+
+#endif
+
 static void
 image_set_transform (struct frame *f, struct image *img)
 {
@@ -2638,6 +2856,14 @@ image_set_transform (struct frame *f, struct image *img)
   memcpy (&img->transform, identity, sizeof identity);
 #endif
 
+#if defined HAVE_ANDROID
+  matrix3x3 identity = {
+    { 1, 0, 0 },
+    { 0, 1, 0 },
+    { 0, 0, 1 },
+  };
+#endif
+
 # if (defined HAVE_IMAGEMAGICK \
       && !defined DONT_CREATE_TRANSFORMED_IMAGEMAGICK_IMAGE)
   /* ImageMagick images already have the correct transform.  */
@@ -2675,7 +2901,8 @@ image_set_transform (struct frame *f, struct image *img)
   /* Determine flipping.  */
   flip = !NILP (image_spec_value (img->spec, QCflip, NULL));
 
-# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_NS || defined 
HAVE_HAIKU
+# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_NS || defined 
HAVE_HAIKU \
+  || defined HAVE_ANDROID
   /* We want scale up operations to use a nearest neighbor filter to
      show real pixels instead of munging them, but scale down
      operations to use a blended filter, to avoid aliasing and the like.
@@ -2697,7 +2924,7 @@ image_set_transform (struct frame *f, struct image *img)
 
   matrix3x3 matrix
     = {
-# if defined USE_CAIRO || defined HAVE_XRENDER
+# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_ANDROID
        [0][0] = (!IEEE_FLOATING_POINT && width == 0 ? DBL_MAX
                  : img->width / (double) width),
        [1][1] = (!IEEE_FLOATING_POINT && height == 0 ? DBL_MAX
@@ -2720,7 +2947,7 @@ image_set_transform (struct frame *f, struct image *img)
 
   /* Haiku needs this, since the transformation is done on the basis
      of the view, and not the image.  */
-#ifdef HAVE_HAIKU
+#if defined HAVE_HAIKU
   int extra_tx, extra_ty;
 
   extra_tx = 0;
@@ -2731,8 +2958,9 @@ image_set_transform (struct frame *f, struct image *img)
     rotate_flag = 0;
   else
     {
-# if (defined USE_CAIRO || defined HAVE_XRENDER \
-      || defined HAVE_NTGUI || defined HAVE_NS \
+#ifndef HAVE_ANDROID
+# if (defined USE_CAIRO || defined HAVE_XRENDER                \
+      || defined HAVE_NTGUI || defined HAVE_NS         \
       || defined HAVE_HAIKU)
       int cos_r, sin_r;
       if (rotation == 0)
@@ -2759,7 +2987,7 @@ image_set_transform (struct frame *f, struct image *img)
          sin_r = 1;
          rotate_flag = 1;
 
-#ifdef HAVE_HAIKU
+#if defined HAVE_HAIKU
          if (!flip)
            extra_ty = height;
          extra_tx = 0;
@@ -2795,7 +3023,7 @@ image_set_transform (struct frame *f, struct image *img)
 
       if (0 < rotate_flag)
        {
-#  if defined USE_CAIRO || defined HAVE_XRENDER
+#  if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_ANDROID
          /* 1. Translate so (0, 0) is in the center of the image.  */
          matrix3x3 t
            = { [0][0] = 1,
@@ -2846,6 +3074,93 @@ image_set_transform (struct frame *f, struct image *img)
          img->height = height;
        }
 # endif
+#else
+      /* Calculate the inverse transform from the destination to the
+        source.  The matrix is currently identity with scale
+        applied.
+
+         This code makes more sense to me than what lies above.  But
+         I'm not touching what works.  */
+
+      if (rotation != 0 && rotation != 90
+         && rotation != 180 && rotation != 270)
+       {
+         rotate_flag = 0;
+         goto bail;
+       }
+
+      rotate_flag = 1;
+
+      switch ((int) rotation + (flip ? 1 : 0))
+       {
+       case 0:
+         break;
+
+       case 90:
+         /* Rotate the image 90 degrees clockwise.  IOW, rotate the
+            destination by 90 degrees counterclockwise, which is 270
+            degrees clockwise.  */
+         matrix_rotate (matrix, M_PI * 1.5, 0, 0);
+         matrix_translate (matrix, -height, 0);
+         break;
+
+       case 180:
+         /* Apply clockwise 180 degree rotation around the
+            center.  */
+         matrix_rotate (matrix, M_PI, width / 2.0, height / 2.0);
+         break;
+
+       case 270:
+         /* Apply 270 degree counterclockwise rotation to the
+            destination, which is 90 degrees clockwise.  */
+         matrix_rotate (matrix, M_PI * 0.5, 0, 0);
+         matrix_translate (matrix, 0, -width);
+         break;
+
+       case 1:
+         /* Flipped.  Apply horizontal flip.  */
+         matrix_mirror_horizontal (matrix, width);
+         break;
+
+       case 91:
+         /* Apply a flip but otherwise treat this the same as 90.  */
+         matrix_rotate (matrix, M_PI * 1.5, 0, 0);
+         matrix_translate (matrix, -height, 0);
+         matrix_mirror_horizontal (matrix, height);
+         break;
+
+       case 181:
+         /* Flipped 180 degrees.  Apply a flip and treat this the
+            same as 180.  */
+         matrix_rotate (matrix, M_PI, width / 2.0, height / 2.0);
+         matrix_mirror_horizontal (matrix, width);
+         break;
+
+       case 271:
+         /* Flipped 270 degrees.  Apply a flip and treat this the
+            same as 270.  */
+         matrix_rotate (matrix, M_PI * 0.5, 0, 0);
+         matrix_translate (matrix, 0, -width);
+         matrix_mirror_horizontal (matrix, height);
+         break;
+       }
+
+      /* Now set img->width and img->height.  Flip them if the
+        rotation being applied requires so.  */
+
+      if (rotation != 270 && rotation != 90)
+       {
+         img->width = width;
+         img->height = height;
+       }
+      else
+       {
+         img->height = width;
+         img->width = height;
+       }
+    bail:
+      ;
+#endif
     }
 
   if (rotate_flag < 0)
@@ -2910,6 +3225,103 @@ image_set_transform (struct frame *f, struct image *img)
       img->transform[0][2] = extra_tx;
       img->transform[1][2] = extra_ty;
     }
+# elif defined HAVE_ANDROID
+  /* Create a new image of the right size, then turn it into a pixmap
+     and set that as img->pixmap.  Destroy img->mask for now (this is
+     not right.)  */
+
+  struct android_image *transformed_image, *image;
+  struct android_transform transform;
+
+  /* If there is no transform, simply return.  */
+  if (!memcmp (&matrix, &identity, sizeof matrix))
+    return;
+
+  /* First, get the source image.  */
+  image = image_get_x_image (f, img, false);
+
+  /* Make the transformed image.  */
+  transformed_image = android_create_image (image->depth,
+                                           ANDROID_Z_PIXMAP,
+                                           NULL, img->width,
+                                           img->height);
+
+  /* Allocate memory for that image.  */
+  transformed_image->data
+    = xmalloc (transformed_image->bytes_per_line
+              * transformed_image->height);
+
+  /* Do the transform.  */
+  transform.m1 = matrix[0][0];
+  transform.m2 = matrix[0][1];
+  transform.m3 = matrix[0][2];
+  transform.m4 = matrix[1][0];
+  transform.m5 = matrix[1][1];
+  transform.m6 = matrix[1][2];
+
+  if (image->depth == 24 && smoothing)
+    android_project_image_bilinear (image, transformed_image,
+                                   &transform);
+  else
+    android_project_image_nearest (image, transformed_image,
+                                  &transform);
+
+  image_unget_x_image (img, false, image);
+
+  /* Now replace the image.  */
+
+  if (img->ximg)
+    image_destroy_x_image (img->ximg);
+
+  img->ximg = transformed_image;
+
+#ifndef ANDROID_STUBIFY
+  /* Then replace the pixmap.  */
+  android_free_pixmap (img->pixmap);
+
+  /* In case android_create_pixmap signals.  */
+  img->pixmap = ANDROID_NONE;
+  img->pixmap = android_create_pixmap (img->width, img->height,
+                                      transformed_image->depth);
+  android_put_image (img->pixmap, transformed_image);
+#else
+  emacs_abort ();
+#endif
+
+  /* Now, transform the mask.  The mask should be depth 1, and is
+     always transformed using a nearest neighbor filter.  */
+
+  if (img->mask_img || img->mask)
+    {
+      image = image_get_x_image (f, img, true);
+      transformed_image = android_create_image (1, ANDROID_Z_PIXMAP,
+                                               NULL, img->width,
+                                               img->height);
+      transformed_image->data
+       = xmalloc (transformed_image->bytes_per_line
+                  * transformed_image->height);
+      android_project_image_nearest (image, transformed_image,
+                                    &transform);
+      image_unget_x_image (img, true, image);
+
+      /* Now replace the image.  */
+
+      if (img->mask_img)
+       image_destroy_x_image (img->mask_img);
+
+      img->mask_img = transformed_image;
+
+#ifndef ANDROID_STUBIFY
+      if (img->mask)
+       android_free_pixmap (img->mask);
+
+      img->mask = ANDROID_NONE;
+      img->mask = android_create_pixmap (img->width, img->height, 1);
+      android_put_image (img->mask, transformed_image);
+#endif
+    }
+
+  /* Done! */
 #endif
 }
 
@@ -3217,9 +3629,12 @@ mark_image_cache (struct image_cache *c)
 
 /***********************************************************************
                          X / NS / W32 support code
+             Most of this code is shared with Android to make
+             it easier to maintain.
  ***********************************************************************/
 
-#ifdef HAVE_X_WINDOWS
+#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID
+
 static bool
 x_check_image_size (XImage *ximg, int width, int height)
 {
@@ -3235,7 +3650,11 @@ x_check_image_size (XImage *ximg, int width, int height)
   int bitmap_pad, depth, bytes_per_line;
   if (ximg)
     {
+#ifndef HAVE_ANDROID
       bitmap_pad = ximg->bitmap_pad;
+#else
+      bitmap_pad = (ximg->depth == 1 ? 8 : 32);
+#endif
       depth = ximg->depth;
       bytes_per_line = ximg->bytes_per_line;
     }
@@ -3253,16 +3672,23 @@ static bool
 x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth,
                             XImage **ximg, Pixmap *pixmap)
 {
+#ifndef HAVE_ANDROID
   Display *display = FRAME_X_DISPLAY (f);
   Drawable drawable = FRAME_X_DRAWABLE (f);
+#endif
 
   eassert (input_blocked_p ());
 
   if (depth <= 0)
     depth = FRAME_DISPLAY_INFO (f)->n_planes;
+#ifndef HAVE_ANDROID
   *ximg = XCreateImage (display, FRAME_X_VISUAL (f),
                        depth, ZPixmap, 0, NULL, width, height,
                        depth > 16 ? 32 : depth > 8 ? 16 : 8, 0);
+#else
+  *ximg = android_create_image (depth, ANDROID_Z_PIXMAP, NULL, width,
+                               height);
+#endif
   if (*ximg == NULL)
     {
       image_error ("Unable to allocate X image");
@@ -3282,7 +3708,15 @@ x_create_x_image_and_pixmap (struct frame *f, int width, 
int height, int depth,
   (*ximg)->data = xmalloc ((*ximg)->bytes_per_line * height);
 
   /* Allocate a pixmap of the same size.  */
+#ifndef HAVE_ANDROID
   *pixmap = XCreatePixmap (display, drawable, width, height, depth);
+#else
+#ifndef ANDROID_STUBIFY
+  *pixmap = android_create_pixmap (width, height, depth);
+#else
+  emacs_abort ();
+#endif
+#endif
   if (*pixmap == NO_PIXMAP)
     {
       x_destroy_x_image (*ximg);
@@ -3303,7 +3737,11 @@ x_destroy_x_image (XImage *ximg)
       ximg->data = NULL;
     }
 
+#ifndef HAVE_ANDROID
   XDestroyImage (ximg);
+#else
+  android_destroy_image (ximg);
+#endif
 }
 
 # if !defined USE_CAIRO && defined HAVE_XRENDER
@@ -3370,7 +3808,7 @@ x_create_xrender_picture (struct frame *f, Emacs_Pixmap 
pixmap, int depth)
 static bool
 image_check_image_size (Emacs_Pix_Container ximg, int width, int height)
 {
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
   return x_check_image_size (ximg, width, height);
 #else
   /* FIXME: Implement this check for the HAVE_NS and HAVE_NTGUI cases.
@@ -3408,7 +3846,7 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int 
width, int height, int d
 
   *pimg = *pixmap;
   return 1;
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
   if (!x_create_x_image_and_pixmap (f, width, height, depth, pimg, pixmap))
     return 0;
 # ifdef HAVE_XRENDER
@@ -3554,7 +3992,7 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int 
width, int height, int d
 static void
 image_destroy_x_image (Emacs_Pix_Container pimg)
 {
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
   x_destroy_x_image (pimg);
 #else
   eassert (input_blocked_p ());
@@ -3593,7 +4031,9 @@ gui_put_x_image (struct frame *f, Emacs_Pix_Container 
pimg,
   XPutImage (FRAME_X_DISPLAY (f), pixmap, gc, pimg, 0, 0, 0, 0,
              pimg->width, pimg->height);
   XFreeGC (FRAME_X_DISPLAY (f), gc);
-#endif /* HAVE_X_WINDOWS */
+#elif defined HAVE_ANDROID
+  android_put_image (pixmap, pimg);
+#endif
 
 #ifdef HAVE_NS
   eassert (pimg == pixmap);
@@ -3630,7 +4070,7 @@ static void
 image_put_x_image (struct frame *f, struct image *img, Emacs_Pix_Container 
ximg,
                   bool mask_p)
 {
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
   if (!mask_p)
     {
       eassert (img->ximg == NULL);
@@ -3648,7 +4088,7 @@ image_put_x_image (struct frame *f, struct image *img, 
Emacs_Pix_Container ximg,
 #endif
 }
 
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
 /* Put the X images recorded in IMG on frame F into pixmaps, then free
    the X images and their buffers.  */
 
@@ -3704,7 +4144,7 @@ image_get_x_image (struct frame *f, struct image *img, 
bool mask_p)
 {
 #if defined USE_CAIRO || defined (HAVE_HAIKU)
   return !mask_p ? img->pixmap : img->mask;
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
   XImage *ximg_in_img = !mask_p ? img->ximg : img->mask_img;
 
   if (ximg_in_img)
@@ -3714,9 +4154,15 @@ image_get_x_image (struct frame *f, struct image *img, 
bool mask_p)
     return XGetImage (FRAME_X_DISPLAY (f), !mask_p ? img->pixmap : img->mask,
                      0, 0, img->original_width, img->original_height, ~0, 
ZPixmap);
 #endif
+#ifndef HAVE_ANDROID
   else
     return XGetImage (FRAME_X_DISPLAY (f), !mask_p ? img->pixmap : img->mask,
                      0, 0, img->width, img->height, ~0, ZPixmap);
+#else
+  else
+    return android_get_image (!mask_p ? img->pixmap : img->mask,
+                             ANDROID_Z_PIXMAP);
+#endif
 #elif defined (HAVE_NS)
   Emacs_Pix_Container pixmap = !mask_p ? img->pixmap : img->mask;
 
@@ -3729,13 +4175,18 @@ static void
 image_unget_x_image (struct image *img, bool mask_p, Emacs_Pix_Container ximg)
 {
 #ifdef USE_CAIRO
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
   XImage *ximg_in_img = !mask_p ? img->ximg : img->mask_img;
 
   if (ximg_in_img)
     eassert (ximg == ximg_in_img);
+#ifdef HAVE_ANDROID
+  else
+    android_destroy_image (ximg);
+#else
   else
     XDestroyImage (ximg);
+#endif
 #elif defined (HAVE_NS)
   ns_release_object (ximg);
 #endif
@@ -3754,10 +4205,11 @@ image_unget_x_image (struct image *img, bool mask_p, 
Emacs_Pix_Container ximg)
    PFD is null, do not open the file.  */
 
 static Lisp_Object
-image_find_image_fd (Lisp_Object file, int *pfd)
+image_find_image_fd (Lisp_Object file, image_fd *pfd)
 {
   Lisp_Object file_found, search_path;
   int fd;
+  void *platform;
 
   /* TODO I think this should use something like image-load-path
      instead.  Unfortunately, that can contain non-string elements.  */
@@ -3766,8 +4218,10 @@ image_find_image_fd (Lisp_Object file, int *pfd)
                       Vx_bitmap_file_path);
 
   /* Try to find FILE in data-directory/images, then x-bitmap-file-path.  */
+  platform = NULL;
   fd = openp (search_path, file, Qnil, &file_found,
-             pfd ? Qt : make_fixnum (R_OK), false, false);
+             pfd ? Qt : make_fixnum (R_OK), false, false,
+             pfd ? &platform : NULL);
   if (fd == -2)
     {
       /* The file exists locally, but has a file name handler.
@@ -3777,10 +4231,23 @@ image_find_image_fd (Lisp_Object file, int *pfd)
       Lisp_Object encoded_name = ENCODE_FILE (file_found);
       fd = emacs_open (SSDATA (encoded_name), O_RDONLY, 0);
     }
-  else if (fd < 0)
+  /* FD is -3 if PLATFORM is set to a valid asset file descriptor on
+     Android.  */
+  else if (fd < 0 && fd != -3)
     return Qnil;
+
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
   if (pfd)
     *pfd = fd;
+#else
+  /* Construct an asset file descriptor.  */
+
+  if (pfd)
+    {
+      pfd->fd = fd;
+      pfd->asset = platform;
+    }
+#endif
   return file_found;
 }
 
@@ -3794,15 +4261,26 @@ image_find_image_file (Lisp_Object file)
   return image_find_image_fd (file, 0);
 }
 
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+
+static void
+close_android_fd (void *ptr)
+{
+  android_close_asset (*(struct android_fd_or_asset *) ptr);
+}
+
+#endif
+
 /* Read FILE into memory.  Value is a pointer to a buffer allocated
    with xmalloc holding FILE's contents.  Value is null if an error
    occurred.  FD is a file descriptor open for reading FILE.  Set
    *SIZE to the size of the file.  */
 
 static char *
-slurp_file (int fd, ptrdiff_t *size)
+slurp_file (image_fd fd, ptrdiff_t *size)
 {
-  FILE *fp = fdopen (fd, "rb");
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+  FILE *fp = emacs_fdopen (fd, "rb");
 
   char *buf = NULL;
   struct stat st;
@@ -3812,7 +4290,7 @@ slurp_file (int fd, ptrdiff_t *size)
       specpdl_ref count = SPECPDL_INDEX ();
       record_unwind_protect_ptr (fclose_unwind, fp);
 
-      if (fstat (fileno (fp), &st) == 0
+      if (sys_fstat (fileno (fp), &st) == 0
          && 0 <= st.st_size && st.st_size < min (PTRDIFF_MAX, SIZE_MAX))
        {
          /* Report an error if we read past the purported EOF.
@@ -3830,6 +4308,39 @@ slurp_file (int fd, ptrdiff_t *size)
 
       unbind_to (count, Qnil);
     }
+#else
+  char *buf;
+  struct stat st;
+  specpdl_ref count;
+
+  if (!android_asset_fstat (fd, &st)
+      && (0 <= st.st_size
+         && st.st_size < min (PTRDIFF_MAX, SIZE_MAX)))
+    {
+      count = SPECPDL_INDEX ();
+      record_unwind_protect_ptr (close_android_fd, &fd);
+      buf = xmalloc (st.st_size + 1);
+
+      /* Read one byte past the end of the file.  That allows
+        detecting if the file grows as it is being read.  */
+
+      if (android_asset_read (fd, buf,
+                             st.st_size + 1) == st.st_size)
+       *size = st.st_size;
+      else
+       {
+         xfree (buf);
+         buf = NULL;
+       }
+
+      unbind_to (count, Qnil);
+    }
+  else
+    {
+      buf = NULL;
+      android_close_asset (fd);
+    }
+#endif
 
   return buf;
 }
@@ -4258,6 +4769,15 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f, struct 
image *img, char *data,
     img->picture = x_create_xrender_picture (f, img->pixmap, 0);
 # endif
 
+#elif defined HAVE_ANDROID
+#ifndef ANDROID_STUBIFY
+  img->pixmap
+    = android_create_pixmap_from_bitmap_data (data, img->width, img->height,
+                                             fg, bg,
+                                             FRAME_DISPLAY_INFO (f)->n_planes);
+#else
+  emacs_abort ();
+#endif
 #elif defined HAVE_NTGUI
   img->pixmap
     = w32_create_pixmap_from_bitmap_data (img->width, img->height, data);
@@ -4543,7 +5063,7 @@ xbm_load (struct frame *f, struct image *img)
   file_name = image_spec_value (img->spec, QCfile, NULL);
   if (STRINGP (file_name))
     {
-      int fd;
+      image_fd fd;
       Lisp_Object file = image_find_image_fd (file_name, &fd);
       if (!STRINGP (file))
        {
@@ -4688,7 +5208,8 @@ xbm_load (struct frame *f, struct image *img)
                              XPM images
  ***********************************************************************/
 
-#if defined (HAVE_XPM) || defined (HAVE_NS) || defined (HAVE_PGTK)
+#if defined (HAVE_XPM) || defined (HAVE_NS) || defined (HAVE_PGTK) \
+  || defined (HAVE_ANDROID)
 
 static bool xpm_image_p (Lisp_Object object);
 static bool xpm_load (struct frame *f, struct image *img);
@@ -4718,7 +5239,8 @@ static bool xpm_load (struct frame *f, struct image *img);
 #endif /* not HAVE_NTGUI */
 #endif /* HAVE_XPM */
 
-#if defined HAVE_XPM || defined USE_CAIRO || defined HAVE_NS || defined 
HAVE_HAIKU
+#if defined HAVE_XPM || defined USE_CAIRO || defined HAVE_NS   \
+  || defined HAVE_HAIKU || defined HAVE_ANDROID
 
 /* Indices of image specification fields in xpm_format, below.  */
 
@@ -4738,7 +5260,8 @@ enum xpm_keyword_index
   XPM_LAST
 };
 
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined 
HAVE_PGTK
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU  \
+  || defined HAVE_PGTK || defined HAVE_ANDROID
 /* Vector of image_keyword structures describing the format
    of valid XPM image specifications.  */
 
@@ -4980,7 +5503,8 @@ init_xpm_functions (void)
 
 #endif /* WINDOWSNT */
 
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined 
HAVE_PGTK
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU  \
+  || defined HAVE_PGTK || defined HAVE_ANDROID
 /* Value is true if COLOR_SYMBOLS is a valid color symbols list
    for XPM images.  Such a list must consist of conses whose car and
    cdr are strings.  */
@@ -5016,9 +5540,9 @@ xpm_image_p (Lisp_Object object)
          && (! fmt[XPM_COLOR_SYMBOLS].count
              || xpm_valid_color_symbols_p (fmt[XPM_COLOR_SYMBOLS].value)));
 }
-#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU || HAVE_PGTK */
+#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU || HAVE_PGTK || HAVE_ANDROID */
 
-#endif /* HAVE_XPM || USE_CAIRO || HAVE_NS || HAVE_HAIKU */
+#endif /* HAVE_XPM || USE_CAIRO || HAVE_NS || HAVE_HAIKU || HAVE_ANDROID */
 
 #if defined HAVE_XPM && defined HAVE_X_WINDOWS && !defined USE_GTK
 ptrdiff_t
@@ -5391,10 +5915,12 @@ xpm_load (struct frame *f, struct image *img)
 #if (defined USE_CAIRO && defined HAVE_XPM)    \
   || (defined HAVE_NS && !defined HAVE_XPM)    \
   || (defined HAVE_HAIKU && !defined HAVE_XPM)  \
-  || (defined HAVE_PGTK && !defined HAVE_XPM)
+  || (defined HAVE_PGTK && !defined HAVE_XPM)  \
+  || (defined HAVE_ANDROID && !defined HAVE_XPM)
 
-/* XPM support functions for NS and Haiku where libxpm is not available, and 
for
-   Cairo.  Only XPM version 3 (without any extensions) is supported.  */
+/* XPM support functions for NS, Haiku and Android where libxpm is not
+   available, and for Cairo.  Only XPM version 3 (without any
+   extensions) is supported.  */
 
 static void xpm_put_color_table_v (Lisp_Object, const char *,
                                    int, Lisp_Object);
@@ -5833,7 +6359,7 @@ xpm_load (struct frame *f,
   file_name = image_spec_value (img->spec, QCfile, NULL);
   if (STRINGP (file_name))
     {
-      int fd;
+      image_fd fd;
       Lisp_Object file = image_find_image_fd (file_name, &fd);
       if (!STRINGP (file))
        {
@@ -6130,7 +6656,8 @@ lookup_rgb_color (struct frame *f, int r, int g, int b)
 {
 #ifdef HAVE_NTGUI
   return PALETTERGB (r >> 8, g >> 8, b >> 8);
-#elif defined USE_CAIRO || defined HAVE_NS || defined HAVE_HAIKU
+#elif defined USE_CAIRO || defined HAVE_NS || defined HAVE_HAIKU       \
+  || defined HAVE_ANDROID
   return RGB_TO_ULONG (r >> 8, g >> 8, b >> 8);
 #else
   xsignal1 (Qfile_error,
@@ -6203,7 +6730,8 @@ image_to_emacs_colors (struct frame *f, struct image 
*img, bool rgb_p)
   p = colors;
   for (y = 0; y < img->height; ++y)
     {
-#if !defined USE_CAIRO && !defined HAVE_NS && !defined HAVE_HAIKU
+#if !defined USE_CAIRO && !defined HAVE_NS && !defined HAVE_HAIKU      \
+  && !defined HAVE_ANDROID
       Emacs_Color *row = p;
       for (x = 0; x < img->width; ++x, ++p)
        p->pixel = GET_PIXEL (ximg, x, y);
@@ -6211,7 +6739,7 @@ image_to_emacs_colors (struct frame *f, struct image 
*img, bool rgb_p)
         {
           FRAME_TERMINAL (f)->query_colors (f, row, img->width);
         }
-#else  /* USE_CAIRO || HAVE_NS || HAVE_HAIKU */
+#else  /* USE_CAIRO || HAVE_NS || HAVE_HAIKU || HAVE_ANDROID */
       for (x = 0; x < img->width; ++x, ++p)
        {
          p->pixel = GET_PIXEL (ximg, x, y);
@@ -6222,7 +6750,7 @@ image_to_emacs_colors (struct frame *f, struct image 
*img, bool rgb_p)
              p->blue = BLUE16_FROM_ULONG (p->pixel);
            }
        }
-#endif /* USE_CAIRO || HAVE_NS */
+#endif /* USE_CAIRO || HAVE_NS || HAVE_ANDROID */
     }
 
   image_unget_x_image_or_dc (img, 0, ximg, prev);
@@ -6287,7 +6815,11 @@ image_from_emacs_colors (struct frame *f, struct image 
*img, Emacs_Color *colors
   Emacs_Pix_Container ximage;
   Emacs_Color *p;
 
+#ifndef HAVE_ANDROID
   ximage = NULL;
+#else
+  ximage = 0;
+#endif
 
   init_color_table ();
 
@@ -6449,7 +6981,9 @@ image_edge_detection (struct frame *f, struct image *img,
 }
 
 
-#if defined HAVE_X_WINDOWS || defined USE_CAIRO || defined HAVE_HAIKU
+#if defined HAVE_X_WINDOWS || defined USE_CAIRO || defined HAVE_HAIKU  \
+  || defined HAVE_ANDROID
+
 static void
 image_pixmap_draw_cross (struct frame *f, Emacs_Pixmap pixmap,
                         int x, int y, unsigned int width, unsigned int height,
@@ -6485,8 +7019,21 @@ image_pixmap_draw_cross (struct frame *f, Emacs_Pixmap 
pixmap,
   XFreeGC (dpy, gc);
 #elif HAVE_HAIKU
   be_draw_cross_on_pixmap (pixmap, x, y, width, height, color);
+#elif HAVE_ANDROID
+#ifndef ANDROID_STUBIFY
+  struct android_gc *gc;
+
+  gc = android_create_gc (0, NULL);
+  android_set_foreground (gc, color);
+  android_draw_line (pixmap, gc, x, y, x + width - 1, y + height - 1);
+  android_draw_line (pixmap, gc, x, y + height - 1, x + width - 1, y);
+  android_free_gc (gc);
+#else
+  emacs_abort ();
+#endif
 #endif
 }
+
 #endif /* HAVE_X_WINDOWS || USE_CAIRO || HAVE_HAIKU */
 
 /* Transform image IMG on frame F so that it looks disabled.  */
@@ -6530,7 +7077,7 @@ image_disable_image (struct frame *f, struct image *img)
 #ifndef HAVE_NTGUI
 #ifndef HAVE_NS  /* TODO: NS support, however this not needed for toolbars */
 
-#if !defined USE_CAIRO && !defined HAVE_HAIKU
+#if !defined USE_CAIRO && !defined HAVE_HAIKU && !defined HAVE_ANDROID
 #define CrossForeground(f) BLACK_PIX_DEFAULT (f)
 #define MaskForeground(f)  WHITE_PIX_DEFAULT (f)
 #else  /* USE_CAIRO || HAVE_HAIKU */
@@ -6841,7 +7388,7 @@ pbm_load (struct frame *f, struct image *img)
 
   if (STRINGP (specified_file))
     {
-      int fd;
+      image_fd fd;
       Lisp_Object file = image_find_image_fd (specified_file, &fd);
       if (!STRINGP (file))
        {
@@ -7509,15 +8056,18 @@ png_load_body (struct frame *f, struct image *img, 
struct png_load_context *c)
   if (NILP (specified_data))
     {
       int fd;
-      Lisp_Object file = image_find_image_fd (specified_file, &fd);
-      if (!STRINGP (file))
+      Lisp_Object file = image_find_image_file (specified_file);
+
+      if (!STRINGP (file)
+         || (fd = emacs_open (SSDATA (ENCODE_FILE (file)),
+                              O_RDONLY, 0)) < 0)
        {
          image_error ("Cannot find image file `%s'", specified_file);
          return 0;
        }
 
       /* Open the image file.  */
-      fp = fdopen (fd, "rb");
+      fp = emacs_fdopen (fd, "rb");
       if (!fp)
        {
          image_error ("Cannot open image file `%s'", file);
@@ -7528,7 +8078,7 @@ png_load_body (struct frame *f, struct image *img, struct 
png_load_context *c)
       if (fread (sig, 1, sizeof sig, fp) != sizeof sig
          || png_sig_cmp (sig, 0, sizeof sig))
        {
-         fclose (fp);
+         emacs_fclose (fp);
          image_error ("Not a PNG file: `%s'", file);
          return 0;
        }
@@ -7582,7 +8132,7 @@ png_load_body (struct frame *f, struct image *img, struct 
png_load_context *c)
     }
   if (! png_ptr)
     {
-      if (fp) fclose (fp);
+      if (fp) emacs_fclose (fp);
       return 0;
     }
 
@@ -7596,7 +8146,7 @@ png_load_body (struct frame *f, struct image *img, struct 
png_load_context *c)
       xfree (c->pixels);
       xfree (c->rows);
       if (c->fp)
-       fclose (c->fp);
+       emacs_fclose (c->fp);
       return 0;
     }
 
@@ -7721,7 +8271,7 @@ png_load_body (struct frame *f, struct image *img, struct 
png_load_context *c)
   png_read_end (png_ptr, info_ptr);
   if (fp)
     {
-      fclose (fp);
+      emacs_fclose (fp);
       c->fp = NULL;
     }
 
@@ -8237,14 +8787,16 @@ jpeg_load_body (struct frame *f, struct image *img,
   if (NILP (specified_data))
     {
       int fd;
-      Lisp_Object file = image_find_image_fd (specified_file, &fd);
-      if (!STRINGP (file))
+      Lisp_Object file = image_find_image_file (specified_file);
+      if (!STRINGP (file)
+         || (fd = emacs_open (SSDATA (ENCODE_FILE (file)),
+                              O_RDONLY, 0)) < 0)
        {
          image_error ("Cannot find image file `%s'", specified_file);
          return 0;
        }
 
-      fp = fdopen (fd, "rb");
+      fp = emacs_fdopen (fd, "rb");
       if (fp == NULL)
        {
          image_error ("Cannot open `%s'", file);
@@ -8284,7 +8836,7 @@ jpeg_load_body (struct frame *f, struct image *img,
 
       /* Close the input file and destroy the JPEG object.  */
       if (fp)
-       fclose (fp);
+       emacs_fclose (fp);
       jpeg_destroy_decompress (&mgr->cinfo);
 
       /* If we already have an XImage, free that.  */
@@ -8379,7 +8931,7 @@ jpeg_load_body (struct frame *f, struct image *img,
   jpeg_finish_decompress (&mgr->cinfo);
   jpeg_destroy_decompress (&mgr->cinfo);
   if (fp)
-    fclose (fp);
+    emacs_fclose (fp);
 
   /* Maybe fill in the background field while we have ximg handy. */
   if (NILP (image_spec_value (img->spec, QCbackground, NULL)))
@@ -9153,11 +9705,16 @@ gif_load (struct frame *f, struct image *img)
 
          /* Get the file size so that we can report it in
             `image-cache-size'.  */
-         struct stat st;
-         FILE *fp = fopen (SSDATA (encoded_file), "rb");
-         if (fstat (fileno (fp), &st) == 0)
-           byte_size = st.st_size;
-         fclose (fp);
+         {
+           struct stat st;
+           int fd;
+
+           fd = emacs_open (SSDATA (encoded_file), O_RDONLY,
+                            0);
+           if (!sys_fstat (fd, &st))
+             byte_size = st.st_size;
+           emacs_close (fd);
+         }
        }
       else
        {
@@ -9735,7 +10292,7 @@ webp_load (struct frame *f, struct image *img)
 
   if (NILP (specified_data))
     {
-      int fd;
+      image_fd fd;
       file = image_find_image_fd (specified_file, &fd);
       if (!STRINGP (file))
        {
@@ -10418,7 +10975,8 @@ imagemagick_load_image (struct frame *f, struct image 
*img,
       return 0;
     }
 
-#ifdef HAVE_MAGICKAUTOORIENTIMAGE
+#if defined HAVE_MAGICKAUTOORIENTIMAGE         \
+  || HAVE_DECL_MAGICKAUTOORIENTIMAGE
   /* If no :rotation is explicitly specified, apply the automatic
      rotation from EXIF. */
   if (NILP (image_spec_value (img->spec, QCrotation, NULL)))
@@ -10575,7 +11133,8 @@ imagemagick_load_image (struct frame *f, struct image 
*img,
   {
     MagickWand *new_wand;
     MagickSetImageBackgroundColor (image_wand, bg_wand);
-#ifdef HAVE_MAGICKMERGEIMAGELAYERS
+#if defined HAVE_MAGICKMERGEIMAGELAYERS                \
+  || HAVE_DECL_MAGICKMERGEIMAGELAYERS
     new_wand = MagickMergeImageLayers (image_wand, MergeLayer);
 #else
     new_wand = MagickFlattenImages (image_wand);
@@ -10604,8 +11163,9 @@ imagemagick_load_image (struct frame *f, struct image 
*img,
 
   init_color_table ();
 
-#if defined (HAVE_MAGICKEXPORTIMAGEPIXELS) && \
-  ! defined (HAVE_NS) && ! defined (HAVE_HAIKU)
+#if (defined (HAVE_MAGICKEXPORTIMAGEPIXELS)         \
+     || HAVE_DECL_MAGICKEXPORTIMAGEPIXELS)          \
+  && ! defined (HAVE_NS) && ! defined (HAVE_HAIKU)
   if (imagemagick_render_type != 0)
     {
       /* Magicexportimage is normally faster than pixelpushing.  This
@@ -11131,7 +11691,7 @@ svg_load (struct frame *f, struct image *img)
   base_uri = image_spec_value (img->spec, QCbase_uri, NULL);
   if (STRINGP (file_name))
     {
-      int fd;
+      image_fd fd;
       Lisp_Object file = image_find_image_fd (file_name, &fd);
       if (!STRINGP (file))
        {
@@ -11954,7 +12514,7 @@ The list of capabilities can include one or more of the 
following:
     {
 #ifdef HAVE_NATIVE_TRANSFORMS
 # if defined HAVE_IMAGEMAGICK || defined (USE_CAIRO) || defined (HAVE_NS) \
-  || defined (HAVE_HAIKU)
+  || defined (HAVE_HAIKU) | defined HAVE_ANDROID
       return list2 (Qscale, Qrotate90);
 # elif defined (HAVE_X_WINDOWS) && defined (HAVE_XRENDER)
       if (FRAME_DISPLAY_INFO (f)->xrender_supported_p)
@@ -12065,7 +12625,8 @@ static struct image_type const image_types[] =
  { SYMBOL_INDEX (Qjpeg), jpeg_image_p, jpeg_load, image_clear_image,
    IMAGE_TYPE_INIT (init_jpeg_functions) },
 #endif
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined 
HAVE_PGTK
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU  \
+  || defined HAVE_PGTK || defined HAVE_ANDROID
  { SYMBOL_INDEX (Qxpm), xpm_image_p, xpm_load, image_clear_image,
    IMAGE_TYPE_INIT (init_xpm_functions) },
 #endif
@@ -12227,7 +12788,8 @@ non-numeric, there is no explicit limit on the size of 
images.  */);
   add_image_type (Qxbm);
 
 #if defined (HAVE_XPM) || defined (HAVE_NS) \
-  || defined (HAVE_HAIKU) || defined (HAVE_PGTK)
+  || defined (HAVE_HAIKU) || defined (HAVE_PGTK) \
+  || defined (HAVE_ANDROID)
   DEFSYM (Qxpm, "xpm");
   add_image_type (Qxpm);
 #endif
diff --git a/src/inotify.c b/src/inotify.c
index 7562ffb1701..105ff5a9d8a 100644
--- a/src/inotify.c
+++ b/src/inotify.c
@@ -40,6 +40,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 # define IN_ONLYDIR 0
 #endif
 
+#ifdef HAVE_ANDROID
+#include "android.h" /* For `android_is_special_directory'.  */
+#endif /* HAVE_ANDROID */
+
 /* File handle for inotify.  */
 static int inotifyfd = -1;
 
@@ -419,6 +423,7 @@ IN_ONESHOT  */)
   int wd = -1;
   uint32_t imask = aspect_to_inotifymask (aspect);
   uint32_t mask = imask | IN_MASK_ADD | IN_EXCL_UNLINK;
+  char *name;
 
   CHECK_STRING (filename);
 
@@ -432,7 +437,19 @@ IN_ONESHOT  */)
     }
 
   encoded_file_name = ENCODE_FILE (filename);
-  wd = inotify_add_watch (inotifyfd, SSDATA (encoded_file_name), mask);
+  name = SSDATA (encoded_file_name);
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  /* If FILENAME actually lies in a special directory, return now
+     instead of letting inotify fail.  These directories cannot
+     receive file notifications as they are read only.  */
+
+  if (android_is_special_directory (name, "/assets")
+      || android_is_special_directory (name, "/content"))
+    return Qnil;
+#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
+  wd = inotify_add_watch (inotifyfd, name, mask);
   if (wd < 0)
     report_file_notify_error ("Could not add watch for file", filename);
 
diff --git a/src/keyboard.c b/src/keyboard.c
index 2e850b74b9b..78c88a2859b 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -44,6 +44,15 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "atimer.h"
 #include "process.h"
 #include "menu.h"
+
+#ifdef HAVE_TEXT_CONVERSION
+#include "textconv.h"
+#endif /* HAVE_TEXT_CONVERSION */
+
+#ifdef HAVE_ANDROID
+#include "android.h"
+#endif /* HAVE_ANDROID */
+
 #include <errno.h>
 
 #ifdef HAVE_PTHREAD
@@ -62,6 +71,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 #include "syssignal.h"
 
+#if defined HAVE_STACK_OVERFLOW_HANDLING && !defined WINDOWSNT
+#include <setjmp.h>
+#endif
+
 #include <sys/types.h>
 #include <unistd.h>
 #include <fcntl.h>
@@ -102,6 +115,13 @@ static KBOARD *all_kboards;
 /* True in the single-kboard state, false in the any-kboard state.  */
 static bool single_kboard;
 
+#ifdef HAVE_TEXT_CONVERSION
+
+/* True if a key sequence is currently being read.  */
+bool reading_key_sequence;
+
+#endif /* HAVE_TEXT_CONVERSION */
+
 /* Minimum allowed size of the recent_keys vector.  */
 #define MIN_NUM_RECENT_KEYS (100)
 
@@ -348,6 +368,10 @@ static struct timespec timer_last_idleness_start_time;
 static Lisp_Object virtual_core_pointer_name;
 static Lisp_Object virtual_core_keyboard_name;
 
+/* If not nil, ID of the last TOUCHSCREEN_END_EVENT to land on the
+   menu bar.  */
+static Lisp_Object menu_bar_touch_id;
+
 
 /* Global variable declarations.  */
 
@@ -1270,7 +1294,7 @@ some_mouse_moved (void)
 
 enum { READ_KEY_ELTS = 30 };
 static int read_key_sequence (Lisp_Object *, Lisp_Object,
-                              bool, bool, bool, bool);
+                              bool, bool, bool, bool, bool);
 static void adjust_point_for_property (ptrdiff_t, bool);
 
 static Lisp_Object
@@ -1381,7 +1405,8 @@ command_loop_1 (void)
       /* Read next key sequence; i gets its length.  */
       raw_keybuf_count = 0;
       Lisp_Object keybuf[READ_KEY_ELTS];
-      int i = read_key_sequence (keybuf, Qnil, false, true, true, false);
+      int i = read_key_sequence (keybuf, Qnil, false, true, true, false,
+                                false);
 
       /* A filter may have run while we were reading the input.  */
       if (! FRAME_LIVE_P (XFRAME (selected_frame)))
@@ -1656,7 +1681,8 @@ read_menu_command (void)
   specbind (Qecho_keystrokes, make_fixnum (0));
 
   Lisp_Object keybuf[READ_KEY_ELTS];
-  int i = read_key_sequence (keybuf, Qnil, false, true, true, true);
+  int i = read_key_sequence (keybuf, Qnil, false, true, true, true,
+                            false);
 
   unbind_to (count, Qnil);
 
@@ -3026,6 +3052,7 @@ read_char (int commandflag, Lisp_Object map,
     {
       struct buffer *prev_buffer = current_buffer;
       last_input_event = c;
+
       call4 (Qcommand_execute, tem, Qnil, Fvector (1, &last_input_event), Qt);
 
       if (CONSP (c) && !NILP (Fmemq (XCAR (c), Vwhile_no_input_ignore_events))
@@ -3588,6 +3615,11 @@ readable_events (int flags)
     return 1;
 #endif
 
+#ifdef HAVE_TEXT_CONVERSION
+  if (detect_conversion_events ())
+    return 1;
+#endif
+
   if (!(flags & READABLE_EVENTS_IGNORE_SQUEEZABLES) && some_mouse_moved ())
     return 1;
   if (single_kboard)
@@ -3920,6 +3952,11 @@ kbd_buffer_get_event (KBOARD **kbp,
 
   had_pending_selection_requests = false;
 #endif
+#ifdef HAVE_TEXT_CONVERSION
+  bool had_pending_conversion_events;
+
+  had_pending_conversion_events = false;
+#endif
 
 #ifdef subprocesses
   if (kbd_on_hold_p () && kbd_buffer_nr_stored () < KBD_BUFFER_SIZE / 4)
@@ -3983,6 +4020,13 @@ kbd_buffer_get_event (KBOARD **kbp,
          had_pending_selection_requests = true;
          break;
        }
+#endif
+#ifdef HAVE_TEXT_CONVERSION
+      if (detect_conversion_events ())
+       {
+         had_pending_conversion_events = true;
+         break;
+       }
 #endif
       if (end_time)
        {
@@ -4039,6 +4083,24 @@ kbd_buffer_get_event (KBOARD **kbp,
       return first;
     }
 
+#ifdef HAVE_TEXT_CONVERSION
+  /* There are pending text conversion operations.  Text conversion
+     events should be generated before processing any other keyboard
+     input.  */
+  if (had_pending_conversion_events)
+    {
+      handle_pending_conversion_events ();
+      obj = Qtext_conversion;
+
+      /* See the comment in handle_pending_conversion_events_1.
+         Note that in addition, text conversion events are not
+         generated if no edits were actually made.  */
+      if (conversion_disabled_p ()
+         || NILP (Vtext_conversion_edits))
+       obj = Qnil;
+    }
+  else
+#endif
   /* At this point, we know that there is a readable event available
      somewhere.  If the event queue is empty, then there must be a
      mouse movement enabled and available.  */
@@ -4923,7 +4985,74 @@ static const char *const lispy_accent_keys[] =
   "dead-horn",
 };
 
-#ifdef HAVE_NTGUI
+#ifdef HAVE_ANDROID
+#define FUNCTION_KEY_OFFSET 0
+
+const char *const lispy_function_keys[] =
+  {
+    /* All elements in this array default to 0, except for the few
+       function keys that Emacs recognizes.  */
+    [111] = "escape",
+    [112] = "delete",
+    [120] = "sysrq",
+    [121] = "break",
+    [122] = "home",
+    [123] = "end",
+    [124] = "insert",
+    [126] = "media-play",
+    [127] = "media-pause",
+    [130] = "media-record",
+    [131] = "f1",
+    [132] = "f2",
+    [133] = "f3",
+    [134] = "f4",
+    [135] = "f5",
+    [136] = "f6",
+    [137] = "f7",
+    [138] = "f8",
+    [139] = "f9",
+    [140] = "f10",
+    [141] = "f11",
+    [142] = "f12",
+    [160] = "kp-ret",
+    [164] = "volume-mute",
+    [19]  = "up",
+    [20]  = "down",
+    [213] = "muhenkan",
+    [214] = "henkan",
+    [215] = "hiragana-katakana",
+    [218] = "kana",
+    [21]  = "left",
+    [22]  = "right",
+    [24]  = "volume-up",
+    [259] = "help",
+    [25]  = "volume-down",
+    [268] = "kp-up-left",
+    [269] = "kp-down-left",
+    [270] = "kp-up-right",
+    [271] = "kp-down-right",
+    [272] = "media-skip-forward",
+    [273] = "media-skip-backward",
+    [277] = "cut",
+    [278] = "copy",
+    [279] = "paste",
+    [28]  = "clear",
+    [4]          = "XF86Back",
+    [61]  = "tab",
+    [66]  = "return",
+    [67]  = "backspace",
+    [82]  = "menu",
+    [84]  = "find",
+    [85]  = "media-play-pause",
+    [86]  = "media-stop",
+    [87]  = "media-next",
+    [88]  = "media-previous",
+    [89]  = "media-rewind",
+    [92]  = "prior",
+    [93]  = "next",
+  };
+
+#elif defined HAVE_NTGUI
 #define FUNCTION_KEY_OFFSET 0x0
 
 const char *const lispy_function_keys[] =
@@ -5760,6 +5889,29 @@ coords_in_menu_bar_window (struct frame *f, int x, int y)
 
 #endif
 
+#ifdef HAVE_WINDOW_SYSTEM
+
+/* Return whether or not the coordinates X and Y are inside the
+   tab-bar window of the given frame F.  */
+
+static bool
+coords_in_tab_bar_window (struct frame *f, int x, int y)
+{
+  struct window *window;
+
+  if (!WINDOWP (f->tab_bar_window))
+    return false;
+
+  window = XWINDOW (f->tab_bar_window);
+
+  return (y >= WINDOW_TOP_EDGE_Y (window)
+         && x >= WINDOW_LEFT_EDGE_X (window)
+         && y <= WINDOW_BOTTOM_EDGE_Y (window)
+         && x <= WINDOW_RIGHT_EDGE_X (window));
+}
+
+#endif /* HAVE_WINDOW_SYSTEM */
+
 /* Given a struct input_event, build the lisp event which represents
    it.  If EVENT is 0, build a mouse movement event from the mouse
    movement buffer, which should have a movement event in it.
@@ -6069,6 +6221,11 @@ make_lispy_event (struct input_event *event)
                          }
                      }
 
+                   /* Don't generate a menu bar event if ITEM is
+                      nil.  */
+                   if (NILP (item))
+                     return Qnil;
+
                    /* ELisp manual 2.4b says (x y) are window
                       relative but code says they are
                       frame-relative.  */
@@ -6406,18 +6563,77 @@ make_lispy_event (struct input_event *event)
     case TOUCHSCREEN_BEGIN_EVENT:
       {
        Lisp_Object x, y, id, position;
-       struct frame *f = XFRAME (event->frame_or_window);
+       struct frame *f;
+#ifdef HAVE_WINDOW_SYSTEM
+       int tab_bar_item;
+       bool close;
+#endif /* HAVE_WINDOW_SYSTEM */
+
+       f = XFRAME (event->frame_or_window);
+
+       if (!FRAME_LIVE_P (f))
+         return Qnil;
 
        id = event->arg;
        x = event->x;
        y = event->y;
 
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
+       if (coords_in_menu_bar_window (f, XFIXNUM (x), XFIXNUM (y)))
+         {
+           /* If the tap began in the menu bar window, then save the
+              id.  */
+           menu_bar_touch_id = id;
+           return Qnil;
+         }
+#endif /* defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR */
+
        position = make_lispy_position (f, x, y, event->timestamp);
 
-       return list2 (((event->kind
-                       == TOUCHSCREEN_BEGIN_EVENT)
-                      ? Qtouchscreen_begin
-                      : Qtouchscreen_end),
+#ifdef HAVE_WINDOW_SYSTEM
+
+       /* Now check if POSITION lies on the tab bar.  If so, look up
+          the corresponding tab bar item's propertized string as the
+          OBJECT.  */
+
+       if (coords_in_tab_bar_window (f, XFIXNUM (event->x),
+                                     XFIXNUM (event->y))
+           /* `get_tab_bar_item_kbd' returns 0 if the item was
+              previously highlighted, 1 otherwise, and -1 if there is
+              no tab bar item.  */
+           && get_tab_bar_item_kbd (f, XFIXNUM (event->x),
+                                    XFIXNUM (event->y), &tab_bar_item,
+                                    &close) >= 0)
+         {
+           /* First, obtain the propertized string.  */
+           x = Fcopy_sequence (AREF (f->tab_bar_items,
+                                     (tab_bar_item
+                                      + TAB_BAR_ITEM_CAPTION)));
+
+           /* Next, add the key binding.  */
+           AUTO_LIST2 (y, Qmenu_item, list3 (AREF (f->tab_bar_items,
+                                                   (tab_bar_item
+                                                    + TAB_BAR_ITEM_KEY)),
+                                             AREF (f->tab_bar_items,
+                                                   (tab_bar_item
+                                                    + TAB_BAR_ITEM_BINDING)),
+                                             close ? Qt : Qnil));
+
+           /* And add the new properties to the propertized string.  */
+           Fadd_text_properties (make_fixnum (0),
+                                 make_fixnum (SCHARS (x)),
+                                 y, x);
+
+           /* Set the position to 0.  */
+           x = Fcons (x, make_fixnum (0));
+
+           /* Finally, add the OBJECT.  */
+           position = nconc2 (position, Fcons (x, Qnil));
+         }
+
+#endif /* HAVE_WINDOW_SYSTEM */
+
+       return list2 (Qtouchscreen_begin,
                      Fcons (id, position));
       }
 
@@ -6425,18 +6641,125 @@ make_lispy_event (struct input_event *event)
       {
        Lisp_Object x, y, id, position;
        struct frame *f = XFRAME (event->frame_or_window);
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
+       int column, row, dummy;
+#endif /* defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR */
+#ifdef HAVE_WINDOW_SYSTEM
+       int tab_bar_item;
+       bool close;
+#endif /* HAVE_WINDOW_SYSTEM */
+
+       if (!FRAME_LIVE_P (f))
+         return Qnil;
 
        id = event->arg;
        x = event->x;
        y = event->y;
 
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
+       if (EQ (menu_bar_touch_id, id))
+         {
+           /* This touch should activate the menu bar.  Generate the
+              menu bar event.  */
+           menu_bar_touch_id = Qnil;
+
+           if (!NILP (f->menu_bar_window))
+             {
+               x_y_to_hpos_vpos (XWINDOW (f->menu_bar_window), XFIXNUM (x),
+                                 XFIXNUM (y), &column, &row, NULL, NULL,
+                                 &dummy);
+
+               if (row >= 0 && row < FRAME_MENU_BAR_LINES (f))
+                 {
+                   Lisp_Object items, item;
+
+                   /* Find the menu bar item under `column'.  */
+                   item = Qnil;
+                   items = FRAME_MENU_BAR_ITEMS (f);
+                   for (i = 0; i < ASIZE (items); i += 4)
+                     {
+                       Lisp_Object pos, string;
+                       string = AREF (items, i + 1);
+                       pos = AREF (items, i + 3);
+                       if (NILP (string))
+                         break;
+                       if (column >= XFIXNUM (pos)
+                           && column < XFIXNUM (pos) + SCHARS (string))
+                         {
+                           item = AREF (items, i);
+                           break;
+                         }
+                     }
+
+                   /* Don't generate a menu bar event if ITEM is
+                      nil.  */
+                   if (NILP (item))
+                     return Qnil;
+
+                   /* ELisp manual 2.4b says (x y) are window
+                      relative but code says they are
+                      frame-relative.  */
+                   position = list4 (event->frame_or_window,
+                                     Qmenu_bar,
+                                     Fcons (event->x, event->y),
+                                     INT_TO_INTEGER (event->timestamp));
+
+                   return list2 (item, position);
+                 }
+             }
+
+           return Qnil;
+         }
+#endif /* defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR */
+
        position = make_lispy_position (f, x, y, event->timestamp);
 
-       return list3 (((event->kind
-                       == TOUCHSCREEN_BEGIN_EVENT)
-                      ? Qtouchscreen_begin
-                      : Qtouchscreen_end),
-                     Fcons (id, position),
+#ifdef HAVE_WINDOW_SYSTEM
+
+       /* Now check if POSITION lies on the tab bar.  If so, look up
+          the corresponding tab bar item's propertized string as the
+          OBJECT.  */
+
+       if (coords_in_tab_bar_window (f, XFIXNUM (event->x),
+                                     XFIXNUM (event->y))
+           /* `get_tab_bar_item_kbd' returns 0 if the item was
+              previously highlighted, 1 otherwise, and -1 if there is
+              no tab bar item.  */
+           && get_tab_bar_item_kbd (f, XFIXNUM (event->x),
+                                    XFIXNUM (event->y), &tab_bar_item,
+                                    &close) >= 0)
+         {
+           /* First, obtain the propertized string.  */
+           x = Fcopy_sequence (AREF (f->tab_bar_items,
+                                     (tab_bar_item
+                                      + TAB_BAR_ITEM_CAPTION)));
+
+           /* Next, add the key binding.  */
+           AUTO_LIST2 (y, Qmenu_item, list3 (AREF (f->tab_bar_items,
+                                                   (tab_bar_item
+                                                    + TAB_BAR_ITEM_KEY)),
+                                             AREF (f->tab_bar_items,
+                                                   (tab_bar_item
+                                                    + TAB_BAR_ITEM_BINDING)),
+                                             close ? Qt : Qnil));
+
+           /* And add the new properties to the propertized string.  */
+           Fadd_text_properties (make_fixnum (0),
+                                 make_fixnum (SCHARS (x)),
+                                 y, x);
+
+           /* Set the position to 0.  */
+           x = Fcons (x, make_fixnum (0));
+
+           /* Finally, add the OBJECT.  */
+           position = nconc2 (position, Fcons (x, Qnil));
+         }
+
+#endif /* HAVE_WINDOW_SYSTEM */
+
+       position = make_lispy_position (f, x, y, event->timestamp);
+
+       return list3 (Qtouchscreen_end, Fcons (id, position),
                      event->modifiers ? Qt : Qnil);
       }
 
@@ -6462,6 +6785,9 @@ make_lispy_event (struct input_event *event)
        struct frame *f = XFRAME (event->frame_or_window);
        evt = Qnil;
 
+       if (!FRAME_LIVE_P (f))
+         return Qnil;
+
        for (tem = event->arg; CONSP (tem); tem = XCDR (tem))
          {
            it = XCAR (tem);
@@ -6470,10 +6796,19 @@ make_lispy_event (struct input_event *event)
            y = XCAR (XCDR (it));
            id = XCAR (XCDR (XCDR (it)));
 
+           /* Don't report touches to the menu bar.  */
+           if (EQ (id, menu_bar_touch_id))
+             continue;
+
            position = make_lispy_position (f, x, y, event->timestamp);
            evt = Fcons (Fcons (id, position), evt);
          }
 
+       if (NILP (evt))
+         /* Don't return an event if the touchpoint list is
+            empty.  */
+         return Qnil;
+
        return list2 (Qtouchscreen_update, evt);
       }
 
@@ -7673,6 +8008,14 @@ tty_read_avail_input (struct terminal *terminal,
 static void
 handle_async_input (void)
 {
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  /* Check and respond to an ``urgent'' query from the UI thread.
+     A query becomes urgent once the UI thread has been waiting
+     for more than two seconds.  */
+
+  android_check_query_urgent ();
+#endif /* HAVE_ANDROID && !ANDROID_STUBIFY */
+
 #ifndef DOS_NT
   while (1)
     {
@@ -7741,6 +8084,16 @@ totally_unblock_input (void)
 void
 handle_input_available_signal (int sig)
 {
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  /* Make all writes from the Android UI thread visible.  If
+     `android_urgent_query' has been set, preceding writes to query
+     related variables should become observable here on as well.  */
+#if defined __aarch64__
+  asm ("dmb ishst");
+#else /* !defined __aarch64__ */
+  __atomic_thread_fence (__ATOMIC_SEQ_CST);
+#endif /* defined __aarch64__ */
+#endif /* HAVE_ANDROID && !ANDROID_STUBIFY */
   pending_signals = true;
 
   if (input_available_clear_time)
@@ -9070,7 +9423,13 @@ set_prop (ptrdiff_t idx, Lisp_Object val)
 
    - `:label LABEL-STRING'.
 
-   A text label to show with the tool bar button if labels are enabled.  */
+   A text label to show with the tool bar button if labels are
+   enabled.
+
+   - `:wrap WRAP'
+
+   WRAP specifies whether to hide this item but display subsequent
+   tool bar items on a new line.  */
 
 static bool
 parse_tool_bar_item (Lisp_Object key, Lisp_Object item)
@@ -9078,7 +9437,15 @@ parse_tool_bar_item (Lisp_Object key, Lisp_Object item)
   Lisp_Object filter = Qnil;
   Lisp_Object caption;
   int i;
-  bool have_label = false;
+  bool have_label;
+#ifndef HAVE_EXT_TOOL_BAR
+  bool is_wrap;
+#endif /* HAVE_EXT_TOOL_BAR */
+
+  have_label = false;
+#ifndef HAVE_EXT_TOOL_BAR
+  is_wrap = false;
+#endif /* HAVE_EXT_TOOL_BAR */
 
   /* Definition looks like `(menu-item CAPTION BINDING PROPS...)'.
      Rule out items that aren't lists, don't start with
@@ -9214,6 +9581,20 @@ parse_tool_bar_item (Lisp_Object key, Lisp_Object item)
       else if (EQ (ikey, QCrtl))
         /* ':rtl STRING' */
        set_prop (TOOL_BAR_ITEM_RTL_IMAGE, value);
+      else if (EQ (ikey, QCwrap))
+       {
+#ifndef HAVE_EXT_TOOL_BAR
+         /* This specifies whether the tool bar item should be hidden
+            but cause subsequent items to be displayed on a new
+            line.  */
+         set_prop (TOOL_BAR_ITEM_WRAP, value);
+         is_wrap = !NILP (value);
+#else /* HAVE_EXT_TOOL_BAR */
+         /* Line wrapping isn't supported on builds utilizing
+            external tool bars.  */
+         return false;
+#endif /* !HAVE_EXT_TOOL_BAR */
+       }
     }
 
 
@@ -9274,6 +9655,15 @@ parse_tool_bar_item (Lisp_Object key, Lisp_Object item)
   if (CONSP (get_keymap (PROP (TOOL_BAR_ITEM_BINDING), 0, 1)))
     return 0;
 
+
+#ifndef HAVE_EXT_TOOL_BAR
+  /* If the menu item is actually a line wrap, make sure it isn't
+     visible or enabled.  */
+
+  if (is_wrap)
+    set_prop (TOOL_BAR_ITEM_ENABLED_P, Qnil);
+#endif /* !HAVE_EXT_TOOL_BAR */
+
   /* If there is a key binding, add it to the help, which will be
      displayed as a tooltip for this entry. */
   Lisp_Object binding = PROP (TOOL_BAR_ITEM_BINDING);
@@ -9695,13 +10085,18 @@ typedef struct keyremap
    If the mapping is a function and DO_FUNCALL is true,
    the function is called with PROMPT as parameter and its return
    value is used as the return value of this function (after checking
-   that it is indeed a vector).  */
+   that it is indeed a vector).
+
+   START and END are the indices of the first and last key of the
+   sequence being remapped within the keyboard buffer KEYBUF.  */
 
 static Lisp_Object
 access_keymap_keyremap (Lisp_Object map, Lisp_Object key, Lisp_Object prompt,
-                       bool do_funcall)
+                       bool do_funcall, unsigned int start, unsigned int end,
+                       Lisp_Object *keybuf)
 {
   Lisp_Object next;
+  specpdl_ref count;
 
   next = access_keymap (map, key, 1, 0, 1);
 
@@ -9717,10 +10112,18 @@ access_keymap_keyremap (Lisp_Object map, Lisp_Object 
key, Lisp_Object prompt,
      its value instead.  */
   if (do_funcall && FUNCTIONP (next))
     {
-      Lisp_Object tem;
+      Lisp_Object tem, remap;
       tem = next;
 
-      next = call1 (next, prompt);
+      /* Build Vcurrent_key_remap_sequence.  */
+      remap = Fvector (end - start + 1, keybuf + start);
+
+      /* Bind `current-key-remap-sequence' to the key sequence being
+        remapped.  */
+      count = SPECPDL_INDEX ();
+      specbind (Qcurrent_key_remap_sequence, remap);
+      next = unbind_to (count, call1 (next, prompt));
+
       /* If the function returned something invalid,
         barf--don't ignore it.  */
       if (! (NILP (next) || VECTORP (next) || STRINGP (next)))
@@ -9745,11 +10148,17 @@ keyremap_step (Lisp_Object *keybuf, volatile keyremap 
*fkey,
               int input, bool doit, int *diff, Lisp_Object prompt)
 {
   Lisp_Object next, key;
+  ptrdiff_t buf_start, buf_end;
+
+  /* Save the key sequence being translated.  */
+  buf_start = fkey->start;
+  buf_end = fkey->end;
 
   key = keybuf[fkey->end++];
 
   if (KEYMAPP (fkey->parent))
-    next = access_keymap_keyremap (fkey->map, key, prompt, doit);
+    next = access_keymap_keyremap (fkey->map, key, prompt, doit,
+                                  buf_start, buf_end, keybuf);
   else
     next = Qnil;
 
@@ -9810,6 +10219,24 @@ void init_raw_keybuf_count (void)
   raw_keybuf_count = 0;
 }
 
+
+
+#ifdef HAVE_TEXT_CONVERSION
+
+static void
+restore_reading_key_sequence (int old_reading_key_sequence)
+{
+  reading_key_sequence = old_reading_key_sequence;
+
+  /* If a key sequence is no longer being read, reset input methods
+     whose state changes were postponed.  */
+
+  if (!old_reading_key_sequence)
+    check_postponed_buffers ();
+}
+
+#endif /* HAVE_TEXT_CONVERSION */
+
 /* Read a sequence of keys that ends with a non prefix character,
    storing it in KEYBUF, a buffer of size READ_KEY_ELTS.
    Prompt with PROMPT.
@@ -9848,12 +10275,16 @@ void init_raw_keybuf_count (void)
    read_char will return it.
 
    If FIX_CURRENT_BUFFER, we restore current_buffer
-   from the selected window's buffer.  */
+   from the selected window's buffer.
+
+   If DISABLE_TEXT_CONVERSION_P, disable text conversion so the input
+   method will always send key events.  */
 
 static int
 read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt,
                   bool dont_downcase_last, bool can_return_switch_frame,
-                  bool fix_current_buffer, bool prevent_redisplay)
+                  bool fix_current_buffer, bool prevent_redisplay,
+                  bool disable_text_conversion_p)
 {
   specpdl_ref count = SPECPDL_INDEX ();
 
@@ -9918,6 +10349,13 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object 
prompt,
   /* Gets around Microsoft compiler limitations.  */
   bool dummyflag = false;
 
+#ifdef HAVE_TEXT_CONVERSION
+  bool disabled_conversion;
+
+  /* Whether or not text conversion has already been disabled.  */
+  disabled_conversion = false;
+#endif /* HAVE_TEXT_CONVERSION */
+
   struct buffer *starting_buffer;
 
   /* List of events for which a fake prefix key has been generated.  */
@@ -9962,6 +10400,16 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object 
prompt,
   keys_start = this_command_key_count;
   this_single_command_key_start = keys_start;
 
+#ifdef HAVE_TEXT_CONVERSION
+  /* Set `reading_key_sequence' to true.  This variable is used by
+     Fset_text_conversion_style to determine if it should postpone
+     resetting the input method until this function completes.  */
+
+  record_unwind_protect_int (restore_reading_key_sequence,
+                            reading_key_sequence);
+  reading_key_sequence = true;
+#endif /* HAVE_TEXT_CONVERSION */
+
   /* We jump here when we need to reinitialize fkey and keytran; this
      happens if we switch keyboards between rescans.  */
  replay_entire_sequence:
@@ -10000,6 +10448,18 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object 
prompt,
   if (INTERACTIVE && t < mock_input)
     echo_truncate (echo_start);
 
+  /* If text conversion is supposed to be disabled immediately, do it
+     now.  */
+
+#ifdef HAVE_TEXT_CONVERSION
+  if (disable_text_conversion_p)
+    {
+      disable_text_conversion ();
+      record_unwind_protect_void (resume_text_conversion);
+      disabled_conversion = true;
+    }
+#endif /* HAVE_TEXT_CONVERSION */
+
   /* If the best binding for the current key sequence is a keymap, or
      we may be looking at a function key's escape sequence, keep on
      reading.  */
@@ -10067,6 +10527,43 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object 
prompt,
        echo_local_start = echo_length ();
       keys_local_start = this_command_key_count;
 
+#ifdef HAVE_TEXT_CONVERSION
+      /* When reading a key sequence while text conversion is in
+        effect, turn it off after the first actual character read.
+        This makes input methods send actual key events instead.
+
+         Make sure only to do this once.  Also, disabling text
+         conversion seems to interact badly with menus, so don't
+         disable text conversion if a menu was displayed.  */
+
+      if (!disabled_conversion && t && !used_mouse_menu
+         && !disable_inhibit_text_conversion)
+       {
+         int i;
+
+         /* used_mouse_menu isn't set if a menu bar prefix key has
+            just been stored.  It appears necessary to look for a
+            prefix key itself.  Don't look through too many keys for
+            efficiency reasons.  */
+
+         for (i = 0; i < min (t, 10); ++i)
+           {
+             if (NUMBERP (keybuf[i])
+                 || (SYMBOLP (keybuf[i])
+                     && EQ (Fget (keybuf[i], Qevent_kind),
+                            Qfunction_key)))
+               goto disable_text_conversion;
+           }
+
+         goto replay_key;
+
+       disable_text_conversion:
+         disable_text_conversion ();
+         record_unwind_protect_void (resume_text_conversion);
+         disabled_conversion = true;
+       }
+#endif
+
     replay_key:
       /* These are no-ops, unless we throw away a keystroke below and
         jumped back up to replay_key; in that case, these restore the
@@ -10294,7 +10791,7 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object 
prompt,
       if (EVENT_HAS_PARAMETERS (key))
        {
          Lisp_Object kind = EVENT_HEAD_KIND (EVENT_HEAD (key));
-         if (EQ (kind, Qmouse_click))
+         if (EQ (kind, Qmouse_click) || EQ (kind, Qtouchscreen))
            {
              Lisp_Object window = POSN_WINDOW (EVENT_START (key));
              Lisp_Object posn = POSN_POSN (EVENT_START (key));
@@ -10372,7 +10869,15 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object 
prompt,
              posn = POSN_POSN (xevent_start (key));
              /* Handle menu-bar events:
                 insert the dummy prefix event `menu-bar'.  */
-             if (EQ (posn, Qmenu_bar) || EQ (posn, Qtab_bar) || EQ (posn, 
Qtool_bar))
+             if ((EQ (posn, Qmenu_bar) || EQ (posn, Qtab_bar)
+                  || EQ (posn, Qtool_bar))
+                 /* Only insert the prefix key if the event comes
+                    directly from the keyboard buffer.  Key
+                    translation functions might return events with a
+                    `posn-area' of tool-bar or tab-bar without
+                    intending for these prefix events to be
+                    generated.  */
+                 && (mock_input <= t))
                {
                  if (READ_KEY_ELTS - t <= 1)
                    error ("Key sequence too long");
@@ -10789,7 +11294,8 @@ static Lisp_Object
 read_key_sequence_vs (Lisp_Object prompt, Lisp_Object continue_echo,
                      Lisp_Object dont_downcase_last,
                      Lisp_Object can_return_switch_frame,
-                     Lisp_Object cmd_loop, bool allow_string)
+                     Lisp_Object cmd_loop, bool allow_string,
+                     bool disable_text_conversion)
 {
   specpdl_ref count = SPECPDL_INDEX ();
 
@@ -10816,7 +11322,8 @@ read_key_sequence_vs (Lisp_Object prompt, Lisp_Object 
continue_echo,
   raw_keybuf_count = 0;
   Lisp_Object keybuf[READ_KEY_ELTS];
   int i = read_key_sequence (keybuf, prompt, ! NILP (dont_downcase_last),
-                            ! NILP (can_return_switch_frame), false, false);
+                            ! NILP (can_return_switch_frame), false, false,
+                            disable_text_conversion);
 
 #if 0  /* The following is fine for code reading a key sequence and
          then proceeding with a lengthy computation, but it's not good
@@ -10838,7 +11345,7 @@ read_key_sequence_vs (Lisp_Object prompt, Lisp_Object 
continue_echo,
                     (i, keybuf)));
 }
 
-DEFUN ("read-key-sequence", Fread_key_sequence, Sread_key_sequence, 1, 5, 0,
+DEFUN ("read-key-sequence", Fread_key_sequence, Sread_key_sequence, 1, 6, 0,
        doc: /* Read a sequence of keystrokes and return as a string or vector.
 The sequence is sufficient to specify a non-prefix command in the
 current local and global maps.
@@ -10884,20 +11391,31 @@ sequences, where they wouldn't conflict with ordinary 
bindings.  See
 The optional fifth argument CMD-LOOP, if non-nil, means
 that this key sequence is being read by something that will
 read commands one after another.  It should be nil if the caller
-will read just one key sequence.  */)
-  (Lisp_Object prompt, Lisp_Object continue_echo, Lisp_Object 
dont_downcase_last, Lisp_Object can_return_switch_frame, Lisp_Object cmd_loop)
+will read just one key sequence.
+
+The optional sixth argument DISABLE-TEXT-CONVERSION, if non-nil, means
+disable input method text conversion for the duration of reading this
+key sequence, and that keyboard input will always result in key events
+being sent.  */)
+  (Lisp_Object prompt, Lisp_Object continue_echo, Lisp_Object 
dont_downcase_last,
+   Lisp_Object can_return_switch_frame, Lisp_Object cmd_loop,
+   Lisp_Object disable_text_conversion)
 {
   return read_key_sequence_vs (prompt, continue_echo, dont_downcase_last,
-                              can_return_switch_frame, cmd_loop, true);
+                              can_return_switch_frame, cmd_loop, true,
+                              !NILP (disable_text_conversion));
 }
 
 DEFUN ("read-key-sequence-vector", Fread_key_sequence_vector,
-       Sread_key_sequence_vector, 1, 5, 0,
+       Sread_key_sequence_vector, 1, 6, 0,
        doc: /* Like `read-key-sequence' but always return a vector.  */)
-  (Lisp_Object prompt, Lisp_Object continue_echo, Lisp_Object 
dont_downcase_last, Lisp_Object can_return_switch_frame, Lisp_Object cmd_loop)
+  (Lisp_Object prompt, Lisp_Object continue_echo, Lisp_Object 
dont_downcase_last,
+   Lisp_Object can_return_switch_frame, Lisp_Object cmd_loop,
+   Lisp_Object disable_text_conversion)
 {
   return read_key_sequence_vs (prompt, continue_echo, dont_downcase_last,
-                              can_return_switch_frame, cmd_loop, false);
+                              can_return_switch_frame, cmd_loop, false,
+                              !NILP (disable_text_conversion));
 }
 
 /* Return true if input events are pending.  */
@@ -11206,7 +11724,7 @@ This may include sensitive information such as 
passwords.  */)
   if (dribble)
     {
       block_input ();
-      fclose (dribble);
+      emacs_fclose (dribble);
       unblock_input ();
       dribble = 0;
     }
@@ -11219,9 +11737,9 @@ This may include sensitive information such as 
passwords.  */)
       encfile = ENCODE_FILE (file);
       fd = emacs_open (SSDATA (encfile), O_WRONLY | O_CREAT | O_EXCL, 0600);
       if (fd < 0 && errno == EEXIST
-         && (unlink (SSDATA (encfile)) == 0 || errno == ENOENT))
+         && (emacs_unlink (SSDATA (encfile)) == 0 || errno == ENOENT))
        fd = emacs_open (SSDATA (encfile), O_WRONLY | O_CREAT | O_EXCL, 0600);
-      dribble = fd < 0 ? 0 : fdopen (fd, "w");
+      dribble = fd < 0 ? 0 : emacs_fdopen (fd, "w");
       if (dribble == 0)
        report_file_error ("Opening dribble", file);
     }
@@ -12105,7 +12623,10 @@ static const struct event_head head_table[] = {
   {SYMBOL_INDEX (Qmake_frame_visible),  SYMBOL_INDEX (Qmake_frame_visible)},
   /* `select-window' should be handled just like `switch-frame'
      in read_key_sequence.  */
-  {SYMBOL_INDEX (Qselect_window),       SYMBOL_INDEX (Qswitch_frame)}
+  {SYMBOL_INDEX (Qselect_window),       SYMBOL_INDEX (Qswitch_frame)},
+  /* Touchscreen events should be prefixed by the posn.  */
+  {SYMBOL_INDEX (Qtouchscreen_begin),  SYMBOL_INDEX (Qtouchscreen)},
+  {SYMBOL_INDEX (Qtouchscreen_end),    SYMBOL_INDEX (Qtouchscreen)},
 };
 
 static Lisp_Object
@@ -12181,6 +12702,7 @@ syms_of_keyboard (void)
   DEFSYM (Qhelp_echo, "help-echo");
   DEFSYM (Qhelp_echo_inhibit_substitution, "help-echo-inhibit-substitution");
   DEFSYM (QCrtl, ":rtl");
+  DEFSYM (QCwrap, ":wrap");
 
   staticpro (&item_properties);
   item_properties = Qnil;
@@ -12451,6 +12973,9 @@ syms_of_keyboard (void)
   virtual_core_keyboard_name = Qnil;
   staticpro (&virtual_core_keyboard_name);
 
+  menu_bar_touch_id = Qnil;
+  staticpro (&menu_bar_touch_id);
+
   defsubr (&Scurrent_idle_time);
   defsubr (&Sevent_symbol_parse_modifiers);
   defsubr (&Sevent_convert_list);
@@ -12810,6 +13335,10 @@ See also `pre-command-hook'.  */);
          "display-monitors-changed-functions");
 
   DEFSYM (Qcoding, "coding");
+  DEFSYM (Qtouchscreen, "touchscreen");
+#ifdef HAVE_TEXT_CONVERSION
+  DEFSYM (Qtext_conversion, "text-conversion");
+#endif
 
   Fset (Qecho_area_clear_hook, Qnil);
 
@@ -13184,9 +13713,25 @@ which see.  */);
   DEFVAR_LISP ("post-select-region-hook", Vpost_select_region_hook,
     doc: /* Abnormal hook run after the region is selected.
 This usually happens as a result of `select-active-regions'.  The hook
-is called with one argument, the string that was selected.  */);;
+is called with one argument, the string that was selected.  */);
   Vpost_select_region_hook = Qnil;
 
+  DEFVAR_BOOL ("disable-inhibit-text-conversion",
+              disable_inhibit_text_conversion,
+    doc: /* Don't disable text conversion inside `read-key-sequence'.
+If non-nil, text conversion will continue to happen after a prefix
+key has been read inside `read-key-sequence'.  */);
+  disable_inhibit_text_conversion = false;
+
+  DEFVAR_LISP ("current-key-remap-sequence",
+              Vcurrent_key_remap_sequence,
+    doc: /* The key sequence currently being remap, or nil.
+Bound to a vector containing the sub-sequence matching a binding
+within `input-decode-map' or `local-function-key-map' when its bound
+function is called to remap that sequence.  */);
+  Vcurrent_key_remap_sequence = Qnil;
+  DEFSYM (Qcurrent_key_remap_sequence, "current-key-remap-sequence");
+
   pdumper_do_now_and_after_load (syms_of_keyboard_for_pdumper);
 }
 
diff --git a/src/keyboard.h b/src/keyboard.h
index 3f86a8e03ad..9f6e65f9a09 100644
--- a/src/keyboard.h
+++ b/src/keyboard.h
@@ -246,6 +246,14 @@ extern KBOARD *initial_kboard;
    kboard, but doing so requires throwing to wrong_kboard_jmpbuf.  */
 extern KBOARD *current_kboard;
 
+
+#ifdef HAVE_TEXT_CONVERSION
+
+/* True if a key sequence is currently being read.  */
+extern bool reading_key_sequence;
+
+#endif /* HAVE_TEXT_CONVERSION */
+
 /* Total number of times read_char has returned, modulo UINTMAX_MAX + 1.  */
 extern uintmax_t num_input_events;
 
@@ -395,8 +403,17 @@ extern void unuse_menu_items (void);
 #define EVENT_HEAD(event) \
   (EVENT_HAS_PARAMETERS (event) ? XCAR (event) : (event))
 
-/* Extract the starting and ending positions from a composite event.  */
-#define EVENT_START(event) (CAR_SAFE (CDR_SAFE (event)))
+/* Extract the starting and ending positions from a composite event. */
+
+/* Unlike Lisp `event-start', this also handles touch screen events,
+   which are not actually mouse events in the general sense.  */
+#define EVENT_START(event)                             \
+  ((EQ (EVENT_HEAD (event), Qtouchscreen_begin)                \
+    || EQ (EVENT_HEAD (event), Qtouchscreen_end))      \
+   ? CDR_SAFE (CAR_SAFE (CDR_SAFE (event)))            \
+   : CAR_SAFE (CDR_SAFE (event)))
+
+/* This does not handle touchscreen events.  */
 #define EVENT_END(event) (CAR_SAFE (CDR_SAFE (CDR_SAFE (event))))
 
 /* Extract the click count from a multi-click event.  */
diff --git a/src/keymap.c b/src/keymap.c
index da2af98c2d6..1f863885003 100644
--- a/src/keymap.c
+++ b/src/keymap.c
@@ -1065,8 +1065,12 @@ possibly_translate_key_sequence (Lisp_Object key, 
ptrdiff_t *length)
        xsignal2 (Qerror,
                  build_string ("`key-valid-p' is not defined, so this syntax 
can't be used: %s"),
                  key);
+      /* If key-valid-p is unhappy about KEY, we return it as-is.
+         This happens when menu items define as bindings strings that
+         should be inserted into the buffer, not commands.  See
+         bug#64927, for example.  */
       if (NILP (call1 (Qkey_valid_p, AREF (key, 0))))
-       xsignal2 (Qerror, build_string ("Invalid `key-parse' syntax: %S"), key);
+       return key;
       key = call1 (Qkey_parse, AREF (key, 0));
       *length = CHECK_VECTOR_OR_STRING (key);
       if (*length == 0)
diff --git a/src/lisp.h b/src/lisp.h
index 90bbdde073e..115796bf926 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -29,6 +29,11 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <float.h>
 #include <inttypes.h>
 #include <limits.h>
+#include <stdio.h>
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
 
 #include <attribute.h>
 #include <count-leading-zeros.h>
@@ -4612,7 +4617,8 @@ extern bool suffix_p (Lisp_Object, const char *);
 extern Lisp_Object save_match_data_load (Lisp_Object, Lisp_Object, Lisp_Object,
                                         Lisp_Object, Lisp_Object);
 extern int openp (Lisp_Object, Lisp_Object, Lisp_Object,
-                  Lisp_Object *, Lisp_Object, bool, bool);
+                  Lisp_Object *, Lisp_Object, bool, bool,
+                 void **);
 enum { S2N_IGNORE_TRAILING = 1 };
 extern Lisp_Object string_to_number (char const *, int, ptrdiff_t *);
 extern void dir_warning (const char *, Lisp_Object);
@@ -4832,6 +4838,7 @@ extern void syms_of_marker (void);
 
 /* Defined in fileio.c.  */
 
+extern Lisp_Object file_name_directory (Lisp_Object);
 extern char *splice_dir_file (char *, char const *, char const *)
   ATTRIBUTE_RETURNS_NONNULL;
 extern bool file_name_absolute_p (const char *);
@@ -5171,11 +5178,27 @@ extern void init_random (void);
 extern void emacs_backtrace (int);
 extern AVOID emacs_abort (void) NO_INLINE;
 extern int emacs_fstatat (int, char const *, void *, int);
+#ifdef HAVE_SYS_STAT_H
+extern int sys_fstat (int, struct stat *);
+#endif
+extern int sys_faccessat (int, const char *, int, int);
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
 extern int emacs_openat (int, char const *, int, int);
+#endif
 extern int emacs_open (const char *, int, int);
 extern int emacs_open_noquit (const char *, int, int);
 extern int emacs_pipe (int[2]);
 extern int emacs_close (int);
+extern FILE *emacs_fdopen (int, const char *);
+extern int emacs_fclose (FILE *);
+extern int emacs_unlink (const char *);
+extern int emacs_symlink (const char *, const char *);
+extern int emacs_rmdir (const char *);
+extern int emacs_mkdir (const char *, mode_t);
+extern int emacs_renameat_noreplace (int, const char *, int,
+                                    const char *);
+extern int emacs_rename (const char *, const char *);
+extern int emacs_fchmodat (int, const char *, mode_t, int);
 extern ptrdiff_t emacs_read (int, void *, ptrdiff_t);
 extern ptrdiff_t emacs_read_quit (int, void *, ptrdiff_t);
 extern ptrdiff_t emacs_write (int, void const *, ptrdiff_t);
@@ -5209,7 +5232,9 @@ extern Lisp_Object directory_files_internal (Lisp_Object, 
Lisp_Object,
                                              bool, Lisp_Object, Lisp_Object);
 
 /* Defined in term.c.  */
+#ifndef HAVE_ANDROID
 extern int *char_ins_del_vector;
+#endif
 extern void syms_of_term (void);
 extern AVOID fatal (const char *msgid, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2);
 
@@ -5319,7 +5344,13 @@ extern char *emacs_root_dir (void);
 
 #ifdef HAVE_TEXT_CONVERSION
 /* Defined in textconv.c.  */
+extern void reset_frame_state (struct frame *);
 extern void report_selected_window_change (struct frame *);
+extern void report_point_change (struct frame *, struct window *,
+                                struct buffer *);
+extern void disable_text_conversion (void);
+extern void resume_text_conversion (void);
+extern void syms_of_textconv (void);
 #endif
 
 #ifdef HAVE_NATIVE_COMP
diff --git a/src/lread.c b/src/lread.c
index c1bbb5a7a15..a3fd76234e0 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -62,6 +62,24 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 #include <fcntl.h>
 
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY   \
+  || (__ANDROID_API__ < 9)
+
+#define lread_fd       int
+#define lread_fd_cmp(n) (fd == (n))
+#define lread_fd_p     (fd >= 0)
+#define lread_close    emacs_close
+#define lread_fstat    fstat
+#define lread_read_quit        emacs_read_quit
+#define lread_lseek    lseek
+
+#define file_stream            FILE *
+#define file_seek              fseek
+#define file_stream_valid_p(p) (p)
+#define file_stream_close      emacs_fclose
+#define file_stream_invalid    NULL
+#define file_get_char          getc
+
 #ifdef HAVE_FSEEKO
 #define file_offset off_t
 #define file_tell ftello
@@ -70,6 +88,79 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #define file_tell ftell
 #endif
 
+#else
+
+#include "android.h"
+
+/* Use an Android file descriptor under Android instead, as this
+   allows loading directly from asset files without loading each asset
+   into memory and creating a separate file descriptor every time.
+
+   Note that `struct android_fd_or_asset' as used here is different
+   from that returned from `android_open_asset'; if fd.asset is NULL,
+   then fd.fd is either a valid file descriptor or -1, meaning that
+   the file descriptor is invalid.
+
+   However, lread requires the ability to seek inside asset files,
+   which is not provided under Android 2.2.  So when building for that
+   particular system, fall back to the usual file descriptor-based
+   code.  */
+
+#define lread_fd       struct android_fd_or_asset
+#define lread_fd_cmp(n)        (!fd.asset && fd.fd == (n))
+#define lread_fd_p     (fd.asset || fd.fd >= 0)
+#define lread_close    android_close_asset
+#define lread_fstat    android_asset_fstat
+#define lread_read_quit        android_asset_read_quit
+#define lread_lseek    android_asset_lseek
+
+/* The invalid file stream.  */
+
+static struct android_fd_or_asset invalid_file_stream =
+  {
+    -1,
+    NULL,
+  };
+
+#define file_stream            struct android_fd_or_asset
+#define file_offset            off_t
+#define file_tell(n)           (android_asset_lseek ((n), 0, SEEK_CUR))
+#define file_seek              android_asset_lseek
+#define file_stream_valid_p(p) ((p).asset || (p).fd >= 0)
+#define file_stream_close      android_close_asset
+#define file_stream_invalid    invalid_file_stream
+
+/* Return a single character from the file input stream STREAM.
+   Value and errors are the same as getc.  */
+
+static int
+file_get_char (file_stream stream)
+{
+  int c;
+  char byte;
+  ssize_t rc;
+
+ retry:
+  rc = android_asset_read (stream, &byte, 1);
+
+  if (rc == 0)
+    c = EOF;
+  else if (rc == -1)
+    {
+      if (errno == EINTR)
+       goto retry;
+      else
+       c = EOF;
+    }
+  else
+    c = (unsigned char) byte;
+
+  return c;
+}
+
+#define USE_ANDROID_ASSETS
+#endif
+
 #if IEEE_FLOATING_POINT
 # include <ieee754.h>
 # ifndef INFINITY
@@ -117,7 +208,7 @@ static Lisp_Object read_objects_completed;
 static struct infile
 {
   /* The input stream.  */
-  FILE *stream;
+  file_stream stream;
 
   /* Lookahead byte count.  */
   signed char lookahead;
@@ -374,7 +465,7 @@ skip_dyn_bytes (Lisp_Object readcharfun, ptrdiff_t n)
   if (FROM_FILE_P (readcharfun))
     {
       block_input ();          /* FIXME: Not sure if it's needed.  */
-      fseek (infile->stream, n - infile->lookahead, SEEK_CUR);
+      file_seek (infile->stream, n - infile->lookahead, SEEK_CUR);
       unblock_input ();
       infile->lookahead = 0;
     }
@@ -398,7 +489,7 @@ skip_dyn_eof (Lisp_Object readcharfun)
   if (FROM_FILE_P (readcharfun))
     {
       block_input ();          /* FIXME: Not sure if it's needed.  */
-      fseek (infile->stream, 0, SEEK_END);
+      file_seek (infile->stream, 0, SEEK_END);
       unblock_input ();
       infile->lookahead = 0;
     }
@@ -479,10 +570,12 @@ readbyte_from_stdio (void)
     return infile->buf[--infile->lookahead];
 
   int c;
-  FILE *instream = infile->stream;
+  file_stream instream = infile->stream;
 
   block_input ();
 
+#if !defined USE_ANDROID_ASSETS
+
   /* Interrupted reads have been observed while reading over the network.  */
   while ((c = getc (instream)) == EOF && errno == EINTR && ferror (instream))
     {
@@ -492,6 +585,35 @@ readbyte_from_stdio (void)
       clearerr (instream);
     }
 
+#else
+
+  {
+    char byte;
+    ssize_t rc;
+
+  retry:
+    rc = android_asset_read (instream, &byte, 1);
+
+    if (rc == 0)
+      c = EOF;
+    else if (rc == -1)
+      {
+       if (errno == EINTR)
+         {
+           unblock_input ();
+           maybe_quit ();
+           block_input ();
+           goto retry;
+         }
+       else
+         c = EOF;
+      }
+    else
+      c = (unsigned char) byte;
+  }
+
+#endif
+
   unblock_input ();
 
   return (c == EOF ? -1 : c);
@@ -671,7 +793,11 @@ static void substitute_in_interval (INTERVAL, void *);
    if the character warrants that.
 
    If SECONDS is a number, wait that many seconds for input, and
-   return Qnil if no input arrives within that time.  */
+   return Qnil if no input arrives within that time.
+
+   If text conversion is enabled and ASCII_REQUIRED && ERROR_NONASCII,
+   temporarily disable any input method which wants to perform
+   edits, unless `disable-inhibit-text-conversion'.  */
 
 static Lisp_Object
 read_filtered_event (bool no_switch_frame, bool ascii_required,
@@ -679,12 +805,29 @@ read_filtered_event (bool no_switch_frame, bool 
ascii_required,
 {
   Lisp_Object val, delayed_switch_frame;
   struct timespec end_time;
+#ifdef HAVE_TEXT_CONVERSION
+  specpdl_ref count;
+#endif
 
 #ifdef HAVE_WINDOW_SYSTEM
   if (display_hourglass_p)
     cancel_hourglass ();
 #endif
 
+#ifdef HAVE_TEXT_CONVERSION
+  count = SPECPDL_INDEX ();
+
+  /* Don't use text conversion when trying to just read a
+     character.  */
+
+  if (ascii_required && error_nonascii
+      && !disable_inhibit_text_conversion)
+    {
+      disable_text_conversion ();
+      record_unwind_protect_void (resume_text_conversion);
+    }
+#endif
+
   delayed_switch_frame = Qnil;
 
   /* Compute timeout.  */
@@ -760,7 +903,11 @@ read_filtered_event (bool no_switch_frame, bool 
ascii_required,
 
 #endif
 
+#ifdef HAVE_TEXT_CONVERSION
+  return unbind_to (count, val);
+#else
   return val;
+#endif
 }
 
 DEFUN ("read-char", Fread_char, Sread_char, 0, 3, 0,
@@ -1039,7 +1186,7 @@ lisp_file_lexically_bound_p (Lisp_Object readcharfun, 
bool *prefixes)
    safe to load.  Only files compiled with Emacs can be loaded.  */
 
 static int
-safe_to_load_version (Lisp_Object file, int fd)
+safe_to_load_version (Lisp_Object file, lread_fd fd)
 {
   struct stat st;
   char buf[512];
@@ -1048,12 +1195,12 @@ safe_to_load_version (Lisp_Object file, int fd)
 
   /* If the file is not regular, then we cannot safely seek it.
      Assume that it is not safe to load as a compiled file.  */
-  if (fstat (fd, &st) == 0 && !S_ISREG (st.st_mode))
+  if (lread_fstat (fd, &st) == 0 && !S_ISREG (st.st_mode))
     return 0;
 
   /* Read the first few bytes from the file, and look for a line
      specifying the byte compiler version used.  */
-  nbytes = emacs_read_quit (fd, buf, sizeof buf);
+  nbytes = lread_read_quit (fd, buf, sizeof buf);
   if (nbytes > 0)
     {
       /* Skip to the next newline, skipping over the initial `ELC'
@@ -1068,7 +1215,7 @@ safe_to_load_version (Lisp_Object file, int fd)
        version = 0;
     }
 
-  if (lseek (fd, 0, SEEK_SET) < 0)
+  if (lread_lseek (fd, 0, SEEK_SET) < 0)
     report_file_error ("Seeking to start of file", file);
 
   return version;
@@ -1142,7 +1289,7 @@ close_infile_unwind (void *arg)
 {
   struct infile *prev_infile = arg;
   eassert (infile && infile != prev_infile);
-  fclose (infile->stream);
+  file_stream_close (infile->stream);
   infile = prev_infile;
 }
 
@@ -1171,6 +1318,22 @@ loadhist_initialize (Lisp_Object filename)
   specbind (Qcurrent_load_list, Fcons (filename, Qnil));
 }
 
+#ifdef USE_ANDROID_ASSETS
+
+/* Like `close_file_unwind'.  However, PTR is a pointer to an Android
+   file descriptor instead of a system file descriptor.  */
+
+static void
+close_file_unwind_android_fd (void *ptr)
+{
+  struct android_fd_or_asset *fd;
+
+  fd = ptr;
+  android_close_asset (*fd);
+}
+
+#endif
+
 DEFUN ("load", Fload, Sload, 1, 5, 0,
        doc: /* Execute a file of Lisp code named FILE.
 First try FILE with `.elc' appended, then try with `.el', then try
@@ -1219,8 +1382,12 @@ Return t if the file exists and loads successfully.  */)
   (Lisp_Object file, Lisp_Object noerror, Lisp_Object nomessage,
    Lisp_Object nosuffix, Lisp_Object must_suffix)
 {
-  FILE *stream UNINIT;
-  int fd;
+  file_stream stream UNINIT;
+  lread_fd fd;
+#ifdef USE_ANDROID_ASSETS
+  int rc;
+  void *asset;
+#endif
   specpdl_ref fd_index UNINIT;
   specpdl_ref count = SPECPDL_INDEX ();
   Lisp_Object found, efound, hist_file_name;
@@ -1261,7 +1428,12 @@ Return t if the file exists and loads successfully.  */)
      since it would try to load a directory as a Lisp file.  */
   if (SCHARS (file) == 0)
     {
+#if !defined USE_ANDROID_ASSETS
       fd = -1;
+#else
+      fd.asset = NULL;
+      fd.fd = -1;
+#endif
       errno = ENOENT;
     }
   else
@@ -1300,12 +1472,22 @@ Return t if the file exists and loads successfully.  */)
            suffixes = CALLN (Fappend, suffixes, Vload_file_rep_suffixes);
        }
 
-      fd =
-       openp (Vload_path, file, suffixes, &found, Qnil, load_prefer_newer,
-              no_native);
+#if !defined USE_ANDROID_ASSETS
+      fd = openp (Vload_path, file, suffixes, &found, Qnil,
+                 load_prefer_newer, no_native, NULL);
+#else
+      asset = NULL;
+      rc = openp (Vload_path, file, suffixes, &found, Qnil,
+                 load_prefer_newer, no_native, &asset);
+      fd.fd = rc;
+      fd.asset = asset;
+
+      /* fd.asset will be non-NULL if this is actually an asset
+        file.  */
+#endif
     }
 
-  if (fd == -1)
+  if (lread_fd_cmp (-1))
     {
       if (NILP (noerror))
        report_file_error ("Cannot open load file", file);
@@ -1317,7 +1499,7 @@ Return t if the file exists and loads successfully.  */)
     Vuser_init_file = found;
 
   /* If FD is -2, that means openp found a magic file.  */
-  if (fd == -2)
+  if (lread_fd_cmp (-2))
     {
       if (NILP (Fequal (found, file)))
        /* If FOUND is a different file name from FILE,
@@ -1346,11 +1528,21 @@ Return t if the file exists and loads successfully.  */)
 #endif
     }
 
+#if !defined USE_ANDROID_ASSETS
   if (0 <= fd)
     {
       fd_index = SPECPDL_INDEX ();
       record_unwind_protect_int (close_file_unwind, fd);
     }
+#else
+  if (fd.asset || fd.fd >= 0)
+    {
+      /* Use a different kind of unwind_protect here.  */
+      fd_index = SPECPDL_INDEX ();
+      record_unwind_protect_ptr (close_file_unwind_android_fd,
+                                &fd);
+    }
+#endif
 
 #ifdef HAVE_MODULES
   bool is_module =
@@ -1416,11 +1608,12 @@ Return t if the file exists and loads successfully.  */)
   if (is_elc
       /* version = 1 means the file is empty, in which case we can
         treat it as not byte-compiled.  */
-      || (fd >= 0 && (version = safe_to_load_version (file, fd)) > 1))
+      || (lread_fd_p
+         && (version = safe_to_load_version (file, fd)) > 1))
     /* Load .elc files directly, but not when they are
        remote and have no handler!  */
     {
-      if (fd != -2)
+      if (!lread_fd_cmp (-2))
        {
          struct stat s1, s2;
          int result;
@@ -1477,9 +1670,9 @@ Return t if the file exists and loads successfully.  */)
        {
          Lisp_Object val;
 
-         if (fd >= 0)
+         if (lread_fd_p)
            {
-             emacs_close (fd);
+             lread_close (fd);
              clear_unwind_protect (fd_index);
            }
          val = call4 (Vload_source_file_function, found, hist_file_name,
@@ -1489,12 +1682,12 @@ Return t if the file exists and loads successfully.  */)
        }
     }
 
-  if (fd < 0)
+  if (!lread_fd_p)
     {
       /* We somehow got here with fd == -2, meaning the file is deemed
         to be remote.  Don't even try to reopen the file locally;
         just force a failure.  */
-      stream = NULL;
+      stream = file_stream_invalid;
       errno = EINVAL;
     }
   else if (!is_module && !is_native_elisp)
@@ -1505,7 +1698,15 @@ Return t if the file exists and loads successfully.  */)
       efound = ENCODE_FILE (found);
       stream = emacs_fopen (SSDATA (efound), fmode);
 #else
-      stream = fdopen (fd, fmode);
+#if !defined USE_ANDROID_ASSETS
+      stream = emacs_fdopen (fd, fmode);
+#else
+      /* Android systems use special file descriptors which can point
+        into compressed data and double as file streams.  FMODE is
+        unused.  */
+      ((void) fmode);
+      stream = fd;
+#endif
 #endif
     }
 
@@ -1517,15 +1718,15 @@ Return t if the file exists and loads successfully.  */)
     {
       /* `module-load' uses the file name, so we can close the stream
          now.  */
-      if (fd >= 0)
+      if (lread_fd_p)
         {
-          emacs_close (fd);
+          lread_close (fd);
           clear_unwind_protect (fd_index);
         }
     }
   else
     {
-      if (! stream)
+      if (!file_stream_valid_p (stream))
         report_file_error ("Opening stdio stream", file);
       set_unwind_protect_ptr (fd_index, close_infile_unwind, infile);
       input.stream = stream;
@@ -1664,7 +1865,8 @@ directories, make sure the PREDICATE function returns 
`dir-ok' for them.  */)
   (Lisp_Object filename, Lisp_Object path, Lisp_Object suffixes, Lisp_Object 
predicate)
 {
   Lisp_Object file;
-  int fd = openp (path, filename, suffixes, &file, predicate, false, true);
+  int fd = openp (path, filename, suffixes, &file, predicate, false, true,
+                 NULL);
   if (NILP (predicate) && fd >= 0)
     emacs_close (fd);
   return file;
@@ -1680,7 +1882,7 @@ maybe_swap_for_eln1 (Lisp_Object src_name, Lisp_Object 
eln_name,
 
   if (eln_fd > 0)
     {
-      if (fstat (eln_fd, &eln_st) || S_ISDIR (eln_st.st_mode))
+      if (sys_fstat (eln_fd, &eln_st) || S_ISDIR (eln_st.st_mode))
        emacs_close (eln_fd);
       else
        {
@@ -1805,14 +2007,20 @@ maybe_swap_for_eln (bool no_native, Lisp_Object 
*filename, int *fd,
 
    If NEWER is true, try all SUFFIXes and return the result for the
    newest file that exists.  Does not apply to remote files,
-   or if a non-nil and non-t PREDICATE is specified.
+   platform-specific files, or if a non-nil and non-t PREDICATE is
+   specified.
+
+   If NO_NATIVE is true do not try to load native code.
 
-   if NO_NATIVE is true do not try to load native code.  */
+   If PLATFORM is non-NULL and the file being loaded lies in a special
+   directory, such as the Android `/assets' directory, return a handle
+   to that directory in *PLATFORM instead of a file descriptor; in
+   that case, value is -3.  */
 
 int
 openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes,
        Lisp_Object *storeptr, Lisp_Object predicate, bool newer,
-       bool no_native)
+       bool no_native, void **platform)
 {
   ptrdiff_t fn_size = 100;
   char buf[100];
@@ -1824,6 +2032,9 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object 
suffixes,
   ptrdiff_t max_suffix_len = 0;
   int last_errno = ENOENT;
   int save_fd = -1;
+#ifdef USE_ANDROID_ASSETS
+  struct android_fd_or_asset platform_fd;
+#endif
   USE_SAFE_ALLOCA;
 
   /* The last-modified time of the newest matching file found.
@@ -1968,8 +2179,8 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object 
suffixes,
                fd = -1;
                if (INT_MAX < XFIXNAT (predicate))
                  last_errno = EINVAL;
-               else if (faccessat (AT_FDCWD, pfn, XFIXNAT (predicate),
-                                   AT_EACCESS)
+               else if (sys_faccessat (AT_FDCWD, pfn, XFIXNAT (predicate),
+                                       AT_EACCESS)
                         == 0)
                  {
                    if (file_directory_p (encoded_fn))
@@ -1989,11 +2200,34 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object 
suffixes,
                     it.  Only open the file when we are sure that it
                     exists.  */
 #ifdef WINDOWSNT
-                if (faccessat (AT_FDCWD, pfn, R_OK, AT_EACCESS))
+                if (sys_faccessat (AT_FDCWD, pfn, R_OK, AT_EACCESS))
                   fd = -1;
                 else
 #endif
-                  fd = emacs_open (pfn, O_RDONLY, 0);
+                 {
+#if !defined USE_ANDROID_ASSETS
+                   fd = emacs_open (pfn, O_RDONLY, 0);
+#else
+                   if (platform)
+                     {
+                       platform_fd = android_open_asset (pfn, O_RDONLY, 0);
+
+                       if (platform_fd.asset
+                           && platform_fd.asset != (void *) -1)
+                         {
+                           *storeptr = string;
+                           goto handle_platform_fd;
+                         }
+
+                       if (platform_fd.asset == (void *) -1)
+                         fd = -1;
+                       else
+                         fd = platform_fd.fd;
+                     }
+                   else
+                     fd = emacs_open (pfn, O_RDONLY, 0);
+#endif
+                 }
 
                if (fd < 0)
                  {
@@ -2002,7 +2236,7 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object 
suffixes,
                  }
                else
                  {
-                   int err = (fstat (fd, &st) != 0 ? errno
+                   int err = (sys_fstat (fd, &st) != 0 ? errno
                               : S_ISDIR (st.st_mode) ? EISDIR : 0);
                    if (err)
                      {
@@ -2061,6 +2295,16 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object 
suffixes,
   SAFE_FREE ();
   errno = last_errno;
   return -1;
+
+#ifdef USE_ANDROID_ASSETS
+ handle_platform_fd:
+
+  /* Here, openp found a platform specific file descriptor.  It can't
+     be a directory under Android, so return it in *PLATFORM and then
+     -3 as the file descriptor.  */
+  *platform = platform_fd.asset;
+  return -3;
+#endif
 }
 
 
@@ -2092,7 +2336,7 @@ build_load_history (Lisp_Object filename, bool entire)
        {
          foundit = 1;
 
-         /*  If we're loading the entire file, remove old data.  */
+         /* If we're loading the entire file, remove old data.  */
          if (entire)
            {
              if (NILP (prev))
@@ -2100,8 +2344,8 @@ build_load_history (Lisp_Object filename, bool entire)
              else
                Fsetcdr (prev, XCDR (tail));
            }
-
-         /*  Otherwise, cons on new symbols that are not already members.  */
+         /* Otherwise, cons on new symbols that are not already
+            members.  */
          else
            {
              tem2 = Vcurrent_load_list;
@@ -3467,7 +3711,7 @@ skip_lazy_string (Lisp_Object readcharfun)
          ss->string = xrealloc (ss->string, ss->size);
        }
 
-      FILE *instream = infile->stream;
+      file_stream instream = infile->stream;
       ss->position = (file_tell (instream) - infile->lookahead);
 
       /* Copy that many bytes into the saved string.  */
@@ -3477,7 +3721,7 @@ skip_lazy_string (Lisp_Object readcharfun)
        ss->string[i++] = c = infile->buf[--infile->lookahead];
       block_input ();
       for (; i < nskip && c >= 0; i++)
-       ss->string[i] = c = getc (instream);
+       ss->string[i] = c = file_get_char (instream);
       unblock_input ();
 
       ss->length = i;
diff --git a/src/marker.c b/src/marker.c
index e42c49a5434..7b15cd62f1e 100644
--- a/src/marker.c
+++ b/src/marker.c
@@ -23,6 +23,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "lisp.h"
 #include "character.h"
 #include "buffer.h"
+#include "window.h"
 
 /* Record one cached position found recently by
    buf_charpos_to_bytepos or buf_bytepos_to_charpos.  */
@@ -566,6 +567,31 @@ set_marker_internal (Lisp_Object marker, Lisp_Object 
position,
 
       attach_marker (m, b, charpos, bytepos);
     }
+
+#ifdef HAVE_TEXT_CONVERSION
+
+  /* If B is the buffer's mark and there is a window displaying B, and
+     text conversion is enabled while the mark is active, redisplay
+     the buffer.
+
+     propagate_window_redisplay will propagate this redisplay to the
+     window, which will eventually reach
+     mark_window_display_accurate_1.  At that point,
+     report_point_change will be told to update the mark as seen by
+     the input method.
+
+     This is done all the way in (the seemingly irrelevant) redisplay
+     because the selection reported to the input method is actually what
+     is visible on screen, namely w->last_point.  */
+
+  if (m->buffer
+      && EQ (marker, BVAR (m->buffer, mark))
+      && !NILP (BVAR (m->buffer, mark_active))
+      && buffer_window_count (m->buffer))
+    bset_redisplay (m->buffer);
+
+#endif
+
   return marker;
 }
 
diff --git a/src/menu.c b/src/menu.c
index 73d4215b94b..6ab34a16996 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -48,7 +48,7 @@ static bool
 have_boxes (void)
 {
 #if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NTGUI) || 
defined (HAVE_NS) \
-  || defined (HAVE_HAIKU)
+  || defined (HAVE_HAIKU) || defined (HAVE_ANDROID)
   if (FRAME_WINDOW_P (XFRAME (Vmenu_updating_frame)))
     return 1;
 #endif
@@ -167,7 +167,7 @@ ensure_menu_items (int items)
     }
 }
 
-#ifdef HAVE_EXT_MENU_BAR
+#if defined HAVE_EXT_MENU_BAR || defined HAVE_ANDROID
 
 /* Begin a submenu.  */
 
@@ -191,7 +191,7 @@ push_submenu_end (void)
   menu_items_submenu_depth--;
 }
 
-#endif /* HAVE_EXT_MENU_BAR */
+#endif /* HAVE_EXT_MENU_BAR || HAVE_ANDROID */
 
 /* Indicate boundary between left and right.  */
 
@@ -420,8 +420,9 @@ single_menu_item (Lisp_Object key, Lisp_Object item, 
Lisp_Object dummy, void *sk
                  AREF (item_properties, ITEM_PROPERTY_SELECTED),
                  AREF (item_properties, ITEM_PROPERTY_HELP));
 
-#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) \
-  || defined (HAVE_NTGUI) || defined (HAVE_HAIKU) || defined (HAVE_PGTK)
+#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS)  \
+  || defined (HAVE_NTGUI) || defined (HAVE_HAIKU) || defined (HAVE_PGTK) \
+  || defined (HAVE_ANDROID)
   /* Display a submenu using the toolkit.  */
   if (FRAME_WINDOW_P (XFRAME (Vmenu_updating_frame))
       && ! (NILP (map) || NILP (enabled)))
@@ -1151,7 +1152,7 @@ x_popup_menu_1 (Lisp_Object position, Lisp_Object menu)
        else
          {
            menuflags |= MENU_FOR_CLICK;
-           tem = Fcar (XCDR (position));    /* EVENT_START (position) */
+           tem = EVENT_START (position);    /* EVENT_START (position) */
            window = Fcar (tem);             /* POSN_WINDOW (tem) */
            tem2 = Fcar (Fcdr (tem));        /* POSN_POSN (tem) */
            /* The MENU_KBD_NAVIGATION field is set when the menu
@@ -1465,9 +1466,10 @@ cached information about equivalent key sequences.
 If the user gets rid of the menu without making a valid choice, for
 instance by clicking the mouse away from a valid choice or by typing
 keyboard input, then this normally results in a quit and
-`x-popup-menu' does not return.  But if POSITION is a mouse button
-event (indicating that the user invoked the menu with the mouse) then
-no quit occurs and `x-popup-menu' returns nil.  */)
+`x-popup-menu' does not return.  But if POSITION is a mouse button or
+touch screen event (indicating that the user invoked the menu with the
+a pointing device) then no quit occurs and `x-popup-menu' returns
+nil.  */)
   (Lisp_Object position, Lisp_Object menu)
 {
   init_raw_keybuf_count ();
diff --git a/src/pdumper.c b/src/pdumper.c
index 5e2dca2123b..4f9710ee610 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -4070,10 +4070,12 @@ types.  */)
 {
   eassert (initialized);
 
+#ifndef HAVE_ANDROID
   if (! noninteractive)
     error ("Dumping Emacs currently works only in batch mode.  "
            "If you'd like it to work interactively, please consider "
            "contributing a patch to Emacs.");
+#endif
 
   if (will_dump_with_unexec_p ())
     error ("This Emacs instance was started under the assumption "
@@ -4743,7 +4745,9 @@ dump_discard_mem (void *mem, size_t size)
 # ifdef HAVE_POSIX_MADVISE
       /* Discard COWed pages.  */
       (void) posix_madvise (mem, size, POSIX_MADV_DONTNEED);
-# endif
+# elif defined HAVE_MADVISE
+      (void) madvise (mem, size, MADV_DONTNEED);
+#endif
       /* Release the commit charge for the mapping.  */
       (void) mprotect (mem, size, PROT_NONE);
 #endif
@@ -5613,7 +5617,7 @@ pdumper_load (const char *dump_filename, char *argv0)
     }
 
   err = PDUMPER_LOAD_FILE_NOT_FOUND;
-  if (fstat (dump_fd, &stat) < 0)
+  if (sys_fstat (dump_fd, &stat) < 0)
     goto out;
 
   err = PDUMPER_LOAD_BAD_FILE_TYPE;
@@ -5835,6 +5839,10 @@ void
 syms_of_pdumper (void)
 {
 #ifdef HAVE_PDUMPER
+  unsigned char desired[sizeof fingerprint];
+  int i;
+  char hexbuf[2 * sizeof fingerprint];
+
   defsubr (&Sdump_emacs_portable);
   defsubr (&Sdump_emacs_portable__sort_predicate);
   defsubr (&Sdump_emacs_portable__sort_predicate_copied);
@@ -5847,5 +5855,17 @@ syms_of_pdumper (void)
   DEFSYM (Qdump_file_name, "dump-file-name");
   DEFSYM (Qafter_pdump_load_hook, "after-pdump-load-hook");
   defsubr (&Spdumper_stats);
+
+  for (i = 0; i < sizeof fingerprint; i++)
+    desired[i] = fingerprint[i];
+
+  hexbuf_digest (hexbuf, desired, sizeof desired);
+
+  DEFVAR_LISP ("pdumper-fingerprint", Vpdumper_fingerprint,
+              doc: /* The fingerprint of this Emacs binary.
+It is a string that is supposed to be unique to each build of
+Emacs.  */);
+  Vpdumper_fingerprint = make_unibyte_string ((char *) hexbuf,
+                                             sizeof hexbuf);
 #endif /* HAVE_PDUMPER */
 }
diff --git a/src/print.c b/src/print.c
index e9ce2fa4856..103ca64c1cf 100644
--- a/src/print.c
+++ b/src/print.c
@@ -759,8 +759,7 @@ For instance:
 
   (prin1 object nil \\='((length . 100) (circle . t))).
 
-See the manual entry `(elisp)Output Overrides' for a list of possible
-values.
+See Info node `(elisp)Output Overrides' for a list of possible values.
 
 As a special case, OVERRIDES can also simply be the symbol t, which
 means "use default values for all the print-related settings".  */)
@@ -1924,12 +1923,17 @@ print_vectorlike (Lisp_Object obj, Lisp_Object 
printcharfun, bool escapeflag,
              print_c_string ("#<font-entity", printcharfun);
            for (int i = 0; i < FONT_SPEC_MAX; i++)
              {
-               printchar (' ', printcharfun);
-               if (i < FONT_WEIGHT_INDEX || i > FONT_WIDTH_INDEX)
-                 print_object (AREF (obj, i), printcharfun, escapeflag);
-               else
-                 print_object (font_style_symbolic (obj, i, 0),
-                               printcharfun, escapeflag);
+               /* FONT_EXTRA_INDEX can contain private information in
+                  font entities which isn't safe to print.  */
+               if (i != FONT_EXTRA_INDEX || !FONT_ENTITY_P (obj))
+                 {
+                   printchar (' ', printcharfun);
+                   if (i < FONT_WEIGHT_INDEX || i > FONT_WIDTH_INDEX)
+                     print_object (AREF (obj, i), printcharfun, escapeflag);
+                   else
+                     print_object (font_style_symbolic (obj, i, 0),
+                                   printcharfun, escapeflag);
+                 }
              }
          }
        else
diff --git a/src/process.c b/src/process.c
index a5f0cf9de4a..d836f4ff476 100644
--- a/src/process.c
+++ b/src/process.c
@@ -119,6 +119,11 @@ static struct rlimit nofile_limit;
 #include "gnutls.h"
 #endif
 
+#ifdef HAVE_ANDROID
+#include "android.h"
+#include "androidterm.h"
+#endif
+
 #ifdef HAVE_WINDOW_SYSTEM
 #include TERM_HEADER
 #endif /* HAVE_WINDOW_SYSTEM */
@@ -876,7 +881,8 @@ allocate_pty (char pty_name[PTY_NAME_SIZE])
 
            /* Check to make certain that both sides are available.
               This avoids a nasty yet stupid bug in rlogins.  */
-           if (faccessat (AT_FDCWD, pty_name, R_OK | W_OK, AT_EACCESS) != 0)
+           if (sys_faccessat (AT_FDCWD, pty_name,
+                              R_OK | W_OK, AT_EACCESS) != 0)
              {
                emacs_close (fd);
                continue;
@@ -2002,7 +2008,7 @@ usage: (make-process &rest ARGS)  */)
        {
          tem = Qnil;
          openp (Vexec_path, program, Vexec_suffixes, &tem,
-                make_fixnum (X_OK), false, false);
+                make_fixnum (X_OK), false, false, NULL);
          if (NILP (tem))
            report_file_error ("Searching for program", program);
          tem = Fexpand_file_name (tem, Qnil);
@@ -5679,7 +5685,17 @@ wait_reading_process_output (intmax_t time_limit, int 
nsecs, int read_kbd,
            timeout = short_timeout;
 #endif
 
-         /* Non-macOS HAVE_GLIB builds call thread_select in xgselect.c.  */
+         /* Android doesn't support threads and requires using a
+            replacement for pselect in android.c to poll for
+            events.  */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+         nfds = android_select (max_desc + 1,
+                                &Available, (check_write ? &Writeok : 0),
+                                NULL, &timeout);
+#else
+
+         /* Non-macOS HAVE_GLIB builds call thread_select in
+            xgselect.c.  */
 #if defined HAVE_GLIB && !defined HAVE_NS
          nfds = xg_select (max_desc + 1,
                            &Available, (check_write ? &Writeok : 0),
@@ -5695,6 +5711,7 @@ wait_reading_process_output (intmax_t time_limit, int 
nsecs, int read_kbd,
                                (check_write ? &Writeok : 0),
                                NULL, &timeout, NULL);
 #endif /* !HAVE_GLIB */
+#endif /* HAVE_ANDROID && !ANDROID_STUBIFY */
 
 #ifdef HAVE_GNUTLS
          /* Merge tls_available into Available. */
@@ -7232,10 +7249,10 @@ process has been transmitted to the serial port.  */)
     send_process (proc, "\004", 1, Qnil);
   else if (EQ (XPROCESS (proc)->type, Qserial))
     {
-#ifndef WINDOWSNT
+#if !defined WINDOWSNT && defined HAVE_TCDRAIN
       if (tcdrain (XPROCESS (proc)->outfd) != 0)
        report_file_error ("Failed tcdrain", Qnil);
-#endif /* not WINDOWSNT */
+#endif /* not WINDOWSNT && not TCDRAIN */
       /* Do nothing on Windows because writes are blocking.  */
     }
   else
@@ -7455,6 +7472,16 @@ handle_child_signal (int sig)
            {
              changed = true;
              if (STRINGP (XCDR (head)))
+               /* handle_child_signal is called in an async signal
+                  handler but needs to unlink temporary files which
+                  might've been created in an Android content
+                  provider.
+
+                  emacs_unlink is not async signal safe because
+                  deleting files from content providers must proceed
+                  through Java code.  Consequentially, if XCDR (head)
+                  lies on a content provider it will not be removed,
+                  which is a bug.  */
                unlink (SSDATA (XCDR (head)));
              XSETCAR (tail, Qnil);
            }
diff --git a/src/scroll.c b/src/scroll.c
index eee1ad80950..1f530dc5c95 100644
--- a/src/scroll.c
+++ b/src/scroll.c
@@ -21,6 +21,11 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 #include <config.h>
 
+/* The entire file is defined out under Android, where there is no
+   text terminal support of any kind.  */
+
+#ifndef HAVE_ANDROID
+
 #include "lisp.h"
 #include "termchar.h"
 #include "dispextern.h"
@@ -984,3 +989,5 @@ do_line_insertion_deletion_costs (struct frame *frame,
                 FRAME_DELETE_COST (frame), FRAME_DELETEN_COST (frame),
                 coefficient);
 }
+
+#endif
diff --git a/src/sfnt.c b/src/sfnt.c
new file mode 100644
index 00000000000..876db70bcda
--- /dev/null
+++ b/src/sfnt.c
@@ -0,0 +1,19768 @@
+/* TrueType format font support for GNU Emacs.
+
+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, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#include <config.h>
+
+#include "sfnt.h"
+
+#include <assert.h>
+#include <attribute.h>
+#include <byteswap.h>
+#include <fcntl.h>
+#include <intprops.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <errno.h>
+
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
+#if defined __GNUC__ && !defined __clang__
+#pragma GCC diagnostic ignored "-Wstringop-overflow"
+#endif
+
+#ifdef TEST
+
+#include <time.h>
+#include <timespec.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xrender.h>
+
+static void *
+xmalloc (size_t size)
+{
+  void *ptr;
+
+  ptr = malloc (size);
+
+  if (!ptr)
+    abort ();
+
+  return ptr;
+}
+
+static void *
+xrealloc (void *ptr, size_t size)
+{
+  void *new_ptr;
+
+  new_ptr = realloc (ptr, size);
+
+  if (!new_ptr)
+    abort ();
+
+  return new_ptr;
+}
+
+static void
+xfree (void *ptr)
+{
+  return free (ptr);
+}
+
+/* Use this for functions that are static while building in test mode,
+   but are used outside as well.  */
+#define TEST_STATIC static
+
+/* Needed for tests.  */
+#define ARRAYELTS(arr) (sizeof (arr) / sizeof (arr)[0])
+
+#else
+#define TEST_STATIC
+#include "lisp.h"
+#endif
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+/* This file provides generic support for reading most TrueType fonts,
+   and some OpenType fonts with TrueType outlines, along with glyph
+   lookup, outline decomposition, and alpha mask generation from those
+   glyphs.  It is intended to be used on any platform where proper
+   libraries such as FreeType are not easily available, and the native
+   font library is too limited for Emacs to support properly.
+
+   Unlike most popular libraries for handling fonts, no ``font'' or
+   ``face'' type is provided.  Instead, routines and structure
+   definitions for accessing and making use of individual tables in a
+   font file are exported, which allows for flexibility in the rest of
+   Emacs.
+
+   Try not to keep this file too dependent on Emacs.  Everything Lisp
+   related goes in sfntfont.c.  The author wants to keep using it for
+   some other (free) software.
+
+   The source of reference is the TrueType Reference Manual, published
+   by Apple Computer, which is currently found at:
+
+     https://developer.apple.com/fonts/TrueType-Reference-Manual/
+
+   Apple's TrueType implementation is notably missing features
+   provided by Microsoft's extended OpenType scaler, such as the two
+   additional phantom points on the Y axis, and also behaves
+   differently, especially when it comes to considering phantom points
+   as anchors in compound glyphs.
+
+   As a result, do not expect this scaler to work well with Microsoft
+   fonts such as Arial.  */
+
+
+
+/* Mapping between sfnt table names and their identifiers.  */
+
+static uint32_t sfnt_table_names[] =
+  {
+    [SFNT_TABLE_CMAP] = 0x636d6170,
+    [SFNT_TABLE_GLYF] = 0x676c7966,
+    [SFNT_TABLE_HEAD] = 0x68656164,
+    [SFNT_TABLE_HHEA] = 0x68686561,
+    [SFNT_TABLE_HMTX] = 0x686d7478,
+    [SFNT_TABLE_LOCA] = 0x6c6f6361,
+    [SFNT_TABLE_MAXP] = 0x6d617870,
+    [SFNT_TABLE_NAME] = 0x6e616d65,
+    [SFNT_TABLE_META] = 0x6d657461,
+    [SFNT_TABLE_CVT ] = 0x63767420,
+    [SFNT_TABLE_FPGM] = 0x6670676d,
+    [SFNT_TABLE_PREP] = 0x70726570,
+    [SFNT_TABLE_FVAR] = 0x66766172,
+    [SFNT_TABLE_GVAR] = 0x67766172,
+    [SFNT_TABLE_CVAR] = 0x63766172,
+    [SFNT_TABLE_AVAR] = 0x61766172,
+  };
+
+/* Swap values from TrueType to system byte order.  */
+
+static void
+sfnt_swap16_1 (uint16_t *value)
+{
+#ifndef WORDS_BIGENDIAN
+  *value = bswap_16 (*value);
+#endif
+}
+
+static void
+sfnt_swap32_1 (uint32_t *value)
+{
+#ifndef WORDS_BIGENDIAN
+  *value = bswap_32 (*value);
+#endif
+}
+
+#define sfnt_swap16(what) (sfnt_swap16_1 ((uint16_t *) (what)))
+#define sfnt_swap32(what) (sfnt_swap32_1 ((uint32_t *) (what)))
+
+/* Read the table directory from the file FD.  FD must currently be at
+   the start of the file (or an offset defined in the TTC header, if
+   applicable), and must be seekable.  Return the table directory upon
+   success, else NULL.
+
+   Value is NULL upon failure, and the offset subtable upon success.
+   If FD is actually a TrueType collection file, value is -1.  */
+
+TEST_STATIC struct sfnt_offset_subtable *
+sfnt_read_table_directory (int fd)
+{
+  struct sfnt_offset_subtable *subtable;
+  ssize_t rc;
+  size_t offset, subtable_size;
+  int i;
+
+  subtable = xmalloc (sizeof *subtable);
+  offset = SFNT_ENDOF (struct sfnt_offset_subtable,
+                      range_shift, uint16_t);
+  rc = read (fd, subtable, offset);
+
+  if (rc < offset)
+    {
+      if (rc >= sizeof (uint32_t))
+       {
+         /* Detect a TTC file.  In that case, the first long will be
+            ``ttcf''.  */
+         sfnt_swap32 (&subtable->scaler_type);
+
+         if (subtable->scaler_type == SFNT_TTC_TTCF)
+           {
+             xfree (subtable);
+             return (struct sfnt_offset_subtable *) -1;
+           }
+       }
+
+      xfree (subtable);
+      return NULL;
+    }
+
+  sfnt_swap32 (&subtable->scaler_type);
+
+  /* Bail out early if this font is actually a TrueType collection
+     file.  */
+
+  if (subtable->scaler_type == SFNT_TTC_TTCF)
+    {
+      xfree (subtable);
+      return (struct sfnt_offset_subtable *) -1;
+    }
+
+  sfnt_swap16 (&subtable->num_tables);
+  sfnt_swap16 (&subtable->search_range);
+  sfnt_swap16 (&subtable->entry_selector);
+  sfnt_swap16 (&subtable->range_shift);
+
+  /* Figure out how many more tables have to be read, and read each
+     one of them.  */
+  subtable_size = (subtable->num_tables
+                  * sizeof (struct sfnt_table_directory));
+  subtable = xrealloc (subtable, sizeof *subtable + subtable_size);
+  subtable->subtables
+    = (struct sfnt_table_directory *) (subtable + 1);
+
+  rc = read (fd, subtable->subtables, subtable_size);
+
+  if (rc < offset)
+    {
+      xfree (subtable);
+      return NULL;
+    }
+
+  /* Swap each of the subtables.  */
+
+  for (i = 0; i < subtable->num_tables; ++i)
+    {
+      sfnt_swap32 (&subtable->subtables[i].tag);
+      sfnt_swap32 (&subtable->subtables[i].checksum);
+      sfnt_swap32 (&subtable->subtables[i].offset);
+      sfnt_swap32 (&subtable->subtables[i].length);
+    }
+
+  return subtable;
+}
+
+/* Return a pointer to the table directory entry for TABLE in
+   SUBTABLE, or NULL if it was not found.  */
+
+static struct sfnt_table_directory *
+sfnt_find_table (struct sfnt_offset_subtable *subtable,
+                enum sfnt_table table)
+{
+  int i;
+
+  for (i = 0; i < subtable->num_tables; ++i)
+    {
+      if (subtable->subtables[i].tag == sfnt_table_names[table])
+       return &subtable->subtables[i];
+    }
+
+  return NULL;
+}
+
+
+
+/* Character mapping routines.  */
+
+/* Read a format 0 cmap subtable from FD.  HEADER has already been
+   read.  */
+
+static struct sfnt_cmap_format_0 *
+sfnt_read_cmap_format_0 (int fd,
+                        struct sfnt_cmap_encoding_subtable_data *header)
+{
+  struct sfnt_cmap_format_0 *format0;
+  ssize_t rc;
+  size_t wanted_size;
+
+  format0 = xmalloc (sizeof *format0);
+
+  /* Fill in fields that have already been read.  */
+  format0->format = header->format;
+  format0->length = header->length;
+
+  /* Read the rest.  */
+  wanted_size = (sizeof *format0
+                - offsetof (struct sfnt_cmap_format_0,
+                            language));
+  rc = read (fd, &format0->language, wanted_size);
+
+  if (rc < wanted_size)
+    {
+      xfree (format0);
+      return (struct sfnt_cmap_format_0 *) -1;
+    }
+
+  /* Swap fields and return.  */
+  sfnt_swap16 (&format0->language);
+  return format0;
+}
+
+/* Read a format 2 cmap subtable from FD.  HEADER has already been
+   read.  */
+
+static struct sfnt_cmap_format_2 *
+sfnt_read_cmap_format_2 (int fd,
+                        struct sfnt_cmap_encoding_subtable_data *header)
+{
+  struct sfnt_cmap_format_2 *format2;
+  ssize_t rc;
+  size_t min_bytes;
+  int i, nsub;
+
+  /* Reject contents that are too small.  */
+  min_bytes = SFNT_ENDOF (struct sfnt_cmap_format_2,
+                         sub_header_keys, uint16_t[256]);
+  if (header->length < min_bytes)
+    return NULL;
+
+  /* Add enough bytes at the end to fit the two variable length
+     pointers.  */
+  format2 = xmalloc (header->length + sizeof *format2);
+  format2->format = header->format;
+  format2->length = header->length;
+
+  /* Read the part before the variable length data.  */
+  min_bytes -= offsetof (struct sfnt_cmap_format_2, language);
+  rc = read (fd, &format2->language, min_bytes);
+  if (rc < min_bytes)
+    {
+      xfree (format2);
+      return (struct sfnt_cmap_format_2 *) -1;
+    }
+
+  /* Swap the fields now.  */
+
+  sfnt_swap16 (&format2->language);
+
+  /* At the same time, look for the largest value in sub_header_keys.
+     That will be the number of subheaders and elements in the glyph
+     index array.  */
+
+  nsub = 0;
+
+  for (i = 0; i < 256; ++i)
+    {
+      sfnt_swap16 (&format2->sub_header_keys[i]);
+
+      if (format2->sub_header_keys[i] > nsub)
+       nsub = format2->sub_header_keys[i];
+    }
+
+  if (!nsub)
+    /* If there are no subheaders, then things are finished.  */
+    return format2;
+
+  /* Otherwise, read the rest of the variable length data to the end
+     of format2.  */
+  min_bytes = (format2->length
+              - SFNT_ENDOF (struct sfnt_cmap_format_2,
+                            sub_header_keys, uint16_t[256]));
+  rc = read (fd, format2 + 1, min_bytes);
+  if (rc < min_bytes)
+    {
+      xfree (format2);
+      return (struct sfnt_cmap_format_2 *) -1;
+    }
+
+  /* Check whether or not the data is of the correct size.  */
+  if (min_bytes < nsub * sizeof *format2->subheaders)
+    {
+      xfree (format2);
+      return (struct sfnt_cmap_format_2 *) -1;
+    }
+
+  /* Point the data pointers to the right location, swap everything,
+     and return.  */
+
+  format2->subheaders
+    = (struct sfnt_cmap_format_2_subheader *) (format2 + 1);
+  format2->glyph_index_array
+    = (uint16_t *) (format2->subheaders + nsub);
+
+  for (i = 0; i < nsub; ++i)
+    {
+      sfnt_swap16 (&format2->subheaders[i].first_code);
+      sfnt_swap16 (&format2->subheaders[i].entry_count);
+      sfnt_swap16 (&format2->subheaders[i].id_delta);
+      sfnt_swap16 (&format2->subheaders[i].id_range_offset);
+    }
+
+  /* Figure out how big the glyph index array is, and swap everything
+     there.  */
+  format2->num_glyphs
+    = (min_bytes - nsub * sizeof *format2->subheaders) / 2;
+
+  for (i = 0; i < format2->num_glyphs; ++i)
+    sfnt_swap16 (&format2->glyph_index_array[i]);
+
+  return format2;
+}
+
+/* Read a format 4 cmap subtable from FD.  HEADER has already been
+   read.  */
+
+static struct sfnt_cmap_format_4 *
+sfnt_read_cmap_format_4 (int fd,
+                        struct sfnt_cmap_encoding_subtable_data *header)
+{
+  struct sfnt_cmap_format_4 *format4;
+  size_t min_bytes, variable_size;
+  ssize_t rc;
+  size_t bytes_minus_format4;
+  int seg_count, i;
+
+  min_bytes = SFNT_ENDOF (struct sfnt_cmap_format_4,
+                         entry_selector, uint16_t);
+
+  /* Check that the length is at least min_bytes.  */
+  if (header->length < min_bytes)
+    return NULL;
+
+  /* Allocate the format4 buffer, making it the size of the buffer
+     itself plus that of the data.  */
+  format4 = xmalloc (header->length + sizeof *format4);
+
+  /* Copy over fields that have already been read.  */
+  format4->format = header->format;
+  format4->length = header->length;
+
+  /* Read the initial data.  */
+  min_bytes -= offsetof (struct sfnt_cmap_format_4, language);
+  rc = read (fd, &format4->language, min_bytes);
+  if (rc < min_bytes)
+    {
+      xfree (format4);
+      return (struct sfnt_cmap_format_4 *) -1;
+    }
+
+  /* Swap fields that have been read.  */
+  sfnt_swap16 (&format4->language);
+  sfnt_swap16 (&format4->seg_count_x2);
+  sfnt_swap16 (&format4->search_range);
+  sfnt_swap16 (&format4->entry_selector);
+
+  /* Get the number of segments to read.  */
+  seg_count = format4->seg_count_x2 / 2;
+
+  /* Now calculate whether or not the size is sufficiently large.  */
+  bytes_minus_format4
+    = format4->length - SFNT_ENDOF (struct sfnt_cmap_format_4,
+                                   entry_selector, uint16_t);
+  variable_size = (seg_count * sizeof *format4->end_code
+                  + sizeof *format4->reserved_pad
+                  + seg_count * sizeof *format4->start_code
+                  + seg_count * sizeof *format4->id_delta
+                  + seg_count * sizeof *format4->id_range_offset);
+
+  if (bytes_minus_format4 < variable_size)
+    {
+      /* Not enough bytes to fit the entire implied table
+        contents.  */
+      xfree (format4);
+      return NULL;
+    }
+
+  /* Read the rest of the bytes to the end of format4.  */
+  rc = read (fd, format4 + 1, bytes_minus_format4);
+  if (rc < bytes_minus_format4)
+    {
+      xfree (format4);
+      return (struct sfnt_cmap_format_4 *) -1;
+    }
+
+  /* Set data pointers to the right locations.  */
+  format4->end_code = (uint16_t *) (format4 + 1);
+  format4->reserved_pad = format4->end_code + seg_count;
+  format4->start_code = format4->reserved_pad + 1;
+  format4->id_delta = (int16_t *) (format4->start_code + seg_count);
+  format4->id_range_offset = format4->id_delta + seg_count;
+  format4->glyph_index_array = (uint16_t *) (format4->id_range_offset
+                                            + seg_count);
+
+  /* N.B. that the number of elements in glyph_index_array is
+     (bytes_minus_format4 - variable_size) / 2.  Swap all the
+     data.  */
+
+  sfnt_swap16 (format4->reserved_pad);
+
+  for (i = 0; i < seg_count; ++i)
+    {
+      sfnt_swap16 (&format4->end_code[i]);
+      sfnt_swap16 (&format4->start_code[i]);
+      sfnt_swap16 (&format4->id_delta[i]);
+      sfnt_swap16 (&format4->id_range_offset[i]);
+    }
+
+  format4->glyph_index_size
+    = (bytes_minus_format4 - variable_size) / 2;
+
+  for (i = 0; i < format4->glyph_index_size; ++i)
+    sfnt_swap16 (&format4->glyph_index_array[i]);
+
+  /* Done.  Return the format 4 character map.  */
+  return format4;
+}
+
+/* Read a format 6 cmap subtable from FD.  HEADER has already been
+   read.  */
+
+static struct sfnt_cmap_format_6 *
+sfnt_read_cmap_format_6 (int fd,
+                        struct sfnt_cmap_encoding_subtable_data *header)
+{
+  struct sfnt_cmap_format_6 *format6;
+  size_t min_size;
+  ssize_t rc;
+  uint16_t i;
+
+  min_size = SFNT_ENDOF (struct sfnt_cmap_format_6, entry_count,
+                        uint16_t);
+
+  /* See if header->length is big enough.  */
+  if (header->length < min_size)
+    return NULL;
+
+  /* Allocate the buffer to hold header->size and enough for at least
+     the glyph index array pointer.  */
+  format6 = xmalloc (header->length + sizeof *format6);
+
+  /* Fill in data that has already been read.  */
+  format6->format = header->format;
+  format6->length = header->length;
+
+  /* Read the fixed size data.  */
+  min_size -= offsetof (struct sfnt_cmap_format_6, language);
+  rc = read (fd, &format6->language, min_size);
+  if (rc < min_size)
+    {
+      xfree (format6);
+      return (struct sfnt_cmap_format_6 *) -1;
+    }
+
+  /* Swap what was read.  */
+  sfnt_swap16 (&format6->language);
+  sfnt_swap16 (&format6->first_code);
+  sfnt_swap16 (&format6->entry_count);
+
+  /* Figure out whether or not header->length is sufficient to hold
+     the variable length data.  */
+  if (header->length
+      < format6->entry_count * sizeof *format6->glyph_index_array)
+    {
+      xfree (format6);
+      return NULL;
+    }
+
+  /* Read the variable length data.  */
+  rc = read (fd, format6 + 1,
+            (format6->entry_count
+             * sizeof *format6->glyph_index_array));
+  if (rc < format6->entry_count * sizeof *format6->glyph_index_array)
+    {
+      xfree (format6);
+      return (struct sfnt_cmap_format_6 *) -1;
+    }
+
+  /* Set the data pointer and swap everything.  */
+  format6->glyph_index_array = (uint16_t *) (format6 + 1);
+  for (i = 0; i < format6->entry_count; ++i)
+    sfnt_swap16 (&format6->glyph_index_array[i]);
+
+  /* All done! */
+  return format6;
+}
+
+/* Read a format 8 cmap subtable from FD.  HEADER has already been
+   read.  */
+
+static struct sfnt_cmap_format_8 *
+sfnt_read_cmap_format_8 (int fd,
+                        struct sfnt_cmap_encoding_subtable_data *header)
+{
+  struct sfnt_cmap_format_8 *format8;
+  size_t min_size, temp;
+  ssize_t rc;
+  uint32_t length, i;
+
+  /* Read the 32-bit length field.  */
+  if (read (fd, &length, sizeof (length)) < sizeof (length))
+    return (struct sfnt_cmap_format_8 *) -1;
+
+  /* Swap the 32-bit length field.  */
+  sfnt_swap32 (&length);
+
+  min_size = SFNT_ENDOF (struct sfnt_cmap_format_8, num_groups,
+                        uint32_t);
+
+  /* Make sure the header is at least as large as min_size.  */
+  if (length < min_size)
+    return NULL;
+
+  /* Allocate a buffer of sufficient size.  */
+  format8 = xmalloc (length + sizeof *format8);
+  format8->format = header->format;
+  format8->reserved = header->length;
+  format8->length = length;
+
+  /* Read the fixed length data.  */
+  min_size -= offsetof (struct sfnt_cmap_format_8, language);
+  rc = read (fd, &format8->language, min_size);
+  if (rc < min_size)
+    {
+      xfree (format8);
+      return (struct sfnt_cmap_format_8 *) -1;
+    }
+
+  /* Swap what was read.  */
+  sfnt_swap32 (&format8->language);
+  sfnt_swap32 (&format8->num_groups);
+
+  /* See if the size is sufficient to read the variable length
+     data.  */
+  min_size = SFNT_ENDOF (struct sfnt_cmap_format_8, num_groups,
+                        uint32_t);
+
+  if (INT_MULTIPLY_WRAPV (format8->num_groups, sizeof *format8->groups,
+                         &temp))
+    {
+      xfree (format8);
+      return NULL;
+    }
+
+  if (INT_ADD_WRAPV (min_size, temp, &min_size))
+    {
+      xfree (format8);
+      return NULL;
+    }
+
+  if (length < min_size)
+    {
+      xfree (format8);
+      return NULL;
+    }
+
+  /* Now read the variable length data.  */
+  rc = read (fd, format8 + 1, temp);
+  if (rc < temp)
+    {
+      xfree (format8);
+      return (struct sfnt_cmap_format_8 *) -1;
+    }
+
+  /* Set the pointer to the variable length data.  */
+  format8->groups
+    = (struct sfnt_cmap_format_8_or_12_group *) (format8 + 1);
+
+  for (i = 0; i < format8->num_groups; ++i)
+    {
+      sfnt_swap32 (&format8->groups[i].start_char_code);
+      sfnt_swap32 (&format8->groups[i].end_char_code);
+      sfnt_swap32 (&format8->groups[i].start_glyph_code);
+    }
+
+  /* All done.  */
+  return format8;
+}
+
+/* Read a format 12 cmap subtable from FD.  HEADER has already been
+   read.  */
+
+static struct sfnt_cmap_format_12 *
+sfnt_read_cmap_format_12 (int fd,
+                         struct sfnt_cmap_encoding_subtable_data *header)
+{
+  struct sfnt_cmap_format_12 *format12;
+  size_t min_size, temp;
+  ssize_t rc;
+  uint32_t length, i;
+
+  /* Read the 32-bit length field.  */
+  if (read (fd, &length, sizeof (length)) < sizeof (length))
+    return (struct sfnt_cmap_format_12 *) -1;
+
+  /* Swap the 32-bit length field.  */
+  sfnt_swap32 (&length);
+
+  min_size = SFNT_ENDOF (struct sfnt_cmap_format_12, num_groups,
+                        uint32_t);
+
+  /* Make sure the header is at least as large as min_size.  */
+  if (length < min_size)
+    return NULL;
+
+  /* Allocate a buffer of sufficient size.  */
+  format12 = xmalloc (length + sizeof *format12);
+  format12->format = header->format;
+  format12->reserved = header->length;
+  format12->length = length;
+
+  /* Read the fixed length data.  */
+  min_size -= offsetof (struct sfnt_cmap_format_12, language);
+  rc = read (fd, &format12->language, min_size);
+  if (rc < min_size)
+    {
+      xfree (format12);
+      return (struct sfnt_cmap_format_12 *) -1;
+    }
+
+  /* Swap what was read.  */
+  sfnt_swap32 (&format12->language);
+  sfnt_swap32 (&format12->num_groups);
+
+  /* See if the size is sufficient to read the variable length
+     data.  */
+  min_size = SFNT_ENDOF (struct sfnt_cmap_format_12, num_groups,
+                        uint32_t);
+
+  if (INT_MULTIPLY_WRAPV (format12->num_groups, sizeof *format12->groups,
+                         &temp))
+    {
+      xfree (format12);
+      return NULL;
+    }
+
+  if (INT_ADD_WRAPV (min_size, temp, &min_size))
+    {
+      xfree (format12);
+      return NULL;
+    }
+
+  if (length < min_size)
+    {
+      xfree (format12);
+      return NULL;
+    }
+
+  /* Now read the variable length data.  */
+  rc = read (fd, format12 + 1, temp);
+  if (rc < temp)
+    {
+      xfree (format12);
+      return (struct sfnt_cmap_format_12 *) -1;
+    }
+
+  /* Set the pointer to the variable length data.  */
+  format12->groups
+    = (struct sfnt_cmap_format_8_or_12_group *) (format12 + 1);
+
+  for (i = 0; i < format12->num_groups; ++i)
+    {
+      sfnt_swap32 (&format12->groups[i].start_char_code);
+      sfnt_swap32 (&format12->groups[i].end_char_code);
+      sfnt_swap32 (&format12->groups[i].start_glyph_code);
+    }
+
+  /* All done.  */
+  return format12;
+}
+
+/* Read a 3-byte big endian number from BYTES.  */
+
+static unsigned int
+sfnt_read_24 (unsigned char *bytes)
+{
+  return (bytes[0] << 16u) | (bytes[1] << 8u) | bytes[2];
+}
+
+/* Read a format 14 cmap table from FD.  HEADER->format will be 14 and
+   HEADER->length will be 0; the 16-bit length field is not read.
+   OFFSET is the offset of the table's header in the font file.
+
+   Only variation selector records will be read.  UVS tables will
+   not.  */
+
+static struct sfnt_cmap_format_14 *
+sfnt_read_cmap_format_14 (int fd,
+                         struct sfnt_cmap_encoding_subtable_data *header,
+                         off_t offset)
+{
+  struct sfnt_cmap_format_14 *format14;
+  uint32_t length;
+  uint32_t num_records;
+  uint32_t buffer1[2];
+  size_t size, temp;
+  char buffer[3 + 4 + 4];
+  int i;
+
+  /* Read the length field and number of variation selector
+     records.  */
+
+  if (read (fd, buffer1, sizeof buffer1) < sizeof buffer1)
+    return NULL;
+
+  length = buffer1[0];
+  num_records = buffer1[1];
+
+  sfnt_swap32 (&length);
+  sfnt_swap32 (&num_records);
+
+  /* Now, the number of records present is known.  Allocate the format
+     14 cmap table.  */
+
+  size = sizeof *format14;
+  if (INT_MULTIPLY_WRAPV (num_records, sizeof *format14->records,
+                         &temp)
+      || INT_ADD_WRAPV (size, temp, &size))
+    return NULL;
+
+  format14 = xmalloc (size);
+
+  /* Fill in the data already read.  */
+  format14->format = header->format;
+  format14->length = length;
+  format14->num_var_selector_records = num_records;
+  format14->offset = offset;
+
+  /* Set the pointer to the remaining record data.  */
+  format14->records
+    = (struct sfnt_variation_selector_record *) (format14 + 1);
+
+  /* Read each variation selector record.  */
+
+  for (i = 0; i < num_records; ++i)
+    {
+      if (read (fd, buffer, sizeof buffer) < sizeof buffer)
+       {
+         xfree (format14);
+         return NULL;
+       }
+
+      /* First, read the 24 bit variation selector.  */
+      format14->records[i].var_selector
+       = sfnt_read_24 ((unsigned char *) buffer);
+
+      /* Next, read the two unaligned longs.  */
+      memcpy (&format14->records[i].default_uvs_offset,
+             buffer + 3,
+             sizeof format14->records[i].default_uvs_offset);
+      memcpy (&format14->records[i].nondefault_uvs_offset,
+             buffer + 7,
+             sizeof format14->records[i].nondefault_uvs_offset);
+
+      /* And swap them.  */
+      sfnt_swap32 (&format14->records[i].default_uvs_offset);
+      sfnt_swap32 (&format14->records[i].nondefault_uvs_offset);
+    }
+
+  /* Return the format 14 character mapping table.  */
+  return format14;
+}
+
+/* Read the CMAP subtable data from a given file FD at TABLE_OFFSET
+   bytes from DIRECTORY_OFFSET.  Return the subtable data if it is
+   supported.  Else, value is NULL if the format is unsupported, or -1
+   upon an IO error.  */
+
+static struct sfnt_cmap_encoding_subtable_data *
+sfnt_read_cmap_table_1 (int fd, uint32_t directory_offset,
+                       uint32_t table_offset)
+{
+  off_t offset;
+  struct sfnt_cmap_encoding_subtable_data header;
+
+  if (INT_ADD_WRAPV (directory_offset, table_offset, &offset))
+    return (struct sfnt_cmap_encoding_subtable_data *) -1;
+
+  if (lseek (fd, offset, SEEK_SET) == (off_t) -1)
+    return (struct sfnt_cmap_encoding_subtable_data *) -1;
+
+  if (read (fd, &header.format, sizeof header.format)
+      < sizeof header.format)
+    return (struct sfnt_cmap_encoding_subtable_data *) -1;
+
+  sfnt_swap16 (&header.format);
+
+  /* Format 14 tables are rather special: they do not have a 16-bit
+     `length' field.  When these tables are encountered, leave reading
+     the rest of the header to `sfnt_read_cmap_table_14'.  */
+
+  if (header.format != 14)
+    {
+      if (read (fd, &header.length, sizeof header.length)
+         < sizeof header.length)
+       return (struct sfnt_cmap_encoding_subtable_data *) -1;
+
+      sfnt_swap16 (&header.length);
+    }
+  else
+    header.length = 0;
+
+  switch (header.format)
+    {
+    case 0:
+      /* If the length changes, then something has changed to the
+        format.  */
+      if (header.length != 262)
+       return NULL;
+
+      return ((struct sfnt_cmap_encoding_subtable_data *)
+             sfnt_read_cmap_format_0 (fd, &header));
+
+    case 2:
+      return ((struct sfnt_cmap_encoding_subtable_data *)
+             sfnt_read_cmap_format_2 (fd, &header));
+
+    case 4:
+      return ((struct sfnt_cmap_encoding_subtable_data *)
+             sfnt_read_cmap_format_4 (fd, &header));
+
+    case 6:
+      return ((struct sfnt_cmap_encoding_subtable_data *)
+             sfnt_read_cmap_format_6 (fd, &header));
+
+    case 8:
+      return ((struct sfnt_cmap_encoding_subtable_data *)
+             sfnt_read_cmap_format_8 (fd, &header));
+
+    case 12:
+      return ((struct sfnt_cmap_encoding_subtable_data *)
+             sfnt_read_cmap_format_12 (fd, &header));
+
+    case 14:
+      return ((struct sfnt_cmap_encoding_subtable_data *)
+             sfnt_read_cmap_format_14 (fd, &header, offset));
+
+    default:
+      return NULL;
+    }
+}
+
+/* Read the CMAP table of a given font from the file FD.  Use the
+   table directory specified in SUBTABLE.
+
+   Return the CMAP table and a list of encoding subtables in
+   *SUBTABLES and *DATA upon success, else NULL.  If DATA is NULL, do
+   not read the subtable data.  */
+
+TEST_STATIC struct sfnt_cmap_table *
+sfnt_read_cmap_table (int fd, struct sfnt_offset_subtable *subtable,
+                     struct sfnt_cmap_encoding_subtable **subtables,
+                     struct sfnt_cmap_encoding_subtable_data ***data)
+{
+  struct sfnt_table_directory *directory;
+  struct sfnt_cmap_table *cmap;
+  ssize_t rc;
+  int i, j;
+
+  /* Find the CMAP table in the table directory.  */
+  directory = sfnt_find_table (subtable, SFNT_TABLE_CMAP);
+
+  if (!directory)
+    return NULL;
+
+  /* Seek to the start of the CMAP table.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  /* Read the table header.  */
+  cmap = xmalloc (sizeof *cmap);
+  rc = read (fd, cmap, sizeof *cmap);
+
+  if (rc < sizeof *cmap)
+    {
+      xfree (cmap);
+      return NULL;
+    }
+
+  /* Swap the header data.  */
+  sfnt_swap16 (&cmap->version);
+  sfnt_swap16 (&cmap->num_subtables);
+
+  if (cmap->version != 0)
+    {
+      xfree (cmap);
+      return NULL;
+    }
+
+  *subtables = xmalloc (cmap->num_subtables
+                       * sizeof **subtables);
+
+
+  /* First, read the common parts of each encoding subtable.  */
+
+  for (i = 0; i < cmap->num_subtables; ++i)
+    {
+      /* Read the common part of the new subtable.  */
+      rc = read (fd, &(*subtables)[i], sizeof (*subtables)[i]);
+
+      if (rc < sizeof (*subtables))
+       {
+         xfree (cmap);
+         xfree (*subtables);
+         return NULL;
+       }
+
+      sfnt_swap16 (&(*subtables)[i].platform_id);
+      sfnt_swap16 (&(*subtables)[i].platform_specific_id);
+      sfnt_swap32 (&(*subtables)[i].offset);
+    }
+
+  /* If data is NULL, the caller only wants the table headers.  */
+
+  if (!data)
+    return cmap;
+
+  /* Second, read each encoding subtable itself.  */
+  *data = xmalloc (cmap->num_subtables * sizeof *data);
+
+  for (i = 0; i < cmap->num_subtables; ++i)
+    {
+      (*data)[i] = sfnt_read_cmap_table_1 (fd, directory->offset,
+                                          (*subtables)[i].offset);
+
+      if ((*data)[i] == (void *) -1)
+       {
+         /* An IO error occurred (as opposed to the subtable format
+            being unsupported.)  Return now.  */
+
+         for (j = 0; j < i; ++j)
+           xfree ((*data)[j]);
+
+         xfree (*data);
+         xfree (*subtables);
+         xfree (cmap);
+         return NULL;
+       }
+    }
+
+  return cmap;
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 0 cmap
+   FORMAT0.  Return 0 if no glyph was found.  */
+
+static sfnt_glyph
+sfnt_lookup_glyph_0 (sfnt_char character,
+                    struct sfnt_cmap_format_0 *format0)
+{
+  if (character >= 256)
+    return 0;
+
+  return format0->glyph_index_array[character];
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 2 cmap
+   FORMAT2.  Return 0 if no glyph was found.  */
+
+static sfnt_glyph
+sfnt_lookup_glyph_2 (sfnt_char character,
+                    struct sfnt_cmap_format_2 *format2)
+{
+  unsigned char i, k, j;
+  struct sfnt_cmap_format_2_subheader *subheader;
+  unsigned char *slice;
+  uint16_t glyph;
+
+  if (character > 65335)
+    return 0;
+
+  i = character >> 16;
+  j = character & 0xff;
+  k = format2->sub_header_keys[i] / 8;
+
+  if (k)
+    {
+      subheader = &format2->subheaders[k];
+
+      if (subheader->first_code <= j
+         && j <= ((int) subheader->first_code
+                  + (int) subheader->entry_count))
+       {
+         /* id_range_offset is actually the number of bytes past
+            itself containing the uint16_t ``slice''.  It is possibly
+            unaligned.  */
+         slice = (unsigned char *) &subheader->id_range_offset;
+         slice += subheader->id_range_offset;
+         slice += (j - subheader->first_code) * sizeof (uint16_t);
+
+         if (slice < (unsigned char *) format2->glyph_index_array
+             || (slice + 1
+                 > (unsigned char *) (format2->glyph_index_array
+                                      + format2->num_glyphs)))
+           /* The character is out of bounds.  */
+           return 0;
+
+         memcpy (&glyph, slice, sizeof glyph);
+         return (glyph + subheader->id_delta) % 65536;
+       }
+      else
+       return 0;
+    }
+
+  /* k is 0, so glyph_index_array[i] is the glyph.  */
+  return (i < format2->num_glyphs
+         ? format2->glyph_index_array[i]
+         : 0);
+}
+
+/* Like `bsearch'.  However, return the highest element above KEY if
+   it could not be found.  */
+
+static void *
+sfnt_bsearch_above (const void *key, const void *base,
+                   size_t nmemb, size_t size,
+                   int (*compar) (const void *,
+                                  const void *))
+{
+  const unsigned char *bytes, *sample;
+  size_t low, high, mid;
+
+  bytes = base;
+  low = 0;
+  high = nmemb - 1;
+
+  if (!nmemb)
+    return NULL;
+
+  while (low != high)
+    {
+      mid = low + (high - low) / 2;
+      sample = bytes + mid * size;
+
+      if (compar (key, sample) > 0)
+       low = mid + 1;
+      else
+       high = mid;
+    }
+
+  return (unsigned char *) bytes + low * size;
+}
+
+/* Compare two uint16_t's.  Used to bisect through a format 4
+   table.  */
+
+static int
+sfnt_compare_uint16 (const void *a, const void *b)
+{
+  return ((int) *((uint16_t *) a)) - ((int) *((uint16_t *) b));
+}
+
+/* Look up the glyph corresponding to CODE in the format 4 cmap
+   FORMAT4, using the table segment SEGMENT.  Value is 0 if no glyph
+   was found.  */
+
+static sfnt_glyph
+sfnt_lookup_glyph_4_1 (uint16_t code, uint16_t segment,
+                      struct sfnt_cmap_format_4 *format4)
+{
+  uint16_t *index;
+
+  if (format4->id_range_offset[segment])
+    {
+      /* id_range_offset is not 0, so the glyph mapping depends on
+        it.  */
+      index = (uint16_t *) (&format4->id_range_offset[segment]
+                           + format4->id_range_offset[segment] / 2
+                           + (code - format4->start_code[segment]));
+
+      /* Check that index is not out of bounds.  */
+      if (index >= (format4->glyph_index_array
+                   + format4->glyph_index_size)
+         || index < format4->glyph_index_array)
+       return 0;
+
+      /* Return what is in index.  */
+      return (*index ? (format4->id_delta[segment]
+                       + *index) % 65536 : 0);
+    }
+
+  /* Otherwise, just add id_delta.  */
+  return (format4->id_delta[segment] + code) % 65536;
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 4 cmap
+   FORMAT4.  Return 0 if no glyph was found.  */
+
+static sfnt_glyph
+sfnt_lookup_glyph_4 (sfnt_char character,
+                    struct sfnt_cmap_format_4 *format4)
+{
+  uint16_t *segment_address;
+  uint16_t code, segment;
+  sfnt_glyph glyph;
+
+  if (character > 65535)
+    return 0;
+
+  code = character;
+
+  /* Find the segment ending above or at CHARACTER.  */
+  segment_address = sfnt_bsearch_above (&code, format4->end_code,
+                                       format4->seg_count_x2 / 2,
+                                       sizeof code,
+                                       sfnt_compare_uint16);
+  segment = segment_address - format4->end_code;
+
+  /* If the segment starts too late, return 0.  */
+  if (!segment_address || format4->start_code[segment] > character)
+    return 0;
+
+  glyph = sfnt_lookup_glyph_4_1 (character, segment, format4);
+
+  if (glyph)
+    return glyph;
+
+  /* Droid Sans Mono has overlapping segments in its format 4 cmap
+     subtable where the first segment's end code is 32, while the
+     second segment's start code is also 32.  The TrueType Reference
+     Manual says that mapping should begin by searching for the first
+     segment whose end code is greater than or equal to the character
+     being indexed, but that results in the first subtable being
+     found, which doesn't work, while the second table does.  Try to
+     detect this situation and use the second table if possible.  */
+
+  if (!glyph
+      /* The character being looked up is the current segment's end
+        code.  */
+      && code == format4->end_code[segment]
+      /* There is an additional segment.  */
+      && segment + 1 < format4->seg_count_x2 / 2
+      /* That segment's start code is the same as this segment's end
+        code.  */
+      && format4->start_code[segment + 1] == format4->end_code[segment])
+    /* Try the second segment.  */
+    return sfnt_lookup_glyph_4_1 (character, segment + 1, format4);
+
+  /* Fail.  */
+  return 0;
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 6 cmap
+   FORMAT6.  Return 0 if no glyph was found.  */
+
+static sfnt_glyph
+sfnt_lookup_glyph_6 (sfnt_char character,
+                    struct sfnt_cmap_format_6 *format6)
+{
+  if (character < format6->first_code
+      || character >= (format6->first_code
+                      + (int) format6->entry_count))
+    return 0;
+
+  return format6->glyph_index_array[character - format6->first_code];
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 8 cmap
+   FORMAT8.  Return 0 if no glyph was found.  */
+
+static sfnt_glyph
+sfnt_lookup_glyph_8 (sfnt_char character,
+                    struct sfnt_cmap_format_8 *format8)
+{
+  uint32_t i;
+
+  if (character > 0xffffffff)
+    return 0;
+
+  for (i = 0; i < format8->num_groups; ++i)
+    {
+      if (format8->groups[i].start_char_code <= character
+         && format8->groups[i].end_char_code >= character)
+       return (format8->groups[i].start_glyph_code
+               + (character
+                  - format8->groups[i].start_char_code));
+    }
+
+  return 0;
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 12 cmap
+   FORMAT12.  Return 0 if no glyph was found.  */
+
+static sfnt_glyph
+sfnt_lookup_glyph_12 (sfnt_char character,
+                     struct sfnt_cmap_format_12 *format12)
+{
+  uint32_t i;
+
+  if (character > 0xffffffff)
+    return 0;
+
+  for (i = 0; i < format12->num_groups; ++i)
+    {
+      if (format12->groups[i].start_char_code <= character
+         && format12->groups[i].end_char_code >= character)
+       return (format12->groups[i].start_glyph_code
+               + (character
+                  - format12->groups[i].start_char_code));
+    }
+
+  return 0;
+}
+
+/* Look up the glyph index corresponding to the character CHARACTER,
+   which must be in the correct encoding for the cmap table pointed to
+   by DATA.
+
+   DATA must be either a format 0, 2, 4, 6, 8 or 12 cmap table, else
+   behavior is undefined.  */
+
+TEST_STATIC sfnt_glyph
+sfnt_lookup_glyph (sfnt_char character,
+                  struct sfnt_cmap_encoding_subtable_data *data)
+{
+  switch (data->format)
+    {
+    case 0:
+      return sfnt_lookup_glyph_0 (character,
+                                 (struct sfnt_cmap_format_0 *) data);
+
+    case 2:
+      return sfnt_lookup_glyph_2 (character,
+                                 (struct sfnt_cmap_format_2 *) data);
+
+    case 4:
+      return sfnt_lookup_glyph_4 (character,
+                                 (struct sfnt_cmap_format_4 *) data);
+
+    case 6:
+      return sfnt_lookup_glyph_6 (character,
+                                 (struct sfnt_cmap_format_6 *) data);
+
+    case 8:
+      return sfnt_lookup_glyph_8 (character,
+                                 (struct sfnt_cmap_format_8 *) data);
+
+    case 12:
+      return sfnt_lookup_glyph_12 (character,
+                                  (struct sfnt_cmap_format_12 *) data);
+    }
+
+  return 0;
+}
+
+
+
+/* Header reading routines.  */
+
+/* Read the head table of a given font FD.  Use the table directory
+   specified in SUBTABLE.
+
+   Return the head table upon success, else NULL.  */
+
+TEST_STATIC struct sfnt_head_table *
+sfnt_read_head_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+  struct sfnt_table_directory *directory;
+  struct sfnt_head_table *head;
+  ssize_t rc;
+
+  /* Find the table in the directory.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_HEAD);
+
+  if (!directory)
+    return NULL;
+
+  /* Seek to the location given in the directory.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  /* Read the entire table.  */
+  head = xmalloc (sizeof *head);
+  rc = read (fd, head, sizeof *head);
+
+  if (rc < sizeof *head)
+    {
+      xfree (head);
+      return NULL;
+    }
+
+  /* Swap the header data.  */
+  sfnt_swap32 (&head->version);
+  sfnt_swap32 (&head->revision);
+
+  if (head->version != 0x00010000)
+    {
+      xfree (head);
+      return NULL;
+    }
+
+  /* Swap the rest of the data.  */
+  sfnt_swap32 (&head->checksum_adjustment);
+  sfnt_swap32 (&head->magic);
+
+  if (head->magic != 0x5f0f3cf5)
+    {
+      xfree (head);
+      return NULL;
+    }
+
+  sfnt_swap16 (&head->flags);
+  sfnt_swap16 (&head->units_per_em);
+  sfnt_swap32 (&head->created_high);
+  sfnt_swap32 (&head->created_low);
+  sfnt_swap32 (&head->modified_high);
+  sfnt_swap32 (&head->modified_low);
+  sfnt_swap16 (&head->xmin);
+  sfnt_swap16 (&head->xmax);
+  sfnt_swap16 (&head->ymin);
+  sfnt_swap16 (&head->ymax);
+  sfnt_swap16 (&head->mac_style);
+  sfnt_swap16 (&head->lowest_rec_ppem);
+  sfnt_swap16 (&head->font_direction_hint);
+  sfnt_swap16 (&head->index_to_loc_format);
+  sfnt_swap16 (&head->glyph_data_format);
+
+  return head;
+}
+
+/* Read the hhea table of a given font FD.  Use the table directory
+   specified in SUBTABLE.
+
+   Return the head table upon success, else NULL.  */
+
+TEST_STATIC struct sfnt_hhea_table *
+sfnt_read_hhea_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+  struct sfnt_table_directory *directory;
+  struct sfnt_hhea_table *hhea;
+  ssize_t rc;
+
+  /* Find the table in the directory.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_HHEA);
+
+  if (!directory)
+    return NULL;
+
+  /* Check the length is right.  */
+  if (directory->length != sizeof *hhea)
+    return NULL;
+
+  /* Seek to the location given in the directory.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  /* Read the entire table.  */
+  hhea = xmalloc (sizeof *hhea);
+  rc = read (fd, hhea, sizeof *hhea);
+
+  if (rc < sizeof *hhea)
+    {
+      xfree (hhea);
+      return NULL;
+    }
+
+  /* Swap the header data.  */
+  sfnt_swap32 (&hhea->version);
+
+  if (hhea->version != 0x00010000)
+    {
+      xfree (hhea);
+      return NULL;
+    }
+
+  /* Swap the rest of the data.  */
+  sfnt_swap16 (&hhea->ascent);
+  sfnt_swap16 (&hhea->descent);
+  sfnt_swap16 (&hhea->line_gap);
+  sfnt_swap16 (&hhea->advance_width_max);
+  sfnt_swap16 (&hhea->min_left_side_bearing);
+  sfnt_swap16 (&hhea->min_right_side_bearing);
+  sfnt_swap16 (&hhea->x_max_extent);
+  sfnt_swap16 (&hhea->caret_slope_rise);
+  sfnt_swap16 (&hhea->caret_slope_run);
+  sfnt_swap16 (&hhea->reserved1);
+  sfnt_swap16 (&hhea->reserved2);
+  sfnt_swap16 (&hhea->reserved3);
+  sfnt_swap16 (&hhea->reserved4);
+  sfnt_swap16 (&hhea->metric_data_format);
+  sfnt_swap16 (&hhea->num_of_long_hor_metrics);
+
+  return hhea;
+}
+
+/* Read a short loca table from the given font FD.  Use the table
+   directory specified in SUBTABLE.
+
+   Return the short table upon success, else NULL.  */
+
+TEST_STATIC struct sfnt_loca_table_short *
+sfnt_read_loca_table_short (int fd, struct sfnt_offset_subtable *subtable)
+{
+  struct sfnt_table_directory *directory;
+  struct sfnt_loca_table_short *loca;
+  ssize_t rc;
+  int i;
+
+  /* Find the table in the directory.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_LOCA);
+
+  if (!directory)
+    return NULL;
+
+  /* Seek to the location given in the directory.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  /* Figure out how many glyphs there are based on the length.  */
+  loca = xmalloc (sizeof *loca + directory->length);
+  loca->offsets = (uint16_t *) (loca + 1);
+  loca->num_offsets = directory->length / 2;
+
+  /* Read the variable-length table data.  */
+  rc = read (fd, loca->offsets, directory->length);
+  if (rc < directory->length)
+    {
+      xfree (loca);
+      return NULL;
+    }
+
+  /* Swap each of the offsets.  */
+  for (i = 0; i < loca->num_offsets; ++i)
+    sfnt_swap16 (&loca->offsets[i]);
+
+  /* Return the table.  */
+  return loca;
+}
+
+/* Read a long loca table from the given font FD.  Use the table
+   directory specified in SUBTABLE.
+
+   Return the long table upon success, else NULL.  */
+
+TEST_STATIC struct sfnt_loca_table_long *
+sfnt_read_loca_table_long (int fd, struct sfnt_offset_subtable *subtable)
+{
+  struct sfnt_table_directory *directory;
+  struct sfnt_loca_table_long *loca;
+  ssize_t rc;
+  int i;
+
+  /* Find the table in the directory.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_LOCA);
+
+  if (!directory)
+    return NULL;
+
+  /* Seek to the location given in the directory.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  /* Figure out how many glyphs there are based on the length.  */
+  loca = xmalloc (sizeof *loca + directory->length);
+  loca->offsets = (uint32_t *) (loca + 1);
+  loca->num_offsets = directory->length / 4;
+
+  /* Read the variable-length table data.  */
+  rc = read (fd, loca->offsets, directory->length);
+  if (rc < directory->length)
+    {
+      xfree (loca);
+      return NULL;
+    }
+
+  /* Swap each of the offsets.  */
+  for (i = 0; i < loca->num_offsets; ++i)
+    sfnt_swap32 (&loca->offsets[i]);
+
+  /* Return the table.  */
+  return loca;
+}
+
+/* Read the maxp table from the given font FD.  Use the table
+   directory specified in SUBTABLE.
+
+   Return the maxp table upon success, else NULL.  If the version is
+   0.5, fields past num_glyphs will not be populated.  */
+
+TEST_STATIC struct sfnt_maxp_table *
+sfnt_read_maxp_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+  struct sfnt_table_directory *directory;
+  struct sfnt_maxp_table *maxp;
+  size_t size;
+  ssize_t rc;
+
+  /* Find the table in the directory.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_MAXP);
+
+  if (!directory)
+    return NULL;
+
+  /* Seek to the location given in the directory.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  /* If directory->length is not big enough for version 0.5, punt.  */
+  if (directory->length < SFNT_ENDOF (struct sfnt_maxp_table,
+                                     num_glyphs, uint16_t))
+    return NULL;
+
+  /* Allocate the buffer to hold the data.  Then, read
+     directory->length or sizeof *maxp bytes into it, whichever is
+     smaller.  */
+
+  maxp = xmalloc (sizeof *maxp);
+  size = MIN (directory->length, sizeof *maxp);
+  rc = read (fd, maxp, size);
+
+  if (rc < size)
+    {
+      xfree (maxp);
+      return NULL;
+    }
+
+  /* Now, swap version and num_glyphs.  */
+  sfnt_swap32 (&maxp->version);
+  sfnt_swap16 (&maxp->num_glyphs);
+
+  /* Reject version 1.0 tables that are too small.  */
+  if (maxp->version > 0x00005000 && size < sizeof *maxp)
+    {
+      xfree (maxp);
+      return NULL;
+    }
+
+  /* If the table is version 0.5, then this function is done.  */
+  if (maxp->version == 0x00005000)
+    return maxp;
+  else if (maxp->version != 0x00010000)
+    {
+      /* Reject invalid versions.  */
+      xfree (maxp);
+      return NULL;
+    }
+
+  /* Otherwise, swap the rest of the fields.  */
+  sfnt_swap16 (&maxp->max_points);
+  sfnt_swap16 (&maxp->max_contours);
+  sfnt_swap16 (&maxp->max_composite_points);
+  sfnt_swap16 (&maxp->max_composite_contours);
+  sfnt_swap16 (&maxp->max_zones);
+  sfnt_swap16 (&maxp->max_twilight_points);
+  sfnt_swap16 (&maxp->max_storage);
+  sfnt_swap16 (&maxp->max_function_defs);
+  sfnt_swap16 (&maxp->max_instruction_defs);
+  sfnt_swap16 (&maxp->max_stack_elements);
+  sfnt_swap16 (&maxp->max_size_of_instructions);
+  sfnt_swap16 (&maxp->max_component_elements);
+  sfnt_swap16 (&maxp->max_component_depth);
+
+  /* All done.  */
+  return maxp;
+}
+
+
+
+/* Glyph outlining generation.  */
+
+/* Read a glyf table from the given font FD.  Use the table directory
+   specified in SUBTABLE.  The glyph data is not swapped.
+
+   Return the glyf table upon success, else NULL.  */
+
+TEST_STATIC struct sfnt_glyf_table *
+sfnt_read_glyf_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+  struct sfnt_table_directory *directory;
+  struct sfnt_glyf_table *glyf;
+  ssize_t rc;
+
+  /* Find the table in the directory.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_GLYF);
+
+  if (!directory)
+    return NULL;
+
+  /* Seek to the location given in the directory.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  /* Allocate enough to hold everything.  */
+  glyf = xmalloc (sizeof *glyf + directory->length);
+  glyf->size = directory->length;
+  glyf->glyphs = (unsigned char *) (glyf + 1);
+
+  /* Read the glyph data.  */
+  rc = read (fd, glyf->glyphs, glyf->size);
+  if (rc < glyf->size)
+    {
+      xfree (glyf);
+      return NULL;
+    }
+
+  /* Return the table.  */
+  return glyf;
+}
+
+#if defined HAVE_MMAP && !defined TEST
+
+/* Map a glyph table from the given font FD.  Use the table directory
+   specified in SUBTABLE.  The glyph data is not byte-swapped.
+
+   Value is the glyf table upon success, else NULL.
+   A mapped glyf table must be unmapped using `sfnt_unmap_glyf_table'.
+   The caller must correctly handle bus errors in between glyf->table
+   and glyf->size.  */
+
+struct sfnt_glyf_table *
+sfnt_map_glyf_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+  struct sfnt_table_directory *directory;
+  struct sfnt_glyf_table *glyf;
+  void *glyphs;
+  size_t offset, page, map_offset;
+
+  /* Find the table in the directory.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_GLYF);
+
+  if (!directory)
+    return NULL;
+
+  /* Now try to map the glyph data.  Make sure offset is a multiple of
+     the page size.  */
+
+  page = getpagesize ();
+  offset = directory->offset & ~(page - 1);
+
+  /* Figure out how much larger the mapping should be.  */
+  map_offset = directory->offset - offset;
+
+  /* Do the mmap.  */
+  glyphs = mmap (NULL, directory->length + map_offset,
+                PROT_READ, MAP_PRIVATE, fd, offset);
+
+  if (glyphs == MAP_FAILED)
+    return NULL;
+
+  /* An observation is that glyphs tend to be accessed in sequential
+     order and immediately after the font's glyph table is loaded.  */
+
+#ifdef HAVE_POSIX_MADVISE
+  posix_madvise (glyphs, directory->length,
+                POSIX_MADV_WILLNEED);
+#elif defined HAVE_MADVISE
+  madvise (glyphs, directory->length, MADV_WILLNEED);
+#endif
+
+  /* Allocate the glyf table.  */
+  glyf = xmalloc (sizeof *glyf);
+  glyf->size = directory->length;
+  glyf->glyphs = (unsigned char *) glyphs + map_offset;
+  glyf->start = glyphs;
+
+  return glyf;
+}
+
+/* Unmap the mmap'ed glyf table GLYF, then free its associated data.
+   Value is 0 upon success, else 1, in which case GLYF is still freed
+   all the same.  */
+
+int
+sfnt_unmap_glyf_table (struct sfnt_glyf_table *glyf)
+{
+  int rc;
+  size_t size;
+
+  /* Calculate the size of the mapping.  */
+  size = glyf->size + (glyf->glyphs - glyf->start);
+
+  rc = munmap (glyf->start, size);
+  xfree (glyf);
+
+  return rc != 0;
+}
+
+#endif /* HAVE_MMAP */
+
+/* Read the simple glyph outline from the glyph GLYPH from the
+   specified glyf table at the given offset.  Set GLYPH->simple to a
+   non-NULL value upon success, else set it to NULL.  */
+
+static void
+sfnt_read_simple_glyph (struct sfnt_glyph *glyph,
+                       struct sfnt_glyf_table *glyf,
+                       size_t offset)
+{
+  struct sfnt_simple_glyph *simple;
+  ssize_t min_size, min_size_2;
+  int i, number_of_points, repeat_count;
+  unsigned char *instructions_start;
+  unsigned char *flags_start, *flags_end;
+  unsigned char *vec_start;
+  int16_t delta, x, y;
+
+  /* Calculate the minimum size of the glyph data.  This is the size
+     of the instruction length field followed by
+     glyph->number_of_contours * sizeof (uint16_t).  */
+
+  min_size = (glyph->number_of_contours * sizeof (uint16_t)
+             + sizeof (uint16_t));
+
+  /* Check that the size is big enough.  */
+  if (glyf->size < offset + min_size)
+    {
+      glyph->simple = NULL;
+      return;
+    }
+
+  /* Allocate enough to read at least that.  */
+  simple = xmalloc (sizeof *simple + min_size);
+  simple->end_pts_of_contours = (uint16_t *) (simple + 1);
+  memcpy (simple->end_pts_of_contours, glyf->glyphs + offset,
+         min_size);
+
+  /* This is not really an index into simple->end_pts_of_contours.
+     Rather, it is reading the first word past it.  */
+  simple->instruction_length
+    = simple->end_pts_of_contours[glyph->number_of_contours];
+
+  /* Swap the contour end point indices and the instruction
+     length.  */
+
+  for (i = 0; i < glyph->number_of_contours; ++i)
+    sfnt_swap16 (&simple->end_pts_of_contours[i]);
+
+  sfnt_swap16 (&simple->instruction_length);
+
+  /* Based on those values, calculate the maximum size of the
+     following data.  This is the instruction length + the last
+     contour point + the last contour point * uint16_t * 2.  */
+
+  if (glyph->number_of_contours)
+    number_of_points
+      = simple->end_pts_of_contours[glyph->number_of_contours - 1] + 1;
+  else
+    number_of_points = 0;
+
+  min_size_2 = (simple->instruction_length
+               + number_of_points
+               + (number_of_points
+                  * sizeof (uint16_t) * 2));
+
+  /* Set simple->number_of_points.  */
+  simple->number_of_points = number_of_points;
+
+  /* Make simple big enough.  */
+  simple = xrealloc (simple, sizeof *simple + min_size + min_size_2);
+  simple->end_pts_of_contours = (uint16_t *) (simple + 1);
+
+  /* Set the instruction data pointer and other pointers.
+     simple->instructions comes one word past number_of_contours,
+     because end_pts_of_contours also contains the instruction
+     length.  */
+  simple->instructions = (uint8_t *) (simple->end_pts_of_contours
+                                     + glyph->number_of_contours + 1);
+  simple->flags = simple->instructions + simple->instruction_length;
+
+  /* Read instructions into the glyph.  */
+  instructions_start = glyf->glyphs + offset + min_size;
+
+  if (instructions_start >= glyf->glyphs + glyf->size
+      || (instructions_start + simple->instruction_length
+         >= glyf->glyphs + glyf->size))
+    {
+      glyph->simple = NULL;
+      xfree (simple);
+      return;
+    }
+
+  memcpy (simple->instructions, instructions_start,
+         simple->instruction_length);
+
+  /* Start reading flags.  */
+  flags_start = (glyf->glyphs + offset
+                + min_size + simple->instruction_length);
+  flags_end = flags_start + number_of_points;
+
+  if (flags_start >= glyf->glyphs + glyf->size)
+    {
+      glyph->simple = NULL;
+      xfree (simple);
+      return;
+    }
+
+  i = 0;
+
+  while (flags_start < flags_end)
+    {
+      if (i == number_of_points)
+       break;
+
+      if (flags_start >= glyf->glyphs + glyf->size)
+       break;
+
+      simple->flags[i++] = *flags_start;
+
+      if (*flags_start & 010) /* REPEAT_FLAG */
+       {
+         /* The next byte specifies how many times this byte is to be
+            repeated.  Check that it is in range.  */
+
+         if (flags_start + 1 >= glyf->glyphs + glyf->size)
+           {
+             glyph->simple = NULL;
+             xfree (simple);
+             return;
+           }
+
+         /* Repeat the current flag until
+            glyph->number_of_points.  */
+
+         repeat_count = *(flags_start + 1);
+
+         while (i < number_of_points && repeat_count)
+           {
+             simple->flags[i++] = *flags_start;
+             repeat_count--;
+           }
+
+         /* Skip one byte in flags_start.  */
+         flags_start++;
+       }
+
+      flags_start++;
+    }
+
+  /* If an insufficient number of flags have been read, then the
+     outline is invalid.  */
+
+  if (i != number_of_points)
+    {
+      glyph->simple = NULL;
+      xfree (simple);
+      return;
+    }
+
+  /* Now that the flags have been decoded, start decoding the
+     vectors.  */
+  simple->x_coordinates = (int16_t *) (simple->flags + number_of_points);
+  vec_start = flags_start;
+  i = 0;
+  x = 0;
+
+  /* flags_start is now repurposed to act as a pointer to the flags
+     for the current vector! */
+  flags_start = simple->flags;
+
+  while (i < number_of_points)
+    {
+      delta = 0;
+
+      if ((*flags_start) & 02) /* X_SHORT_VECTOR */
+       {
+         /* The next byte is a delta to apply to the previous
+            value.  Make sure it is in bounds.  */
+
+         if (vec_start + 1 >= glyf->glyphs + glyf->size)
+           {
+             glyph->simple = NULL;
+             xfree (simple);
+             return;
+           }
+
+         delta = *vec_start++;
+
+         if (!(*flags_start & 020)) /* SAME_X */
+           delta = -delta;
+       }
+      else if (!(*flags_start & 020)) /* SAME_X */
+       {
+         /* The next word is a delta to apply to the previous value.
+            Make sure it is in bounds.  */
+
+         if (vec_start + 2 >= glyf->glyphs + glyf->size)
+           {
+             glyph->simple = NULL;
+             xfree (simple);
+             return;
+           }
+
+         /* Read the unaligned word and swap it.  */
+         memcpy (&delta, vec_start, sizeof delta);
+         sfnt_swap16 (&delta);
+         vec_start += 2;
+       }
+
+      /* Apply the delta and set the X value.  */
+      x += delta;
+      simple->x_coordinates[i++] = x;
+      flags_start++;
+    }
+
+  /* Decode the Y vector.  flags_start is again repurposed to act as a
+     pointer to the flags for the current vector.  */
+  flags_start = simple->flags;
+  y = 0;
+  simple->y_coordinates = simple->x_coordinates + i;
+  i = 0;
+
+  while (i < number_of_points)
+    {
+      delta = 0;
+
+      if (*flags_start & 04) /* Y_SHORT_VECTOR */
+       {
+         /* The next byte is a delta to apply to the previous
+            value.  Make sure it is in bounds.  */
+
+         if (vec_start + 1 >= glyf->glyphs + glyf->size)
+           {
+             glyph->simple = NULL;
+             xfree (simple);
+             return;
+           }
+
+         delta = *vec_start++;
+
+         if (!(*flags_start & 040)) /* SAME_Y */
+           delta = -delta;
+       }
+      else if (!(*flags_start & 040)) /* SAME_Y */
+       {
+         /* The next word is a delta to apply to the previous value.
+            Make sure it is in bounds.  */
+
+         if (vec_start + 2 >= glyf->glyphs + glyf->size)
+           {
+             glyph->simple = NULL;
+             xfree (simple);
+             return;
+           }
+
+         /* Read the unaligned word and swap it.  */
+         memcpy (&delta, vec_start, sizeof delta);
+         sfnt_swap16 (&delta);
+         vec_start += 2;
+       }
+
+      /* Apply the delta and set the X value.  */
+      y += delta;
+      simple->y_coordinates[i++] = y;
+      flags_start++;
+    }
+
+  /* All done.  */
+  simple->y_coordinates_end = simple->y_coordinates + i;
+  glyph->simple = simple;
+  return;
+}
+
+/* Read the compound glyph outline from the glyph GLYPH from the
+   specified glyf table at the given offset.  Set GLYPH->compound to a
+   non-NULL value upon success, else set it to NULL.  */
+
+static void
+sfnt_read_compound_glyph (struct sfnt_glyph *glyph,
+                         struct sfnt_glyf_table *glyf,
+                         size_t offset)
+{
+  uint16_t flags, instruction_length, words[2], words4[4];
+  size_t required_bytes, num_components, i;
+  unsigned char *data, *instruction_base;
+
+  /* Assume failure for now.  Figure out how many bytes have to be
+     allocated by reading the compound data.  */
+  glyph->compound = NULL;
+  required_bytes = 0;
+  num_components = 0;
+  data = glyf->glyphs + offset;
+
+  /* Offset could be unaligned.  */
+  do
+    {
+      if (data + 2 > glyf->glyphs + glyf->size)
+       return;
+
+      memcpy (&flags, data, sizeof flags);
+      sfnt_swap16 (&flags);
+      data += sizeof flags;
+
+      /* Require at least one structure to hold this data.  */
+      required_bytes += sizeof (struct sfnt_compound_glyph_component);
+      num_components++;
+
+      /* Skip past unused data.  */
+      data += 2;
+
+      if (flags & 01) /* ARG_1_AND_2_ARE_WORDS */
+       data += sizeof (int16_t) * 2;
+      else
+       data += sizeof (int8_t) * 2;
+
+      if (flags & 010) /* WE_HAVE_A_SCALE */
+       data += sizeof (uint16_t);
+      else if (flags & 0100) /* WE_HAVE_AN_X_AND_Y_SCALE */
+       data += sizeof (uint16_t) * 2;
+      else if (flags & 0200) /* WE_HAVE_A_TWO_BY_TWO */
+       data += sizeof (uint16_t) * 4;
+    }
+  while (flags & 040); /* MORE_COMPONENTS */
+
+  if (flags & 0400) /* WE_HAVE_INSTRUCTIONS */
+    {
+      /* Figure out the instruction length.  */
+      if (data + 2 > glyf->glyphs + glyf->size)
+       return;
+
+      /* Now see how much is required to hold the instruction
+        data.  */
+      memcpy (&instruction_length, data,
+             sizeof instruction_length);
+      sfnt_swap16 (&instruction_length);
+      required_bytes += instruction_length;
+      data += sizeof data + instruction_length;
+    }
+
+  /* Now allocate the buffer to hold all the glyph data.  */
+  glyph->compound = xmalloc (sizeof *glyph->compound
+                            + required_bytes);
+  glyph->compound->components
+    = (struct sfnt_compound_glyph_component *) (glyph->compound + 1);
+  glyph->compound->num_components = num_components;
+
+  /* Figure out where instruction data starts.  It comes after
+     glyph->compound->components ends.  */
+  instruction_base
+    = (unsigned char *) (glyph->compound->components
+                        + glyph->compound->num_components);
+
+  /* Start reading.  */
+  i = 0;
+  data = glyf->glyphs + offset;
+  do
+    {
+      if (data + 4 > glyf->glyphs + glyf->size)
+       {
+         xfree (glyph->compound);
+         glyph->compound = NULL;
+         return;
+       }
+
+      memcpy (&flags, data, sizeof flags);
+      sfnt_swap16 (&flags);
+      data += sizeof flags;
+      glyph->compound->components[i].flags = flags;
+
+      memcpy (&glyph->compound->components[i].glyph_index,
+             data, sizeof glyph->compound->components[i].glyph_index);
+      sfnt_swap16 (&glyph->compound->components[i].glyph_index);
+      data += sizeof glyph->compound->components[i].glyph_index;
+
+      if (flags & 01) /* ARG_1_AND_2_ARE_WORDS.  */
+       {
+         if (data + 4 > glyf->glyphs + glyf->size)
+           {
+             xfree (glyph->compound);
+             glyph->compound = NULL;
+             return;
+           }
+
+         /* Read two words into arg1 and arg2.  */
+         memcpy (words, data, sizeof words);
+         sfnt_swap16 (&words[0]);
+         sfnt_swap16 (&words[1]);
+
+         glyph->compound->components[i].argument1.c = words[0];
+         glyph->compound->components[i].argument2.c = words[1];
+         data += sizeof words;
+       }
+      else
+       {
+         if (data + 2 > glyf->glyphs + glyf->size)
+           {
+             xfree (glyph->compound);
+             glyph->compound = NULL;
+             return;
+           }
+
+         /* Read two bytes into arg1 and arg2.  */
+         glyph->compound->components[i].argument1.a = data[0];
+         glyph->compound->components[i].argument2.a = data[1];
+         data += 2;
+       }
+
+      if (flags & 010) /* WE_HAVE_A_SCALE */
+       {
+         if (data + 2 > glyf->glyphs + glyf->size)
+           {
+             xfree (glyph->compound);
+             glyph->compound = NULL;
+             return;
+           }
+
+         /* Read one word into scale.  */
+         memcpy (&glyph->compound->components[i].u.scale, data,
+                 sizeof glyph->compound->components[i].u.scale);
+         sfnt_swap16 (&glyph->compound->components[i].u.scale);
+         data += sizeof glyph->compound->components[i].u.scale;
+       }
+      else if (flags & 0100) /* WE_HAVE_AN_X_AND_Y_SCALE.  */
+       {
+         if (data + 4 > glyf->glyphs + glyf->size)
+           {
+             xfree (glyph->compound);
+             glyph->compound = NULL;
+             return;
+           }
+
+         /* Read two words into xscale and yscale.  */
+         memcpy (words, data, sizeof words);
+         sfnt_swap16 (&words[0]);
+         sfnt_swap16 (&words[1]);
+
+         glyph->compound->components[i].u.a.xscale = words[0];
+         glyph->compound->components[i].u.a.yscale = words[1];
+         data += sizeof words;
+       }
+      else if (flags & 0200) /* WE_HAVE_A_TWO_BY_TWO */
+       {
+         if (data + 8 > glyf->glyphs + glyf->size)
+           {
+             xfree (glyph->compound);
+             glyph->compound = NULL;
+             return;
+           }
+
+         /* Read 4 words into the transformation matrix.  */
+         memcpy (words4, data, sizeof words4);
+         sfnt_swap16 (&words4[0]);
+         sfnt_swap16 (&words4[1]);
+         sfnt_swap16 (&words4[2]);
+         sfnt_swap16 (&words4[3]);
+
+         glyph->compound->components[i].u.b.xscale = words4[0];
+         glyph->compound->components[i].u.b.scale01 = words4[1];
+         glyph->compound->components[i].u.b.scale10 = words4[2];
+         glyph->compound->components[i].u.b.yscale = words4[3];
+         data += sizeof words4;
+       }
+
+      /* Record the component flags.  */
+      glyph->compound->components[i].flags = flags;
+
+      i++;
+    }
+  while (flags & 040); /* MORE_COMPONENTS */
+
+  if (flags & 0400) /* WE_HAVE_INSTR */
+    {
+      /* Figure out the instruction length.  */
+      if (data + 2 > glyf->glyphs + glyf->size)
+       {
+         xfree (glyph->compound);
+         glyph->compound = NULL;
+         return;
+       }
+
+      /* Now see how much is required to hold the instruction
+        data.  */
+      memcpy (&glyph->compound->instruction_length,
+             data,
+             sizeof glyph->compound->instruction_length);
+      sfnt_swap16 (&glyph->compound->instruction_length);
+      data += 2;
+
+      /* Read the instructions.  */
+      glyph->compound->instructions = instruction_base;
+
+      if (data + glyph->compound->instruction_length
+         > glyf->glyphs + glyf->size)
+       {
+         xfree (glyph->compound);
+         glyph->compound = NULL;
+         return;
+       }
+
+      memcpy (instruction_base, data,
+             glyph->compound->instruction_length);
+    }
+  else
+    {
+      glyph->compound->instructions = NULL;
+      glyph->compound->instruction_length = 0;
+    }
+
+  /* Data read successfully.  */
+  return;
+}
+
+/* Read the description of the glyph GLYPH_CODE from the specified
+   glyf table, using the offsets of LOCA_SHORT or LOCA_LONG, depending
+   on which is non-NULL.  */
+
+TEST_STATIC struct sfnt_glyph *
+sfnt_read_glyph (sfnt_glyph glyph_code,
+                struct sfnt_glyf_table *glyf,
+                struct sfnt_loca_table_short *loca_short,
+                struct sfnt_loca_table_long *loca_long)
+{
+  struct sfnt_glyph glyph, *memory;
+  size_t offset, next_offset;
+
+  /* Check the glyph code is within bounds.  */
+  if (glyph_code > 65535)
+    return NULL;
+
+  if (loca_short)
+    {
+      /* Check that the glyph is within bounds.  glyph_code + 1 is the
+        entry in the table which defines the length of the glyph.  */
+      if (glyph_code + 1 >= loca_short->num_offsets)
+       return NULL;
+
+      offset = loca_short->offsets[glyph_code] * 2;
+      next_offset = loca_short->offsets[glyph_code + 1] * 2;
+    }
+  else if (loca_long)
+    {
+      if (glyph_code + 1 >= loca_long->num_offsets)
+       return NULL;
+
+      offset = loca_long->offsets[glyph_code];
+      next_offset = loca_long->offsets[glyph_code + 1];
+    }
+  else
+    abort ();
+
+  /* If offset - next_offset is 0, then the glyph is empty.  Its
+     horizontal advance may still be provided by the hmtx table.  */
+
+  if (offset == next_offset)
+    {
+      glyph.number_of_contours = 0;
+      glyph.xmin = 0;
+      glyph.ymin = 0;
+      glyph.xmax = 0;
+      glyph.ymax = 0;
+      glyph.simple = xmalloc (sizeof *glyph.simple);
+      glyph.compound = NULL;
+      memset (glyph.simple, 0, sizeof *glyph.simple);
+      memory = xmalloc (sizeof *memory);
+      *memory = glyph;
+      return memory;
+    }
+
+  /* Verify that GLYF is big enough to hold a glyph at OFFSET.  */
+  if (glyf->size < offset + SFNT_ENDOF (struct sfnt_glyph,
+                                       ymax, sfnt_fword))
+    return NULL;
+
+  /* Copy over the glyph data.  */
+  memcpy (&glyph, glyf->glyphs + offset,
+         SFNT_ENDOF (struct sfnt_glyph,
+                     ymax, sfnt_fword));
+
+  /* Swap the glyph data.  */
+  sfnt_swap16 (&glyph.number_of_contours);
+  sfnt_swap16 (&glyph.xmin);
+  sfnt_swap16 (&glyph.ymin);
+  sfnt_swap16 (&glyph.xmax);
+  sfnt_swap16 (&glyph.ymax);
+
+  /* This is set later on after `sfnt_vary_X_glyph'.  */
+  glyph.advance_distortion = 0;
+  glyph.origin_distortion = 0;
+
+  /* Figure out what needs to be read based on
+     glyph.number_of_contours.  */
+  if (glyph.number_of_contours >= 0)
+    {
+      /* Read the simple glyph.  */
+
+      glyph.compound = NULL;
+      sfnt_read_simple_glyph (&glyph, glyf,
+                             offset + SFNT_ENDOF (struct sfnt_glyph,
+                                                  ymax, sfnt_fword));
+
+      if (glyph.simple)
+       {
+         memory = xmalloc (sizeof glyph);
+         *memory = glyph;
+
+         return memory;
+       }
+    }
+  else
+    {
+      /* Read the compound glyph.  */
+
+      glyph.simple = NULL;
+      sfnt_read_compound_glyph (&glyph, glyf,
+                               offset + SFNT_ENDOF (struct sfnt_glyph,
+                                                    ymax, sfnt_fword));
+
+      if (glyph.compound)
+       {
+         memory = xmalloc (sizeof glyph);
+         *memory = glyph;
+
+         return memory;
+       }
+    }
+
+  return NULL;
+}
+
+/* Free a glyph returned from sfnt_read_glyph.  GLYPH may be NULL.  */
+
+TEST_STATIC void
+sfnt_free_glyph (struct sfnt_glyph *glyph)
+{
+  if (!glyph)
+    return;
+
+  xfree (glyph->simple);
+  xfree (glyph->compound);
+  xfree (glyph);
+}
+
+
+
+/* Glyph outline decomposition.  */
+
+/* Apply the transform in the compound glyph component COMPONENT to
+   the array of points of length NUM_COORDINATES given as X and Y.
+
+   Also, apply the fixed point offsets X_OFF and Y_OFF to each X and Y
+   coordinate.
+
+   See sfnt_decompose_compound_glyph for an explanation of why offsets
+   might be applied here, and not while reading the subglyph
+   itself.  */
+
+static void
+sfnt_transform_coordinates (struct sfnt_compound_glyph_component *component,
+                           sfnt_fixed *restrict x, sfnt_fixed *restrict y,
+                           size_t num_coordinates,
+                           sfnt_fixed x_off, sfnt_fixed y_off)
+{
+  double m1, m2, m3;
+  double m4, m5, m6;
+  size_t i;
+
+  if (component->flags & 010) /* WE_HAVE_A_SCALE */
+    {
+      for (i = 0; i < num_coordinates; ++i)
+       {
+         x[i] *= component->u.scale / 16384.0;
+         y[i] *= component->u.scale / 16384.0;
+         x[i] += x_off;
+         y[i] += y_off;
+       }
+    }
+  else if (component->flags & 0100) /* WE_HAVE_AN_X_AND_Y_SCALE */
+    {
+      for (i = 0; i < num_coordinates; ++i)
+       {
+         x[i] *= component->u.a.xscale / 16384.0;
+         y[i] *= component->u.a.yscale / 16384.0;
+         x[i] += x_off;
+         y[i] += y_off;
+       }
+    }
+  else if (component->flags & 0200) /* WE_HAVE_A_TWO_BY_TWO */
+    {
+      /* Apply the specified affine transformation.
+        A transform looks like:
+
+          M1 M2 M3     X
+          M4 M5 M6   * Y
+
+          =
+
+          M1*X + M2*Y + M3*1 = X1
+          M4*X + M5*Y + M6*1 = Y1
+
+        (In most transforms, there is another row at the bottom for
+         mathematical reasons.  Since Z1 is always 1.0, the row is
+         simply implied to be 0 0 1, because 0 * x + 0 * y + 1 * 1 =
+         1.0.  See the definition of matrix3x3 in image.c for some
+         more explanations about this.) */
+      m1 = component->u.b.xscale / 16384.0;
+      m2 = component->u.b.scale01 / 16384.0;
+      m3 = 0;
+      m4 = component->u.b.scale10 / 16384.0;
+      m5 = component->u.b.yscale / 16384.0;
+      m6 = 0;
+
+      for (i = 0; i < num_coordinates; ++i)
+       {
+         x[i] = m1 * x[i] + m2 * y[i] + m3 * 1;
+         y[i] = m4 * x[i] + m5 * y[i] + m6 * 1;
+         x[i] += x_off;
+         y[i] += y_off;
+       }
+    }
+}
+
+struct sfnt_compound_glyph_context
+{
+  /* Arrays of points.  The underlying type is actually sfnt_f26dot6
+     when instructing a compound glyph.  */
+  sfnt_fixed *x_coordinates, *y_coordinates;
+
+  /* Array of flags for the points.  */
+  unsigned char *flags;
+
+  /* Number of points in that array, and the size of that array.  */
+  size_t num_points, points_size;
+
+  /* Array of contour end points.  */
+  size_t *contour_end_points;
+
+  /* Number of elements in and the size of that array.  */
+  size_t num_end_points, end_points_size;
+};
+
+/* Extend the arrays inside the compound glyph decomposition context
+   CONTEXT.  NUMBER_OF_CONTOURS is the number of contours to add.
+   NUMBER_OF_POINTS is the number of points to add.
+
+   Return pointers to the beginning of the extension in *X_BASE,
+   *Y_BASE, *FLAGS_BASE and *CONTOUR_BASE.  Value zero upon success,
+   and something else on failure.  */
+
+static int
+sfnt_expand_compound_glyph_context (struct sfnt_compound_glyph_context 
*context,
+                                   size_t number_of_contours,
+                                   size_t number_of_points,
+                                   sfnt_fixed **x_base, sfnt_fixed **y_base,
+                                   unsigned char **flags_base,
+                                   size_t **contour_base)
+{
+  size_t size_bytes;
+
+  /* Add each field while checking for overflow.  */
+  if (INT_ADD_WRAPV (number_of_contours, context->num_end_points,
+                    &context->num_end_points))
+    return 1;
+
+  if (INT_ADD_WRAPV (number_of_points, context->num_points,
+                    &context->num_points))
+    return 1;
+
+  /* Reallocate each array to the new size if necessary.  */
+  if (context->points_size < context->num_points)
+    {
+      if (INT_MULTIPLY_WRAPV (context->num_points, 2,
+                             &context->points_size))
+       context->points_size = context->num_points;
+
+      if (INT_MULTIPLY_WRAPV (context->points_size,
+                             sizeof *context->x_coordinates,
+                             &size_bytes))
+       return 1;
+
+      context->x_coordinates = xrealloc (context->x_coordinates,
+                                        size_bytes);
+      context->y_coordinates = xrealloc (context->y_coordinates,
+                                        size_bytes);
+      context->flags = xrealloc (context->flags,
+                                context->points_size);
+    }
+
+  /* Set x_base and y_base.  */
+  *x_base = (context->x_coordinates
+            + context->num_points
+            - number_of_points);
+  *y_base = (context->y_coordinates
+            + context->num_points
+            - number_of_points);
+  *flags_base = (context->flags
+                + context->num_points
+                - number_of_points);
+
+  if (context->end_points_size < context->num_end_points)
+    {
+      if (INT_MULTIPLY_WRAPV (context->num_end_points, 2,
+                             &context->end_points_size))
+       context->end_points_size = context->num_end_points;
+
+      if (INT_MULTIPLY_WRAPV (context->end_points_size,
+                             sizeof *context->contour_end_points,
+                             &size_bytes))
+       return 1;
+
+      context->contour_end_points
+       = xrealloc (context->contour_end_points,
+                   size_bytes);
+    }
+
+  /* Set contour_base.  */
+  *contour_base = (context->contour_end_points
+                  + context->num_end_points
+                  - number_of_contours);
+  return 0;
+}
+
+/* Round the 16.16 fixed point number NUMBER to the nearest integral
+   value.  */
+
+static int32_t
+sfnt_round_fixed (int32_t number)
+{
+  /* Add 0.5... */
+  number += (1 << 15);
+
+  /* Remove the fractional.  */
+  return number & ~0xffff;
+}
+
+/* Decompose GLYPH, a compound glyph, into an array of points and
+   contours.
+
+   CONTEXT should be zeroed and put on the stack.  OFF_X and OFF_Y
+   should be zero, as should RECURSION_COUNT.  GET_GLYPH and
+   FREE_GLYPH, along with DCONTEXT, mean the same as in
+   sfnt_decompose_glyph.
+
+   Value is 1 upon failure, else 0.  */
+
+static int
+sfnt_decompose_compound_glyph (struct sfnt_glyph *glyph,
+                              struct sfnt_compound_glyph_context *context,
+                              sfnt_get_glyph_proc get_glyph,
+                              sfnt_free_glyph_proc free_glyph,
+                              sfnt_fixed off_x, sfnt_fixed off_y,
+                              int recursion_count,
+                              void *dcontext)
+{
+  struct sfnt_glyph *subglyph;
+  int i, j, rc;
+  bool need_free;
+  struct sfnt_compound_glyph_component *component;
+  sfnt_fixed x, y, xtemp, ytemp;
+  size_t point UNINIT, point2 UNINIT, index;
+  uint16_t last_point, number_of_contours;
+  sfnt_fixed *x_base, *y_base;
+  size_t *contour_base;
+  unsigned char *flags_base;
+  size_t base_index, contour_start;
+  bool defer_offsets;
+
+  /* Set up the base index.  This is the index from where on point
+     renumbering starts.
+
+     In other words, point 0 in this glyph will be 0 + base_index,
+     point 1 will be 1 + base_index, and so on.  */
+  base_index = context->num_points;
+
+  /* Prevent infinite loops.  Simply limit the level of nesting to the
+     maximum valid value of `max_component_depth', which is 16.  */
+
+  if (recursion_count > 16)
+    return 1;
+
+  for (j = 0; j < glyph->compound->num_components; ++j)
+    {
+      /* Look up the associated subglyph.  */
+      component = &glyph->compound->components[j];
+      subglyph = get_glyph (component->glyph_index,
+                           dcontext, &need_free);
+
+      if (!subglyph)
+       return 1;
+
+      /* Don't defer offsets.  This variable is set if the component
+        glyph is a compound glyph that is anchored to a previously
+        decomposed point, and needs its coordinates adjusted after
+        decomposition completes.  */
+      defer_offsets = false;
+
+      /* Record the size of the point array before expansion.  This
+        will be the base to apply to all points coming from this
+        subglyph.  */
+      contour_start = context->num_points;
+
+      /* Compute the offset for the component.  */
+      if (component->flags & 02) /* ARGS_ARE_XY_VALUES */
+       {
+         /* Component offsets are X/Y values as opposed to points
+            GLYPH.  */
+
+         if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */
+           {
+             /* X and Y are signed bytes.  */
+             x = component->argument1.b * 65536;
+             y = component->argument2.b * 65536;
+           }
+         else
+           {
+             /* X and Y are signed words.  */
+             x = component->argument1.d * 65536;
+             y = component->argument2.d * 65536;
+           }
+
+         /* If there is some kind of scale and component offsets are
+            scaled, then apply the transform to the offset.  */
+         if (component->flags & 04000) /* SCALED_COMPONENT_OFFSET */
+           sfnt_transform_coordinates (component, &x, &y, 1,
+                                       0, 0);
+
+         if (component->flags & 04) /* ROUND_XY_TO_GRID */
+           {
+             x = sfnt_round_fixed (x);
+             y = sfnt_round_fixed (y);
+           }
+       }
+      else
+       {
+         /* The offset is determined by matching a point location in
+            a preceeding component with a point location in the
+            current component.  The index of the point in the
+            previous component can be determined by adding
+            component->argument1.a or component->argument1.c to
+            point.  argument2 contains the index of the point in the
+            current component.  */
+
+         if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */
+           {
+             point = base_index + component->argument1.a;
+             point2 = component->argument2.a;
+           }
+         else
+           {
+             point = base_index + component->argument1.c;
+             point2 = component->argument2.c;
+           }
+
+         /* Now, check that the anchor point specified lies inside
+            the glyph.  */
+
+         if (point >= contour_start)
+           {
+             if (need_free)
+               free_glyph (subglyph, dcontext);
+
+             return 1;
+           }
+
+         if (!subglyph->compound)
+           {
+             if (point2 >= subglyph->simple->number_of_points)
+               {
+                 if (need_free)
+                   free_glyph (subglyph, dcontext);
+
+                 return 1;
+               }
+
+             /* Get the points and use them to compute the offsets.  */
+             xtemp = context->x_coordinates[point];
+             ytemp = context->y_coordinates[point];
+             x = (xtemp - subglyph->simple->x_coordinates[point2] * 65536);
+             y = (ytemp - subglyph->simple->y_coordinates[point2] * 65536);
+           }
+         else
+           {
+             /* First, set offsets to 0, because it is not yet
+                possible to determine the position of the anchor
+                point in the child.  */
+             x = 0;
+             y = 0;
+
+             /* Set a flag which indicates that offsets must be
+                resolved from the child glyph after it is loaded, but
+                before it is incorporated into the parent glyph.  */
+             defer_offsets = true;
+           }
+       }
+
+      if (subglyph->simple)
+       {
+         /* Simple subglyph.  Copy over the points and contours, and
+            transform them.  */
+         if (subglyph->number_of_contours)
+           {
+             index = subglyph->number_of_contours - 1;
+             last_point
+               = subglyph->simple->end_pts_of_contours[index];
+             number_of_contours = subglyph->number_of_contours;
+
+
+             /* Grow various arrays.  */
+             rc = sfnt_expand_compound_glyph_context (context,
+                                                      /* Number of
+                                                         new contours
+                                                         required.  */
+                                                      number_of_contours,
+                                                      /* Number of new
+                                                         points
+                                                         required.  */
+                                                      last_point + 1,
+                                                      &x_base,
+                                                      &y_base,
+                                                      &flags_base,
+                                                      &contour_base);
+             if (rc)
+               {
+                 if (need_free)
+                   free_glyph (subglyph, dcontext);
+
+                 return 1;
+               }
+
+             for (i = 0; i <= last_point; ++i)
+               {
+                 x_base[i] = ((subglyph->simple->x_coordinates[i] * 65536)
+                              + off_x + x);
+                 y_base[i] = ((subglyph->simple->y_coordinates[i] * 65536)
+                              + off_y + y);
+                 flags_base[i] = subglyph->simple->flags[i];
+               }
+
+             /* Apply the transform to the points.  */
+             sfnt_transform_coordinates (component, x_base, y_base,
+                                         last_point + 1, 0, 0);
+
+             /* Copy over the contours.  */
+             for (i = 0; i < number_of_contours; ++i)
+               contour_base[i] = (contour_start
+                                  + subglyph->simple->end_pts_of_contours[i]);
+           }
+       }
+      else
+       {
+         /* Compound subglyph.  Decompose the glyph recursively, and
+            then apply the transform.  */
+         rc = sfnt_decompose_compound_glyph (subglyph,
+                                             context,
+                                             get_glyph,
+                                             free_glyph,
+                                             off_x + x,
+                                             off_y + y,
+                                             recursion_count + 1,
+                                             dcontext);
+
+         if (rc)
+           {
+             if (need_free)
+               free_glyph (subglyph, dcontext);
+
+             return 1;
+           }
+
+         /* When an anchor point is being used to translate the
+            glyph, and the subglyph in question is actually a
+            compound glyph, it is impossible to know which offset to
+            use until the compound subglyph has actually been
+            loaded.
+
+            As a result, the offset is calculated here, using the
+            points in the loaded child compound glyph.  But first, X
+            and Y must be reset to 0, as otherwise the translation
+            might be applied twice if defer_offsets is not set.  */
+
+         x = 0;
+         y = 0;
+
+         if (defer_offsets)
+           {
+             /* Renumber the non renumbered point2 to point into the
+                decomposed component.  */
+             point2 += contour_start;
+
+             /* Next, check that the non-renumbered point being
+                anchored lies inside the glyph data that was
+                decomposed.  */
+
+             if (point2 >= context->num_points)
+               {
+                 if (need_free)
+                   free_glyph (subglyph, dcontext);
+
+                 return 1;
+               }
+
+             /* Get the points and use them to compute the
+                offsets.  */
+
+             xtemp = context->x_coordinates[point];
+             ytemp = context->y_coordinates[point];
+             x = (xtemp - context->x_coordinates[point2]);
+             y = (ytemp - context->y_coordinates[point2]);
+           }
+
+         sfnt_transform_coordinates (component,
+                                     context->x_coordinates + contour_start,
+                                     context->y_coordinates + contour_start,
+                                     contour_start - context->num_points,
+                                     x, y);
+       }
+
+      if (need_free)
+       free_glyph (subglyph, dcontext);
+    }
+
+  /* Decomposition is complete.  CONTEXT now contains the adjusted
+     outlines of the entire compound glyph.  */
+  return 0;
+}
+
+/* Linear-interpolate to a point halfway between the points specified
+   by CONTROL1 and CONTROL2.  Put the result in RESULT.  */
+
+static void
+sfnt_lerp_half (struct sfnt_point *control1, struct sfnt_point *control2,
+               struct sfnt_point *result)
+{
+  result->x = control1->x + ((control2->x - control1->x) / 2);
+  result->y = control1->y + ((control2->y - control1->y) / 2);
+}
+
+/* Decompose contour data inside X, Y and FLAGS, between the indices
+   HERE and LAST.  Call LINE_TO, CURVE_TO and MOVE_TO as appropriate,
+   with DCONTEXT as an argument.  Apply SCALE to each point; SCALE
+   should be the factor necessary to turn points into 16.16 fixed
+   point.
+
+   Value is 1 upon failure, else 0.  */
+
+static int
+sfnt_decompose_glyph_1 (size_t here, size_t last,
+                       sfnt_move_to_proc move_to,
+                       sfnt_line_to_proc line_to,
+                       sfnt_curve_to_proc curve_to,
+                       void *dcontext,
+                       sfnt_fword *x,
+                       sfnt_fword *y, unsigned char *flags,
+                       int scale)
+{
+  struct sfnt_point control1, control2, start, mid;
+  size_t i;
+
+  /* The contour is empty.  */
+
+  if (here == last)
+    return 1;
+
+  /* Move the pen to the start of the contour.  Apparently some fonts
+     have off the curve points as the start of a contour, so when that
+     happens lerp between the first and last points.  */
+
+  if (flags[here] & 01) /* On Curve */
+    {
+      control1.x = x[here] * scale;
+      control1.y = y[here] * scale;
+      start = control1;
+    }
+  else if (flags[last] & 01)
+    {
+      /* Start at the last point if it is on the curve.  Here, the
+         start really becomes the middle of a spline.  */
+      control1.x = x[last] * scale;
+      control1.y = y[last] * scale;
+      start = control1;
+
+      /* Curve back one point early.  */
+      last -= 1;
+      here -= 1;
+    }
+  else
+    {
+      /* Lerp between the start and the end.  */
+      control1.x = x[here] * scale;
+      control1.y = y[here] * scale;
+      control2.x = x[last] * scale;
+      control2.y = y[last] * scale;
+      sfnt_lerp_half (&control1, &control2, &start);
+
+      /* In either of these cases, start iterating from just here as
+        opposed to here + 1, since logically the contour now starts
+        from the last curve.  */
+      here -= 1;
+    }
+
+  /* Move to the start.  */
+  move_to (start, dcontext);
+
+  /* Now handle each point between here + 1 and last.  */
+
+  i = here;
+  while (++i <= last)
+    {
+      /* If the point is on the curve, then draw a line here from the
+        last control point.  */
+
+      if (flags[i] & 01)
+       {
+         control1.x = x[i] * scale;
+         control1.y = y[i] * scale;
+
+         line_to (control1, dcontext);
+
+         /* Move to the next point.  */
+         continue;
+       }
+
+      /* Off the curve points are more interesting.  They are handled
+        one by one, with points in between being interpolated, until
+        either the last point is reached or an on-curve point is
+        processed.  First, load the initial control points.  */
+
+      control1.x = x[i] * scale;
+      control1.y = y[i] * scale;
+
+      while (++i <= last)
+       {
+         /* Load this point.  */
+         control2.x = x[i] * scale;
+         control2.y = y[i] * scale;
+
+         /* If this point is on the curve, curve directly to this
+            point.  */
+
+         if (flags[i] & 01)
+           {
+             curve_to (control1, control2, dcontext);
+             goto continue_loop;
+           }
+
+         /* Calculate the point between here and the previous
+            point.  */
+         sfnt_lerp_half (&control1, &control2, &mid);
+
+         /* Curve over there.  */
+         curve_to (control1, mid, dcontext);
+
+         /* Reload the control point.  */
+         control1 = control2;
+       }
+
+      /* Close the contour by curving back to start.  */
+      curve_to (control1, start, dcontext);
+
+      /* Don't close the contour twice.  */
+      goto exit;
+
+    continue_loop:
+      continue;
+    }
+
+  /* Close the contour with a line back to start.  */
+  line_to (start, dcontext);
+
+ exit:
+  return 0;
+}
+
+/* Decompose contour data inside X, Y and FLAGS, between the indices
+   HERE and LAST.  Call LINE_TO, CURVE_TO and MOVE_TO as appropriate,
+   with DCONTEXT as an argument.  Apply SCALE to each point; SCALE
+   should be the factor necessary to turn points into 16.16 fixed
+   point.
+
+   This is the version of sfnt_decompose_glyph_1 which takes
+   sfnt_fixed (or sfnt_f26dot6) as opposed to sfnt_fword.
+
+   Value is 1 upon failure, else 0.  */
+
+static int
+sfnt_decompose_glyph_2 (size_t here, size_t last,
+                       sfnt_move_to_proc move_to,
+                       sfnt_line_to_proc line_to,
+                       sfnt_curve_to_proc curve_to,
+                       void *dcontext,
+                       sfnt_fixed *x,
+                       sfnt_fixed *y, unsigned char *flags,
+                       int scale)
+{
+  struct sfnt_point control1, control2, start, mid;
+  size_t i;
+
+  /* The contour is empty.  */
+
+  if (here == last)
+    return 1;
+
+  /* Move the pen to the start of the contour.  Apparently some fonts
+     have off the curve points as the start of a contour, so when that
+     happens lerp between the first and last points.  */
+
+  if (flags[here] & 01) /* On Curve */
+    {
+      control1.x = x[here] * scale;
+      control1.y = y[here] * scale;
+      start = control1;
+    }
+  else if (flags[last] & 01)
+    {
+      /* Start at the last point if it is on the curve.  Here, the
+         start really becomes the middle of a spline.  */
+      control1.x = x[last] * scale;
+      control1.y = y[last] * scale;
+      start = control1;
+
+      /* Curve back one point early.  */
+      last -= 1;
+      here -= 1;
+    }
+  else
+    {
+      /* Lerp between the start and the end.  */
+      control1.x = x[here] * scale;
+      control1.y = y[here] * scale;
+      control2.x = x[last] * scale;
+      control2.y = y[last] * scale;
+      sfnt_lerp_half (&control1, &control2, &start);
+
+      /* In either of these cases, start iterating from just here as
+        opposed to here + 1, since logically the contour now starts
+        from the last curve.  */
+      here -= 1;
+    }
+
+  /* Move to the start.  */
+  move_to (start, dcontext);
+
+  /* Now handle each point between here + 1 and last.  */
+
+  i = here;
+  while (++i <= last)
+    {
+      /* If the point is on the curve, then draw a line here from the
+        last control point.  */
+
+      if (flags[i] & 01)
+       {
+         control1.x = x[i] * scale;
+         control1.y = y[i] * scale;
+
+         line_to (control1, dcontext);
+
+         /* Move to the next point.  */
+         continue;
+       }
+
+      /* Off the curve points are more interesting.  They are handled
+        one by one, with points in between being interpolated, until
+        either the last point is reached or an on-curve point is
+        processed.  First, load the initial control points.  */
+
+      control1.x = x[i] * scale;
+      control1.y = y[i] * scale;
+
+      while (++i <= last)
+       {
+         /* Load this point.  */
+         control2.x = x[i] * scale;
+         control2.y = y[i] * scale;
+
+         /* If this point is on the curve, curve directly to this
+            point.  */
+
+         if (flags[i] & 01)
+           {
+             curve_to (control1, control2, dcontext);
+             goto continue_loop;
+           }
+
+         /* Calculate the point between here and the previous
+            point.  */
+         sfnt_lerp_half (&control1, &control2, &mid);
+
+         /* Curve over there.  */
+         curve_to (control1, mid, dcontext);
+
+         /* Reload the control point.  */
+         control1 = control2;
+       }
+
+      /* Close the contour by curving back to start.  */
+      curve_to (control1, start, dcontext);
+
+      /* Don't close the contour twice.  */
+      goto exit;
+
+    continue_loop:
+      continue;
+    }
+
+  /* Close the contour with a line back to start.  */
+  line_to (start, dcontext);
+
+ exit:
+  return 0;
+}
+
+/* Decompose GLYPH into its individual components.  Call MOVE_TO to
+   move to a specific location.  For each line encountered, call
+   LINE_TO to draw a line to that location.  For each spline
+   encountered, call CURVE_TO to draw the curves comprising the
+   spline.
+
+   If GLYPH is compound, use GET_GLYPH to obtain subglyphs.  PROC must
+   return whether or not FREE_GLYPH will be called with the glyph
+   after sfnt_decompose_glyph is done with it.
+
+   All functions will be called with DCONTEXT as an argument.
+
+   The winding rule used to fill the resulting lines is described in
+   chapter 2 of the TrueType reference manual, under the heading
+   "distinguishing the inside from the outside of a glyph."
+
+   Value is 0 upon success, or some non-zero value upon failure, which
+   can happen if the glyph is invalid.  */
+
+static int
+sfnt_decompose_glyph (struct sfnt_glyph *glyph,
+                     sfnt_move_to_proc move_to,
+                     sfnt_line_to_proc line_to,
+                     sfnt_curve_to_proc curve_to,
+                     sfnt_get_glyph_proc get_glyph,
+                     sfnt_free_glyph_proc free_glyph,
+                     void *dcontext)
+{
+  size_t here, last, n;
+  struct sfnt_compound_glyph_context context;
+
+  if (glyph->simple)
+    {
+      if (!glyph->number_of_contours)
+       /* No contours.  Nothing needs to be decomposed.  */
+       return 0;
+
+      here = 0;
+
+      for (n = 0; n < glyph->number_of_contours; ++n)
+       {
+         /* here is the first index into the glyph's point arrays
+            belonging to the contour in question.  last is the index
+            of the last point in the contour.  */
+         last = glyph->simple->end_pts_of_contours[n];
+
+         /* Make sure here and last make sense.  */
+
+         if (here > last || last >= glyph->simple->number_of_points)
+           return 1;
+
+         /* Now perform the decomposition.  */
+         if (sfnt_decompose_glyph_1 (here, last, move_to,
+                                     line_to, curve_to,
+                                     dcontext,
+                                     glyph->simple->x_coordinates,
+                                     glyph->simple->y_coordinates,
+                                     glyph->simple->flags,
+                                     65536))
+           return 1;
+
+         /* Move forward to the start of the next contour.  */
+         here = last + 1;
+       }
+
+      return 0;
+    }
+
+  /* Decompose the specified compound glyph.  */
+  memset (&context, 0, sizeof context);
+
+  if (sfnt_decompose_compound_glyph (glyph, &context,
+                                    get_glyph, free_glyph,
+                                    0, 0, 0, dcontext))
+    {
+      xfree (context.x_coordinates);
+      xfree (context.y_coordinates);
+      xfree (context.flags);
+      xfree (context.contour_end_points);
+
+      return 1;
+    }
+
+  /* Now, generate the outlines.  */
+
+  if (!context.num_end_points)
+    /* No contours.  */
+    goto early;
+
+  here = 0;
+
+  for (n = 0; n < context.num_end_points; ++n)
+    {
+      /* here is the first index into the glyph's point arrays
+        belonging to the contour in question.  last is the index
+        of the last point in the contour.  */
+      last = context.contour_end_points[n];
+
+      /* Make sure here and last make sense.  */
+
+      if (here > last || last >= context.num_points)
+       goto fail;
+
+      /* Now perform the decomposition.  */
+      if (sfnt_decompose_glyph_2 (here, last, move_to,
+                                 line_to, curve_to,
+                                 dcontext,
+                                 context.x_coordinates,
+                                 context.y_coordinates,
+                                 context.flags, 1))
+       goto fail;
+
+      /* Move forward.  */
+      here = last + 1;
+    }
+
+ early:
+  xfree (context.x_coordinates);
+  xfree (context.y_coordinates);
+  xfree (context.flags);
+  xfree (context.contour_end_points);
+  return 0;
+
+ fail:
+  xfree (context.x_coordinates);
+  xfree (context.y_coordinates);
+  xfree (context.flags);
+  xfree (context.contour_end_points);
+  return 1;
+}
+
+struct sfnt_build_glyph_outline_context
+{
+  /* The outline being built.  */
+  struct sfnt_glyph_outline *outline;
+
+  /* Factor to multiply positions by to get the pixel width.  */
+  sfnt_fixed factor;
+
+  /* The position of the pen in 16.16 fixed point format.  */
+  sfnt_fixed x, y;
+};
+
+/* Global state for sfnt_build_glyph_outline and related
+   functions.  */
+static struct sfnt_build_glyph_outline_context build_outline_context;
+
+/* Append the given three words FLAGS, X, and Y to the outline
+   currently being built.  Value is the new pointer to outline
+   memory.  */
+
+static struct sfnt_glyph_outline *
+sfnt_build_append (int flags, sfnt_fixed x, sfnt_fixed y)
+{
+  struct sfnt_glyph_outline *outline;
+
+  if (x == build_outline_context.x
+      && y == build_outline_context.y)
+    /* Ignore redundant motion.  */
+    return build_outline_context.outline;
+
+  outline = build_outline_context.outline;
+  outline->outline_used++;
+
+  /* See if the outline has to be extended.  Checking for overflow
+     should not be necessary.  */
+
+  if (outline->outline_used > outline->outline_size)
+    {
+      outline->outline_size = outline->outline_used * 2;
+
+      /* Extend the outline to some size past the new size.  */
+      outline = xrealloc (outline, (sizeof *outline
+                                   + (outline->outline_size
+                                      * sizeof *outline->outline)));
+      outline->outline
+       = (struct sfnt_glyph_outline_command *) (outline + 1);
+    }
+
+  /* Write the outline data.  */
+  outline->outline[outline->outline_used - 1].flags = flags;
+  outline->outline[outline->outline_used - 1].x = x;
+  outline->outline[outline->outline_used - 1].y = y;
+
+  /* Extend outline bounding box.  */
+
+  if (outline->outline_used == 1)
+    {
+      /* These are the first points in the outline.  */
+      outline->xmin = outline->xmax = x;
+      outline->ymin = outline->ymax = y;
+    }
+  else
+    {
+      outline->xmin = MIN ((sfnt_fixed) x, outline->xmin);
+      outline->ymin = MIN ((sfnt_fixed) y, outline->ymin);
+      outline->xmax = MAX ((sfnt_fixed) x, outline->xmax);
+      outline->ymax = MAX ((sfnt_fixed) y, outline->ymax);
+    }
+
+  return outline;
+}
+
+#ifndef INT64_MAX
+
+/* 64 bit integer type.  */
+
+struct sfnt_large_integer
+{
+  unsigned int high, low;
+};
+
+/* Calculate (A * B), placing the result in *VALUE.  */
+
+static void
+sfnt_multiply_divide_1 (unsigned int a, unsigned int b,
+                       struct sfnt_large_integer *value)
+{
+  unsigned int lo1, hi1, lo2, hi2, lo, hi, i1, i2;
+
+  lo1 = a & 0x0000ffffu;
+  hi1 = a >> 16;
+  lo2 = b & 0x0000ffffu;
+  hi2 = b >> 16;
+
+  lo = lo1 * lo2;
+  i1 = lo1 * hi2;
+  i2 = lo2 * hi1;
+  hi = hi1 * hi2;
+
+  /* Check carry overflow of i1 + i2.  */
+  i1 += i2;
+  hi += (unsigned int) (i1 < i2) << 16;
+
+  hi += i1 >> 16;
+  i1  = i1 << 16;
+
+  /* Check carry overflow of i1 + lo.  */
+  lo += i1;
+  hi += (lo < i1);
+
+  value->low = lo;
+  value->high = hi;
+}
+
+/* Count the number of most significant zero bits in N.  */
+
+static unsigned int
+sfnt_count_leading_zero_bits (unsigned int n)
+{
+  int shift;
+
+  shift = 0;
+
+  if (n & 0xffff0000ul)
+    {
+      n >>= 16;
+      shift += 16;
+    }
+
+  if (n & 0x0000ff00ul)
+    {
+      n >>= 8;
+      shift += 8;
+    }
+
+  if (n & 0x000000f0ul)
+    {
+      n >>= 4;
+      shift += 4;
+    }
+
+  if (n & 0x0000000cul)
+    {
+      n >>= 2;
+      shift += 2;
+    }
+
+  if (n & 0x00000002ul)
+    shift += 1;
+
+  return shift;
+}
+
+/* Calculate AB / C.  Value is a 32 bit unsigned integer.  */
+
+static unsigned int
+sfnt_multiply_divide_2 (struct sfnt_large_integer *ab,
+                       unsigned int c)
+{
+  unsigned int hi, lo;
+  int i;
+  unsigned int r, q; /* Remainder and quotient.  */
+
+  hi = ab->high;
+  lo = ab->low;
+
+  i = 31 - sfnt_count_leading_zero_bits (hi);
+  r = (hi << i) | (lo >> (32 - i));
+  lo <<= i;
+  q = r / c;
+  r -= q * c;
+  i = 32 - i;
+
+  do
+    {
+      q <<= 1;
+      r = (r << 1) | (lo >> 31);
+      lo <<= 1;
+
+      if (r >= c)
+       {
+         r -= c;
+         q |= 1;
+       }
+    }
+  while (--i);
+
+  return q;
+}
+
+#endif
+
+/* Calculate (A * B) / C with no rounding and return the result, using
+   a 64 bit integer if necessary.  */
+
+static unsigned int
+sfnt_multiply_divide (unsigned int a, unsigned int b, unsigned int c)
+{
+#ifndef INT64_MAX
+  struct sfnt_large_integer temp;
+
+  sfnt_multiply_divide_1 (a, b, &temp);
+  return sfnt_multiply_divide_2 (&temp, c);
+#else
+  uint64_t temp;
+
+  temp = (uint64_t) a * (uint64_t) b;
+  return temp / c;
+#endif
+}
+
+#ifndef INT64_MAX
+
+/* Add the specified unsigned 32-bit N to the large integer
+   INTEGER.  */
+
+static void
+sfnt_large_integer_add (struct sfnt_large_integer *integer,
+                       uint32_t n)
+{
+  struct sfnt_large_integer number;
+
+  number.low = integer->low + n;
+  number.high = integer->high + (number.low
+                                < integer->low);
+
+  *integer = number;
+}
+
+/* Calculate (A * B) / C, rounding the result with a threshold of N.
+   Use a 64 bit temporary.  */
+
+static unsigned int
+sfnt_multiply_divide_round (unsigned int a, unsigned int b,
+                           unsigned int n, unsigned int c)
+{
+  struct sfnt_large_integer temp;
+
+  sfnt_multiply_divide_1 (a, b, &temp);
+  sfnt_large_integer_add (&temp, n);
+  return sfnt_multiply_divide_2 (&temp, c);
+}
+
+#endif /* INT64_MAX */
+
+/* The same as sfnt_multiply_divide, but handle signed values
+   instead.  */
+
+MAYBE_UNUSED static int
+sfnt_multiply_divide_signed (int a, int b, int c)
+{
+  int sign;
+
+  sign = 1;
+
+  if (a < 0)
+    sign = -sign;
+
+  if (b < 0)
+    sign = -sign;
+
+  if (c < 0)
+    sign = -sign;
+
+  return (sfnt_multiply_divide (abs (a), abs (b), abs (c))
+         * sign);
+}
+
+/* Multiply the two 16.16 fixed point numbers X and Y.  Return the
+   result regardless of overflow.  */
+
+static sfnt_fixed
+sfnt_mul_fixed (sfnt_fixed x, sfnt_fixed y)
+{
+#ifdef INT64_MAX
+  int64_t product;
+
+  product = (int64_t) x * (int64_t) y;
+
+  /* This can be done quickly with int64_t.  */
+  return product / (int64_t) 65536;
+#else
+  int sign;
+
+  sign = 1;
+
+  if (x < 0)
+    sign = -sign;
+
+  if (y < 0)
+    sign = -sign;
+
+  return sfnt_multiply_divide (abs (x), abs (y),
+                              65536) * sign;
+#endif
+}
+
+/* Multiply the two 16.16 fixed point numbers X and Y, with rounding
+   of the result.  */
+
+static sfnt_fixed
+sfnt_mul_fixed_round (sfnt_fixed x, sfnt_fixed y)
+{
+#ifdef INT64_MAX
+  int64_t product, round;
+
+  product = (int64_t) x * (int64_t) y;
+  round = product < 0 ? -32768 : 32768;
+
+  /* This can be done quickly with int64_t.  */
+  return (product + round) / (int64_t) 65536;
+#else
+  int sign;
+
+  sign = 1;
+
+  if (x < 0)
+    sign = -sign;
+
+  if (y < 0)
+    sign = -sign;
+
+  return sfnt_multiply_divide_round (abs (x), abs (y),
+                                    32768, 65536) * sign;
+#endif
+}
+
+/* Set the pen size to the specified point and return.  POINT will be
+   scaled up to the pixel size.  */
+
+static void
+sfnt_move_to_and_build (struct sfnt_point point, void *dcontext)
+{
+  sfnt_fixed x, y;
+
+  x = sfnt_mul_fixed (build_outline_context.factor, point.x);
+  y = sfnt_mul_fixed (build_outline_context.factor, point.y);
+
+  build_outline_context.outline = sfnt_build_append (0, x, y);
+  build_outline_context.x = x;
+  build_outline_context.y = y;
+}
+
+/* Record a line to the specified point and return.  POINT will be
+   scaled up to the pixel size.  */
+
+static void
+sfnt_line_to_and_build (struct sfnt_point point, void *dcontext)
+{
+  sfnt_fixed x, y;
+
+  x = sfnt_mul_fixed (build_outline_context.factor, point.x);
+  y = sfnt_mul_fixed (build_outline_context.factor, point.y);
+
+  build_outline_context.outline
+    = sfnt_build_append (SFNT_GLYPH_OUTLINE_LINETO,
+                        x, y);
+  build_outline_context.x = x;
+  build_outline_context.y = y;
+}
+
+/* Divide the two 16.16 fixed point numbers X and Y.  Return the
+   result regardless of overflow.  */
+
+static sfnt_fixed
+sfnt_div_fixed (sfnt_fixed x, sfnt_fixed y)
+{
+#ifdef INT64_MAX
+  int64_t result;
+
+  result = ((int64_t) x * 65536) / y;
+
+  return result;
+#else
+  int sign;
+  unsigned int a, b;
+
+  sign = 1;
+
+  if (x < 0)
+    sign = -sign;
+
+  if (y < 0)
+    sign = -sign;
+
+  a = abs (x);
+  b = abs (y);
+
+  return sfnt_multiply_divide (a, 65536, b) * sign;
+#endif
+}
+
+/* Return the ceiling value of the specified fixed point number X.  */
+
+static sfnt_fixed
+sfnt_ceil_fixed (sfnt_fixed x)
+{
+  return (x + 0177777) & 037777600000;
+}
+
+/* Return the floor value of the specified fixed point number X.  */
+
+static sfnt_fixed
+sfnt_floor_fixed (sfnt_fixed x)
+{
+  return x & 037777600000;
+}
+
+/* Given a curve consisting of three points CONTROL0, CONTROL1 and
+   ENDPOINT, return whether or not the curve is sufficiently small to
+   be approximated by a line between CONTROL0 and ENDPOINT.  */
+
+static bool
+sfnt_curve_is_flat (struct sfnt_point control0,
+                   struct sfnt_point control1,
+                   struct sfnt_point endpoint)
+{
+  struct sfnt_point g, h;
+
+  g.x = control1.x - control0.x;
+  g.y = control1.y - control0.y;
+  h.x = endpoint.x - control0.x;
+  h.y = endpoint.y - control0.y;
+
+  /* 2.0 is a constant describing the area covered at which point the
+     curve is considered "flat".  */
+  return (abs (sfnt_mul_fixed (g.x, h.y)
+              - sfnt_mul_fixed (g.y, h.x))
+         <= 0400000);
+}
+
+/* Recursively split the splines in the bezier curve formed from
+   CONTROL0, CONTROL1 and ENDPOINT until the area between the curve's
+   two ends is small enough to be considered ``flat''.  Then, turn
+   those ``flat'' curves into lines.  */
+
+static void
+sfnt_curve_to_and_build_1 (struct sfnt_point control0,
+                          struct sfnt_point control1,
+                          struct sfnt_point endpoint)
+{
+  struct sfnt_point ab, bc, abbc;
+
+  /* control0, control and endpoint make up the spline.  Figure out
+     its distance from a line.  */
+  if (sfnt_curve_is_flat (control0, control1, endpoint))
+    {
+      /* Draw a line to endpoint.  */
+      build_outline_context.outline
+       = sfnt_build_append (SFNT_GLYPH_OUTLINE_LINETO,
+                            endpoint.x, endpoint.y);
+      build_outline_context.x = endpoint.x;
+      build_outline_context.y = endpoint.y;
+    }
+  else
+    {
+      /* Calculate new control points.
+        Maybe apply a recursion limit here? */
+      sfnt_lerp_half (&control0, &control1, &ab);
+      sfnt_lerp_half (&control1, &endpoint, &bc);
+      sfnt_lerp_half (&ab, &bc, &abbc);
+
+      /* Keep splitting until a flat enough spline results.  */
+      sfnt_curve_to_and_build_1 (control0, ab, abbc);
+
+      /* Then go on with the spline between control1 and endpoint.  */
+      sfnt_curve_to_and_build_1 (abbc, bc, endpoint);
+    }
+}
+
+/* Scale and decompose the specified bezier curve into individual
+   lines.  Then, record each of those lines into the outline being
+   built.  */
+
+static void
+sfnt_curve_to_and_build (struct sfnt_point control,
+                        struct sfnt_point endpoint,
+                        void *dcontext)
+{
+  struct sfnt_point control0;
+
+  control0.x = build_outline_context.x;
+  control0.y = build_outline_context.y;
+  control.x = sfnt_mul_fixed (control.x,
+                             build_outline_context.factor);
+  control.y = sfnt_mul_fixed (control.y,
+                             build_outline_context.factor);
+  endpoint.x = sfnt_mul_fixed (endpoint.x,
+                              build_outline_context.factor);
+  endpoint.y = sfnt_mul_fixed (endpoint.y,
+                              build_outline_context.factor);
+
+  sfnt_curve_to_and_build_1 (control0, control, endpoint);
+}
+
+/* Non-reentrantly build the outline for the specified GLYPH at the
+   given scale factor.  Return the outline data with a refcount of 0
+   upon success, or NULL upon failure.
+
+   SCALE is a scale factor that converts between em space and device
+   space.
+
+   Use the unscaled glyph METRICS to determine the origin point of the
+   outline.
+
+   Call GET_GLYPH and FREE_GLYPH with the specified DCONTEXT to obtain
+   glyphs for compound glyph subcomponents.  */
+
+TEST_STATIC struct sfnt_glyph_outline *
+sfnt_build_glyph_outline (struct sfnt_glyph *glyph,
+                         sfnt_fixed scale,
+                         struct sfnt_glyph_metrics *metrics,
+                         sfnt_get_glyph_proc get_glyph,
+                         sfnt_free_glyph_proc free_glyph,
+                         void *dcontext)
+{
+  struct sfnt_glyph_outline *outline;
+  int rc;
+  sfnt_fword origin;
+
+  memset (&build_outline_context, 0, sizeof build_outline_context);
+
+  /* Allocate the outline now with enough for 44 words at the end.  */
+  outline = xmalloc (sizeof *outline + 40 * sizeof (*outline->outline));
+  outline->outline_size = 40;
+  outline->outline_used = 0;
+  outline->refcount = 0;
+  outline->outline
+    = (struct sfnt_glyph_outline_command *) (outline + 1);
+
+  /* DCONTEXT will be passed to GET_GLYPH and FREE_GLYPH, so global
+     variables must be used to communicate with the decomposition
+     functions.  */
+  build_outline_context.outline = outline;
+
+  /* Clear outline bounding box.  */
+  outline->xmin = 0;
+  outline->ymin = 0;
+  outline->xmax = 0;
+  outline->ymax = 0;
+
+  /* Set the scale factor.  */
+  build_outline_context.factor = scale;
+
+  /* Decompose the outline.  */
+  rc = sfnt_decompose_glyph (glyph, sfnt_move_to_and_build,
+                            sfnt_line_to_and_build,
+                            sfnt_curve_to_and_build,
+                            get_glyph, free_glyph, dcontext);
+
+  /* Synchronize the outline object with what might have changed
+     inside sfnt_decompose_glyph.  */
+  outline = build_outline_context.outline;
+
+  if (rc)
+    {
+      xfree (outline);
+      return NULL;
+    }
+
+  /* Compute the origin position.  Note that the original glyph xmin
+     is first used to calculate the origin point, and the origin
+     distortion is applied to it to get the distorted origin.  */
+
+  origin = glyph->xmin - metrics->lbearing + glyph->origin_distortion;
+  outline->origin = sfnt_mul_fixed (origin, scale);
+
+  return outline;
+}
+
+
+
+/* Glyph rasterization.  The algorithm used here is fairly simple.
+   Each contour is decomposed into lines, which turn into a polygon.
+   Then, a bog standard edge filler is used to turn them into
+   spans.  */
+
+/* Coverage table.  This is a four dimensional array indiced by the Y,
+   then X axis fractional, shifted down to 2 bits.  */
+
+static const unsigned char sfnt_poly_coverage[8][9] =
+  {
+    { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+    { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+    { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+    { 0, 3, 7, 11, 15, 19, 23, 27, 31, },
+    { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+    { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+    { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+    { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+  };
+
+/* Return the nearest coordinate on the sample grid no less than
+   F.  */
+
+static sfnt_fixed
+sfnt_poly_grid_ceil (sfnt_fixed f)
+{
+  return (((f + (SFNT_POLY_START - 1))
+          & ~(SFNT_POLY_STEP - 1)) + SFNT_POLY_START);
+}
+
+enum
+  {
+    SFNT_POLY_ALIGNMENT = 4,
+  };
+
+/* Initialize the specified RASTER in preparation for displaying spans
+   for OUTLINE, and set RASTER->refcount to 0.  The caller must then
+   set RASTER->cells to a zeroed array of size RASTER->stride *
+   RASTER->height, aligned to RASTER.  */
+
+TEST_STATIC void
+sfnt_prepare_raster (struct sfnt_raster *raster,
+                    struct sfnt_glyph_outline *outline)
+{
+  raster->width
+    = (sfnt_ceil_fixed (outline->xmax)
+       - sfnt_floor_fixed (outline->xmin)) / 65536;
+  raster->height
+    = (sfnt_ceil_fixed (outline->ymax)
+       - sfnt_floor_fixed (outline->ymin)) / 65536;
+  raster->refcount = 0;
+
+  /* Align the raster to a SFNT_POLY_ALIGNMENT byte boundary.  */
+  raster->stride = ((raster->width
+                    + (SFNT_POLY_ALIGNMENT - 1))
+                   & ~(SFNT_POLY_ALIGNMENT - 1));
+
+  /* Apply outline->origin.  This is 0 by convention in most fonts.
+     However, variable fonts typically change this as variations are
+     applied.  */
+  raster->offx = sfnt_floor_fixed (outline->xmin
+                                  - outline->origin) / 65536;
+  raster->offy = sfnt_floor_fixed (outline->ymin) / 65536;
+}
+
+typedef void (*sfnt_edge_proc) (struct sfnt_edge *, size_t,
+                               void *);
+typedef void (*sfnt_span_proc) (struct sfnt_edge *, sfnt_fixed, void *);
+
+/* Move EDGE->x forward, assuming that the scanline has moved upwards
+   by SFNT_POLY_STEP.  */
+
+static void
+sfnt_step_edge (struct sfnt_edge *edge)
+{
+  /* Add step.  */
+  edge->x += edge->step_x;
+}
+
+/* Build a list of edges for each contour in OUTLINE, applying xmin
+   and ymin as the offset to each edge.  Call EDGE_PROC with DCONTEXT
+   and the resulting edges as arguments.  It is OK to modify the edges
+   given to EDGE_PROC.  Align all edges to the sub-pixel grid.  */
+
+static void
+sfnt_build_outline_edges (struct sfnt_glyph_outline *outline,
+                         sfnt_edge_proc edge_proc, void *dcontext)
+{
+  struct sfnt_edge *edges;
+  size_t i, edge, next_vertex;
+  sfnt_fixed dx, dy, bot, step_x, ymin, xmin;
+  size_t top, bottom, y;
+
+  edges = alloca (outline->outline_used * sizeof *edges);
+  edge = 0;
+
+  /* ymin and xmin must be the same as the offset used to set offy and
+     offx in rasters.  */
+  ymin = sfnt_floor_fixed (outline->ymin);
+  xmin = sfnt_floor_fixed (outline->xmin);
+
+  for (i = 0; i < outline->outline_used; ++i)
+    {
+      /* Set NEXT_VERTEX to the next point (vertex) in this contour.
+
+        If i is past the end of the contour, then don't build edges
+        for this point.  */
+      next_vertex = i + 1;
+
+      if (next_vertex == outline->outline_used
+         || !(outline->outline[next_vertex].flags
+              & SFNT_GLYPH_OUTLINE_LINETO))
+       continue;
+
+      /* Skip past horizontal vertices.  */
+      if (outline->outline[next_vertex].y == outline->outline[i].y)
+       continue;
+
+      /* Figure out the winding direction.  */
+      if (outline->outline[next_vertex].y < outline->outline[i].y)
+       /* Vector will cross imaginary ray from its bottom from the
+          left of the ray.  Winding is thus 1.  */
+       edges[edge].winding = 1;
+      else
+       /* Moving clockwise.  Winding is thus -1.  */
+       edges[edge].winding = -1;
+
+      /* Figure out the top and bottom values of this edge.  If the
+        next edge is below, top is here and bot is the edge below.
+        If the next edge is above, then top is there and this is the
+        bottom.  */
+
+      if (outline->outline[next_vertex].y < outline->outline[i].y)
+       {
+         /* End of edge is below this one (keep in mind this is a
+            cartesian coordinate system, so smaller values are below
+            larger ones.) */
+         top = i;
+         bottom = next_vertex;
+       }
+      else
+       {
+         /* End of edge is above this one.  */
+         bottom = i;
+         top = next_vertex;
+       }
+
+      bot = (outline->outline[bottom].y - ymin);
+      edges[edge].top = (outline->outline[top].y - ymin);
+
+      /* Record the edge.  Rasterization happens from bottom to
+        up, so record the X at the bottom.  */
+      edges[edge].x = (outline->outline[bottom].x - xmin);
+      dx = (outline->outline[top].x - outline->outline[bottom].x);
+      dy = abs (outline->outline[top].y
+               - outline->outline[bottom].y);
+
+      /* Step to first grid point.  */
+      y = sfnt_poly_grid_ceil (bot);
+
+      /* If rounding would make the edge not cover any area, skip this
+        edge. */
+
+      if (y >= edges[edge].top)
+       continue;
+
+      /* Compute the step X.  This is how much X changes for each
+        increase in Y.  */
+
+      step_x = sfnt_div_fixed (dx, dy);
+      edges[edge].next = NULL;
+
+      /* Compute the step X scaled to the poly step.  */
+      edges[edge].step_x
+       = sfnt_mul_fixed (step_x, SFNT_POLY_STEP);
+
+      /* Step to the grid point.  */
+      edges[edge].x += sfnt_mul_fixed (step_x, bot - y);
+
+      /* Set the bottom position.  */
+      edges[edge].bottom = y;
+
+      edge++;
+    }
+
+  if (edge)
+    edge_proc (edges, edge, dcontext);
+}
+
+/* Sort an array of SIZE edges to increase by bottom Y position, in
+   preparation for building spans.
+
+   Insertion sort is used because there are usually not very many
+   edges, and anything larger would bloat up the code.  */
+
+static void
+sfnt_edge_sort (struct sfnt_edge *edges, size_t size)
+{
+  ssize_t i, j;
+  struct sfnt_edge edge;
+
+  for (i = 1; i < size; ++i)
+    {
+      edge = edges[i];
+      j = i - 1;
+
+      while (j >= 0 && (edges[j].bottom > edge.bottom))
+       {
+         edges[j + 1] = edges[j];
+         j--;
+       }
+
+      edges[j + 1] = edge;
+    }
+}
+
+/* Draw EDGES, an unsorted array of polygon edges of size SIZE.  For
+   each scanline, call SPAN_FUNC with a list of active edges and
+   coverage information, and DCONTEXT.
+
+   Sort each edge in ascending order by the bottommost Y coordinate to
+   which it applies.  Start a loop on the Y coordinate, which starts
+   out at that of the bottommost edge.  For each iteration, add edges
+   that now overlap with Y, keeping them sorted by X.  Poly those
+   edges through SPAN_FUNC.  Then, move upwards by SFNT_POLY_STEP,
+   remove edges that no longer apply, and interpolate the remaining
+   edges' X coordinates.  Repeat until all the edges have been polyed.
+
+   Or alternatively, think of this as such: each edge is actually a
+   vector from its bottom position towards its top most position.
+   Every time Y moves upwards, the position of each edge intersecting
+   with Y is interpolated and added to a list of spans along with
+   winding information that is then given to EDGE_FUNC.
+
+   Anti-aliasing is performed using a coverage map for fractional
+   coordinates, and incrementing the Y axis by SFNT_POLY_STEP instead
+   of 1.  SFNT_POLY_STEP is chosen to always keep Y aligned to a grid
+   placed such that there are always 1 << SFNT_POLY_SHIFT positions
+   available for each integral pixel coordinate.  */
+
+static void
+sfnt_poly_edges (struct sfnt_edge *edges, size_t size,
+                sfnt_span_proc span_func, void *dcontext)
+{
+  sfnt_fixed y;
+  size_t e;
+  struct sfnt_edge *active, **prev, *a, *n;
+
+  if (!size)
+    return;
+
+  /* Sort edges to ascend by Y-order.  Once again, remember: cartesian
+     coordinates.  */
+  sfnt_edge_sort (edges, size);
+
+  /* Step down line by line.  Find active edges.  */
+
+  y = edges[0].bottom;
+  active = 0;
+  active = NULL;
+  e = 0;
+
+  for (;;)
+    {
+      /* Add in new edges keeping them sorted.  */
+      for (; e < size && edges[e].bottom <= y; ++e)
+       {
+         /* Find where to place this edge.  */
+         for (prev = &active; (a = *prev); prev = &(a->next))
+           {
+             if (a->x > edges[e].x)
+               break;
+           }
+
+         edges[e].next = *prev;
+         *prev = &edges[e];
+       }
+
+      /* Draw this span at the current position.  Y axis antialiasing
+        is expected to be handled by SPAN_FUNC.  */
+      span_func (active, y, dcontext);
+
+      /* Compute the next Y position.  */
+      y += SFNT_POLY_STEP;
+
+      /* Strip out edges that no longer have effect.  */
+
+      for (prev = &active; (a = *prev);)
+       {
+         if (a->top <= y)
+           *prev = a->next;
+         else
+           prev = &a->next;
+       }
+
+      /* Break if all is done.  */
+      if (!active && e == size)
+       break;
+
+      /* Step all edges.  */
+      for (a = active; a; a = a->next)
+       sfnt_step_edge (a);
+
+      /* Resort on X axis.  */
+      for (prev = &active; (a = *prev) && (n = a->next);)
+       {
+         if (a->x > n->x)
+           {
+             a->next = n->next;
+             n->next = a;
+             *prev = n;
+             prev = &active;
+           }
+         else
+           prev = &a->next;
+       }
+    }
+}
+
+/* Saturate and convert the given unsigned short value X to an
+   unsigned char.  */
+
+static unsigned char
+sfnt_saturate_short (unsigned short x)
+{
+  if (x > 255)
+    return 255;
+
+  return x;
+}
+
+/* Fill a single span of pixels between X0 and X1 at Y, a raster
+   coordinate, onto RASTER.  */
+
+static void
+sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y,
+               sfnt_fixed x0, sfnt_fixed x1)
+{
+  unsigned char *start;
+  const unsigned char *coverage;
+  sfnt_fixed left, right, end;
+  unsigned short w, a;
+  int row;
+#ifndef NDEBUG
+  unsigned char *row_end;
+#endif /* NDEBUG */
+
+  /* Clip bounds to pixmap.  */
+
+  if (x0 < 0)
+    x0 = 0;
+
+  /* If x1 is greater than the raster width, make sure the last pixel
+     is filled and no more after that.  */
+
+  if (x1 > raster->width * 65536)
+    x1 = raster->width * 65536;
+
+  /* Check for empty spans.  */
+  if (x1 <= x0)
+    return;
+
+  /* Figure out coverage based on Y axis fractional.  */
+  coverage = sfnt_poly_coverage[(y >> (16 - SFNT_POLY_SHIFT))
+                               & SFNT_POLY_MASK];
+  row = y >> 16;
+
+  /* Don't fill out of bounds rows.  */
+  if (row < 0 || row >= raster->height)
+    return;
+
+  /* Set start, then start filling according to coverage.  left and
+     right are now .3.  */
+  left = x0 >> (16 - SFNT_POLY_SHIFT);
+  right = x1 >> (16 - SFNT_POLY_SHIFT);
+  start = raster->cells + row * raster->stride;
+#ifndef NDEBUG
+  row_end = start + raster->width;
+#endif /* NDEBUG */
+  start += left >> SFNT_POLY_SHIFT;
+
+  /* If left and right actually lie in the same pixel, just fill with
+     the coverage of both and return.  */
+
+  if ((left & ~SFNT_POLY_MASK) == (right & ~SFNT_POLY_MASK))
+    {
+      /* Assert that start does not exceed the end of the row.  */
+      assert (start <= row_end);
+
+      w = coverage[right - left];
+      a = *start + w;
+
+      *start = sfnt_saturate_short (a);
+      return;
+    }
+
+  /* Compute coverage for first pixel, then poly.  The code from here
+     onwards assumes that left and right are on two different
+     pixels.  */
+
+  if (left & SFNT_POLY_MASK)
+    {
+      /* Assert that start does not exceed the end of the row.  */
+      assert (start <= row_end);
+
+      /* Compute the coverage for the first pixel, and move left past
+        it.  The coverage is a number from 1 to 7 describing how
+        ``partially'' covered this pixel is.  */
+
+      end = (left + SFNT_POLY_SAMPLE - 1) & ~SFNT_POLY_MASK;
+      end = MIN (right, end);
+
+      w = coverage[end - left];
+      a = *start + w;
+
+      /* Now move left past.  */
+      left = end;
+      *start++ = sfnt_saturate_short (a);
+    }
+
+  /* Clear coverage info for first pixel.  Compute coverage for center
+     pixels.  Note that SFNT_POLY_SAMPLE is used and not
+     SFNT_POLY_MASK, because coverage has a blank column at the
+     start.  */
+  w = coverage[SFNT_POLY_SAMPLE];
+
+  /* Fill pixels between left and right.  */
+  while (left + SFNT_POLY_MASK < right)
+    {
+      /* Assert that start does not exceed the end of the row.  */
+      assert (start <= row_end);
+
+      a = *start + w;
+      *start++ = sfnt_saturate_short (a);
+      left += SFNT_POLY_SAMPLE;
+    }
+
+  /* Fill rightmost pixel with any partial coverage.  */
+
+  if (right & SFNT_POLY_MASK)
+    {
+      /* Assert that start does not exceed the end of the row.  */
+      assert (start <= row_end);
+
+      w = coverage[right - left];
+      a = *start + w;
+      *start = sfnt_saturate_short (a);
+    }
+}
+
+/* Poly each span starting from START onto RASTER, at position Y.  Y
+   here is still a cartesian coordinate, where the bottom of the
+   raster is 0.  But that is no longer true by the time sfnt_span_fill
+   is called.  */
+
+static void
+sfnt_poly_span (struct sfnt_edge *start, sfnt_fixed y,
+               struct sfnt_raster *raster)
+{
+  struct sfnt_edge *edge;
+  int winding;
+  sfnt_fixed x0, x1;
+
+  /* Pacify -Wmaybe-uninitialized; x1 and x0 are only used when edge
+     != start, at which point x0 has already been set.  */
+  x0 = x1 = 0;
+
+  /* Generate the X axis coverage map.  Then poly it onto RASTER.
+     winding on each edge determines the winding direction: when it is
+     positive, winding is 1.  When it is negative, winding is -1.
+
+     Fill each consecutive stretch of spans that are inside the glyph;
+     otherwise, coverage will overlap for some spans, but not
+     others.
+
+     The spans must be terminated with an edge that causes an
+     off-transition, or some spans will not be filled.  */
+
+  winding = 0;
+
+  for (edge = start; edge; edge = edge->next)
+    {
+      if (!winding)
+       {
+         if (edge != start && x0 != x1)
+           /* Draw this section of spans that are on.  */
+           sfnt_fill_span (raster, (raster->height << 16) - y,
+                           x0, x1);
+
+         x0 = x1 = edge->x;
+       }
+      else
+       x1 = edge->x;
+
+      winding += edge->winding;
+    }
+
+  /* Draw the last span following the last off-transition.  */
+
+  if (!winding && edge != start && x0 != x1)
+    sfnt_fill_span (raster, (raster->height << 16) - y,
+                   x0, x1);
+}
+
+
+
+/* Main entry point for outline rasterization.  */
+
+/* Raster the spans between START and its end to the raster specified
+   as DCONTEXT.  The span's position is Y.  */
+
+static void
+sfnt_raster_span (struct sfnt_edge *start, sfnt_fixed y,
+                 void *dcontext)
+{
+  sfnt_poly_span (start, y, dcontext);
+}
+
+/* Generate and poly each span in EDGES onto the raster specified as
+   DCONTEXT.  */
+
+static void
+sfnt_raster_edge (struct sfnt_edge *edges, size_t num_edges,
+                 void *dcontext)
+{
+  sfnt_poly_edges (edges, num_edges, sfnt_raster_span,
+                  dcontext);
+}
+
+/* Generate an alpha mask for the glyph outline OUTLINE.  Value is the
+   alpha mask upon success, NULL upon failure.  */
+
+TEST_STATIC struct sfnt_raster *
+sfnt_raster_glyph_outline (struct sfnt_glyph_outline *outline)
+{
+  struct sfnt_raster raster, *data;
+
+  /* Get the raster parameters.  */
+  sfnt_prepare_raster (&raster, outline);
+
+  /* Allocate the raster data.  */
+  data = xmalloc (sizeof *data + raster.stride * raster.height);
+  *data = raster;
+  data->cells = (unsigned char *) (data + 1);
+  memset (data->cells, 0, raster.stride * raster.height);
+
+  /* Generate edges for the outline, polying each array of edges to
+     the raster.  */
+  sfnt_build_outline_edges (outline, sfnt_raster_edge, data);
+
+  /* All done.  */
+  return data;
+}
+
+
+
+/* Glyph metrics computation.  */
+
+/* Read an hmtx table from the font FD, using the table directory
+   specified as SUBTABLE, the maxp table MAXP, and the hhea table
+   HHEA.
+
+   Return NULL upon failure, and the hmtx table otherwise.
+   HHEA->num_of_long_hor_metrics determines the number of horizontal
+   metrics present, and MAXP->num_glyphs -
+   HHEA->num_of_long_hor_metrics determines the number of left-side
+   bearings present.  */
+
+TEST_STATIC struct sfnt_hmtx_table *
+sfnt_read_hmtx_table (int fd, struct sfnt_offset_subtable *subtable,
+                     struct sfnt_hhea_table *hhea,
+                     struct sfnt_maxp_table *maxp)
+{
+  struct sfnt_table_directory *directory;
+  struct sfnt_hmtx_table *hmtx;
+  size_t size;
+  ssize_t rc;
+  int i;
+
+  /* Find the table in the directory.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_HMTX);
+
+  if (!directory)
+    return NULL;
+
+  /* Figure out how many bytes are required.  */
+  size = ((hhea->num_of_long_hor_metrics
+          * sizeof (struct sfnt_long_hor_metric))
+         + (MAX (0, ((int) maxp->num_glyphs
+                     - hhea->num_of_long_hor_metrics))
+            * sizeof (int16_t)));
+
+  /* Check the length matches exactly.  */
+  if (directory->length != size)
+    return NULL;
+
+  /* Seek to the location given in the directory.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  /* Now allocate enough to hold all of that along with the table
+     directory structure.  */
+
+  hmtx = xmalloc (sizeof *hmtx + size);
+
+  /* Read into hmtx + 1.  */
+  rc = read (fd, hmtx + 1, size);
+  if (rc < size)
+    {
+      xfree (hmtx);
+      return NULL;
+    }
+
+  /* Set pointers to data.  */
+  hmtx->h_metrics = (struct sfnt_long_hor_metric *) (hmtx + 1);
+  hmtx->left_side_bearing
+    = (int16_t *) (hmtx->h_metrics
+                  + hhea->num_of_long_hor_metrics);
+
+  /* Swap what was read.  */
+
+  for (i = 0; i < hhea->num_of_long_hor_metrics; ++i)
+    {
+      sfnt_swap16 (&hmtx->h_metrics[i].advance_width);
+      sfnt_swap16 (&hmtx->h_metrics[i].left_side_bearing);
+    }
+
+  for (; i < maxp->num_glyphs; ++i)
+    sfnt_swap16 (&hmtx->left_side_bearing[i - hhea->num_of_long_hor_metrics]);
+
+  /* All done.  */
+  return hmtx;
+}
+
+/* Obtain glyph metrics for the glyph indiced by GLYPH at the
+   specified PIXEL_SIZE.  Return 0 and the metrics in *METRICS if
+   metrics could be found, else 1.
+
+   If PIXEL_SIZE is -1, do not perform any scaling on the glyph
+   metrics; HEAD need not be specified in that case.
+
+   HMTX, HHEA, HEAD and MAXP should be the hmtx, hhea, head, and maxp
+   tables of the font respectively.  */
+
+TEST_STATIC int
+sfnt_lookup_glyph_metrics (sfnt_glyph glyph, int pixel_size,
+                          struct sfnt_glyph_metrics *metrics,
+                          struct sfnt_hmtx_table *hmtx,
+                          struct sfnt_hhea_table *hhea,
+                          struct sfnt_head_table *head,
+                          struct sfnt_maxp_table *maxp)
+{
+  short lbearing;
+  unsigned short advance;
+  sfnt_fixed factor;
+
+  if (glyph < hhea->num_of_long_hor_metrics)
+    {
+      /* There is a long entry in the hmtx table.  */
+      lbearing = hmtx->h_metrics[glyph].left_side_bearing;
+      advance = hmtx->h_metrics[glyph].advance_width;
+    }
+  else if (hhea->num_of_long_hor_metrics
+          && glyph < maxp->num_glyphs)
+    {
+      /* There is a short entry in the hmtx table.  */
+      lbearing
+       = hmtx->left_side_bearing[glyph
+                                 - hhea->num_of_long_hor_metrics];
+      advance
+       = hmtx->h_metrics[hhea->num_of_long_hor_metrics - 1].advance_width;
+    }
+  else
+    /* No entry corresponds to the glyph.  */
+    return 1;
+
+  if (pixel_size == -1)
+    {
+      /* Return unscaled metrics in this case.  */
+      metrics->lbearing = lbearing;
+      metrics->advance = advance;
+      return 0;
+    }
+
+  /* Now scale lbearing and advance up to the pixel size.  */
+  factor = sfnt_div_fixed (pixel_size, head->units_per_em);
+
+  /* Save them.  */
+  metrics->lbearing = sfnt_mul_fixed (lbearing * 65536, factor);
+  metrics->advance = sfnt_mul_fixed (advance * 65536, factor);
+
+  /* All done.  */
+  return 0;
+}
+
+/* Scale the specified glyph metrics by FACTOR.
+   Set METRICS->lbearing and METRICS->advance to their current
+   values times factor.  */
+
+MAYBE_UNUSED TEST_STATIC void
+sfnt_scale_metrics (struct sfnt_glyph_metrics *metrics,
+                   sfnt_fixed factor)
+{
+  metrics->lbearing
+    = sfnt_mul_fixed (metrics->lbearing * 65536, factor);
+  metrics->advance
+    = sfnt_mul_fixed (metrics->advance * 65536, factor);
+}
+
+/* Calculate the factor used to convert em space to device space for a
+   font with the specified HEAD table and PPEM value.  */
+
+MAYBE_UNUSED TEST_STATIC sfnt_fixed
+sfnt_get_scale (struct sfnt_head_table *head, int ppem)
+{
+  /* Figure out how to convert from font unit-space to pixel space.
+     To turn one unit to its corresponding pixel size given a ppem of
+     1, the unit must be divided by head->units_per_em.  Then, it must
+     be multipled by the ppem.  So,
+
+       PIXEL = UNIT / UPEM * PPEM
+
+     which means:
+
+       PIXEL = UNIT * PPEM / UPEM  */
+
+  return sfnt_div_fixed (ppem, head->units_per_em);
+}
+
+
+
+/* Font style parsing.  */
+
+/* Read the name table from the given font FD, using the table
+   directory specified as SUBTABLE.  Perform validation on the offsets
+   in the name records.  Return NULL upon failure, else the name
+   table.  */
+
+TEST_STATIC struct sfnt_name_table *
+sfnt_read_name_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+  struct sfnt_table_directory *directory;
+  struct sfnt_name_table *name;
+  size_t required;
+  ssize_t rc;
+  int i;
+
+  /* Find the table in the directory.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_NAME);
+
+  if (!directory)
+    return NULL;
+
+  /* Seek to the location given in the directory.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  /* Figure out the minimum that has to be read.  */
+  required = SFNT_ENDOF (struct sfnt_name_table,
+                        string_offset, uint16_t);
+
+  if (directory->length < required)
+    return NULL;
+
+  /* Allocate enough to hold the name table and variable length
+     data.  */
+  name = xmalloc (sizeof *name + directory->length);
+
+  /* Read the fixed length data.  */
+  rc = read (fd, name, required);
+  if (rc < required)
+    {
+      xfree (name);
+      return NULL;
+    }
+
+  /* Swap what was read.  */
+  sfnt_swap16 (&name->format);
+  sfnt_swap16 (&name->count);
+  sfnt_swap16 (&name->string_offset);
+
+  /* Reject unsupported formats.  */
+  if (name->format)
+    {
+      xfree (name);
+      return NULL;
+    }
+
+  /* Set the pointer to the start of the variable length data.  */
+  name->name_records
+    = (struct sfnt_name_record *) (name + 1);
+
+  /* Check there is enough for the name records.  */
+  required = directory->length - required;
+  if (required < name->count * sizeof *name->name_records)
+    {
+      xfree (name);
+      return NULL;
+    }
+
+  /* Read the variable length data.  First, read the name records.  */
+  rc = read (fd, name->name_records,
+            (name->count
+             * sizeof *name->name_records));
+  if (rc < (name->count
+           * sizeof *name->name_records))
+    {
+      xfree (name);
+      return NULL;
+    }
+
+  /* Swap each of the name records.  */
+  for (i = 0; i < name->count; ++i)
+    {
+      sfnt_swap16 (&name->name_records[i].platform_id);
+      sfnt_swap16 (&name->name_records[i].platform_specific_id);
+      sfnt_swap16 (&name->name_records[i].language_id);
+      sfnt_swap16 (&name->name_records[i].name_id);
+      sfnt_swap16 (&name->name_records[i].length);
+      sfnt_swap16 (&name->name_records[i].offset);
+    }
+
+  /* Now, read the name data.  */
+
+  if (name->string_offset > directory->length)
+    {
+      xfree (name);
+      return NULL;
+    }
+
+  required = directory->length - name->string_offset;
+
+  /* It can happen that the string offset comes before the name
+     records, and as a result exceeds the number of bytes
+     previously allocated.  Extend name if that is the case.  */
+
+  if (required > (directory->length
+                 - (name->count
+                    * sizeof *name->name_records)))
+    {
+      name = xrealloc (name, (sizeof *name
+                             + (name->count
+                                * sizeof *name->name_records)
+                             + required));
+      name->name_records = (struct sfnt_name_record *) (name + 1);
+    }
+
+  /* There is enough space past name->name_records to hold REQUIRED
+     bytes.  Seek to the right offset.  */
+
+  if (lseek (fd, directory->offset + name->string_offset,
+            SEEK_SET) == (off_t) -1)
+    {
+      xfree (name);
+      return NULL;
+    }
+
+  /* Read REQURIED bytes into the string data.  */
+  name->data = (unsigned char *) (name->name_records
+                                 + name->count);
+  rc = read (fd, name->data, required);
+  if (rc < required)
+    {
+      xfree (name);
+      return NULL;
+    }
+
+  /* Now validate each of the name records.  */
+  for (i = 0; i < name->count; ++i)
+    {
+      if (((int) name->name_records[i].offset
+          + name->name_records[i].length) > required)
+       {
+         /* The name is out of bounds! */
+         xfree (name);
+         return NULL;
+       }
+    }
+
+  /* Return the name table.  */
+  return name;
+}
+
+/* Return a pointer to the name data corresponding with CODE under the
+   name table NAME.  Return the start of the data and the name record
+   under *RECORD upon success, and NULL otherwise.  */
+
+TEST_STATIC unsigned char *
+sfnt_find_name (struct sfnt_name_table *name,
+               enum sfnt_name_identifier_code code,
+               struct sfnt_name_record *record)
+{
+  int i;
+
+  for (i = 0; i < name->count; ++i)
+    {
+      if (name->name_records[i].name_id == code)
+       {
+         /* The offsets within have already been validated.  */
+         *record = name->name_records[i];
+         return name->data + record->offset;
+       }
+    }
+
+  return NULL;
+}
+
+/* Read the meta table from the given font FD, using the table
+   directory specified as SUBTABLE.  Perform validation on the offsets
+   in each metadata record.  Return NULL upon failure, else the meta
+   table.  */
+
+TEST_STATIC struct sfnt_meta_table *
+sfnt_read_meta_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+  struct sfnt_table_directory *directory;
+  struct sfnt_meta_table *meta;
+  size_t required, i, data_size, map_size, offset;
+  ssize_t rc;
+
+  /* Find the table in the directory.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_META);
+
+  if (!directory)
+    return NULL;
+
+  /* Seek to the location given in the directory.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  /* Figure out the minimum that has to be read.  */
+  required = SFNT_ENDOF (struct sfnt_meta_table,
+                        num_data_maps, uint32_t);
+
+  if (directory->length < required)
+    return NULL;
+
+  /* Allocate enough to hold it.  */
+  meta = xmalloc (sizeof *meta);
+
+  /* Read the header.  */
+  rc = read (fd, meta, required);
+  if (rc < required)
+    {
+      xfree (meta);
+      return NULL;
+    }
+
+  /* Swap what has been read so far.  */
+  sfnt_swap32 (&meta->version);
+  sfnt_swap32 (&meta->flags);
+  sfnt_swap32 (&meta->data_offset);
+  sfnt_swap32 (&meta->num_data_maps);
+
+  /* Make sure the meta is supported.  */
+  if (meta->version != 1)
+    {
+      xfree (meta);
+      return NULL;
+    }
+
+  /* Reallocate the table to hold sizeof *meta + meta->num_data_maps
+     times sizeof meta->data_maps + directory->length bytes.  This is
+     because it is ok for metadata to point into the data map itself,
+     so an unswapped copy of the whole meta contents must be
+     retained.  */
+
+  if (INT_MULTIPLY_WRAPV (sizeof *meta->data_maps, meta->num_data_maps,
+                         &map_size)
+      /* Do so while checking for overflow from bad sfnt files.  */
+      || INT_ADD_WRAPV (map_size, sizeof *meta, &data_size)
+      || INT_ADD_WRAPV (data_size, directory->length, &data_size))
+    {
+      xfree (meta);
+      return NULL;
+    }
+
+  /* Do the reallocation.  */
+  meta = xrealloc (meta, data_size);
+
+  /* Check that the remaining data is big enough to hold the data
+     maps.  */
+  if (directory->length - required < map_size)
+    {
+      xfree (meta);
+      return NULL;
+    }
+
+  /* Set pointers to data_maps and data.  */
+  meta->data_maps = (struct sfnt_meta_data_map *) (meta + 1);
+  meta->data = (unsigned char *) (meta->data_maps
+                                 + meta->num_data_maps);
+
+  /* Now, seek back.  Read the entire table into meta->data.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    {
+      xfree (meta);
+      return NULL;
+    }
+
+  rc = read (fd, meta->data, directory->length);
+  if (rc < directory->length)
+    {
+      xfree (meta);
+      return NULL;
+    }
+
+  /* Copy the data maps into meta->data_maps and swap them one by
+     one.  */
+  memcpy (meta->data_maps, meta->data + required,
+         map_size);
+
+  for (i = 0; i < meta->num_data_maps; ++i)
+    {
+      sfnt_swap32 (&meta->data_maps[i].tag);
+      sfnt_swap32 (&meta->data_maps[i].data_offset);
+      sfnt_swap32 (&meta->data_maps[i].data_length);
+
+      /* Verify the data offsets.  Overflow checking is particularly
+        important here.  */
+
+      if (INT_ADD_WRAPV (meta->data_maps[i].data_offset,
+                        meta->data_maps[i].data_length,
+                        &offset))
+       {
+         xfree (meta);
+         return NULL;
+       }
+
+      if (offset > directory->length)
+       {
+         xfree (meta);
+         return NULL;
+       }
+    }
+
+  /* All done.  */
+  return meta;
+}
+
+/* Return a pointer to the metadata corresponding to TAG under the
+   meta table META.  Return the start of the data and the metadata map
+   under *MAP upon success, and NULL otherwise.  */
+
+MAYBE_UNUSED TEST_STATIC char *
+sfnt_find_metadata (struct sfnt_meta_table *meta,
+                   enum sfnt_meta_data_tag tag,
+                   struct sfnt_meta_data_map *map)
+{
+  int i;
+
+  for (i = 0; i < meta->num_data_maps; ++i)
+    {
+      if (meta->data_maps[i].tag == tag)
+       {
+         *map = meta->data_maps[i];
+         return (char *) meta->data + map->data_offset;
+       }
+    }
+
+  return NULL;
+}
+
+
+
+/* TrueType collection format support.  */
+
+/* Read a TrueType collection header from the font file FD.
+   FD must currently at the start of the file.
+
+   Value is the header upon success, else NULL.  */
+
+TEST_STATIC struct sfnt_ttc_header *
+sfnt_read_ttc_header (int fd)
+{
+  struct sfnt_ttc_header *ttc;
+  size_t size, i;
+  ssize_t rc;
+
+  /* First, allocate only as much as required.  */
+
+  ttc = xmalloc (sizeof *ttc);
+
+  /* Read the version 1.0 data.  */
+
+  size = SFNT_ENDOF (struct sfnt_ttc_header, num_fonts,
+                    uint32_t);
+  rc = read (fd, ttc, size);
+  if (rc < size)
+    {
+      xfree (ttc);
+      return NULL;
+    }
+
+  /* Now swap what was read.  */
+  sfnt_swap32 (&ttc->ttctag);
+  sfnt_swap32 (&ttc->version);
+  sfnt_swap32 (&ttc->num_fonts);
+
+  /* Verify that the tag is as expected.  */
+  if (ttc->ttctag != SFNT_TTC_TTCF)
+    {
+      xfree (ttc);
+      return NULL;
+    }
+
+  /* Now, read the variable length data.  Make sure to check for
+     overflow.  */
+
+  if (INT_MULTIPLY_WRAPV (ttc->num_fonts,
+                         sizeof *ttc->offset_table,
+                         &size))
+    {
+      xfree (ttc);
+      return NULL;
+    }
+
+  ttc = xrealloc (ttc, sizeof *ttc + size);
+  ttc->offset_table = (uint32_t *) (ttc + 1);
+  rc = read (fd, ttc->offset_table, size);
+  if (rc < size)
+    {
+      xfree (ttc);
+      return NULL;
+    }
+
+  /* Swap each of the offsets read.  */
+  for (i = 0; i < ttc->num_fonts; ++i)
+    sfnt_swap32 (&ttc->offset_table[i]);
+
+  /* Now, look at the version.  If it is earlier than 2.0, then
+     reading is finished.  */
+
+  if (ttc->version < 0x00020000)
+    return ttc;
+
+  /* If it is 2.0 or later, then continue to read ul_dsig_tag to
+     ul_dsig_offset.  */
+
+  size = (SFNT_ENDOF (struct sfnt_ttc_header, ul_dsig_offset,
+                     uint32_t)
+         - offsetof (struct sfnt_ttc_header, ul_dsig_tag));
+  rc = read (fd, &ttc->ul_dsig_offset, size);
+  if (rc < size)
+    {
+      xfree (ttc);
+      return NULL;
+    }
+
+  /* Swap what was read.  */
+  sfnt_swap32 (&ttc->ul_dsig_tag);
+  sfnt_swap32 (&ttc->ul_dsig_length);
+  sfnt_swap32 (&ttc->ul_dsig_offset);
+
+  /* All done.  */
+  return ttc;
+}
+
+
+
+/* TrueType hinting support.
+
+   If you do not read the code in this section in conjunction with
+   Apple's TrueType Reference Manual, you will get very confused!
+
+   TrueType fonts don't provide simple hinting meta data, unlike Type
+   2 or CFF fonts.
+
+   Instead, they come with a ``font program'', a bytecode program
+   which is executed upon loading the font, a ``control value
+   program'', executed upon font metrics changing, and then a ``glyph
+   program'' for each glyph, which is run to fit its glyph after
+   scaling.
+
+   The virtual machine which runs this bytecode is arranged as such:
+
+   Firstly, there is a set of registers known as the ``graphics
+   state''.  Each time the point size of a font changes, the ``control
+   value program'' is run to establish the default values of the
+   ``graphics state''.  Then, before each glyph program is run, the
+   ``graphics state'' is set back to the default values.
+
+   Secondly, there is an address space which contains all instructions
+   being run for the current program, which is addressed by the
+   interpreter through its program counter and also by the
+   instructions which push data on to the stack.
+
+   Thirdly, there is a single stack, from which most instructions take
+   their operands and store data.
+
+   Then, there is some memory set aside for each font, the ``storage
+   area'', which is addressed through the RS[] and WS[] instructions,
+   and a ``control value table'', which is the `cvt ' table of the
+   font.
+
+   And finally, there is a ``glyph zone'' which holds points from a
+   scaled glyph outline, and a ``twilight zone'', which holds points
+   used by the font program itself.  Both are addressed indirectly
+   through one of three ``zone pointer'' registers, and are accessible
+   only when a program is being run on behalf of a glyph.  */
+
+
+
+/* Functions for reading tables used by the TrueType interpreter.  */
+
+/* Read the cvt table (control value table) from the given font FD,
+   using the table directory specified as SUBTABLE.  Swap all values
+   in the control value table.  Return NULL upon failure, else the cvt
+   table.  */
+
+TEST_STATIC struct sfnt_cvt_table *
+sfnt_read_cvt_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+  struct sfnt_table_directory *directory;
+  size_t required, i;
+  ssize_t rc;
+  struct sfnt_cvt_table *cvt;
+
+  /* Find the table in the directory.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_CVT );
+
+  if (!directory)
+    return NULL;
+
+  /* Seek to the location given in the directory.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  /* Figure out the minimum amount that has to be read.  */
+  if (INT_ADD_WRAPV (sizeof *cvt, directory->length, &required))
+    return NULL;
+
+  /* Allocate enough for that much data.  */
+  cvt = xmalloc (required);
+
+  /* Now set cvt->num_elements as appropriate, and make cvt->values
+     point into the values.  */
+  cvt->num_elements = directory->length / 2;
+  cvt->values = (sfnt_fword *) (cvt + 1);
+
+  /* Read into cvt.  */
+  rc = read (fd, cvt->values, directory->length);
+  if (rc != directory->length)
+    {
+      xfree (cvt);
+      return NULL;
+    }
+
+  /* Swap each element in the control value table.  */
+  for (i = 0; i < cvt->num_elements; ++i)
+    sfnt_swap16 (&cvt->values[i]);
+
+  /* All done.  */
+  return cvt;
+}
+
+/* Read the fpgm table from the given font FD, using the table
+   directory specified as SUBTABLE.  Value is NULL upon failure, else
+   the fpgm table.  */
+
+TEST_STATIC struct sfnt_fpgm_table *
+sfnt_read_fpgm_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+  struct sfnt_table_directory *directory;
+  size_t required;
+  ssize_t rc;
+  struct sfnt_fpgm_table *fpgm;
+
+  /* Find the table in the directory.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_FPGM);
+
+  if (!directory)
+    return NULL;
+
+  /* Seek to the location given in the directory.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  /* Figure out the minimum amount that has to be read.  */
+  if (INT_ADD_WRAPV (sizeof *fpgm, directory->length, &required))
+    return NULL;
+
+  /* Allocate enough for that much data.  */
+  fpgm = xmalloc (sizeof *fpgm + directory->length);
+
+  /* Now set fpgm->num_instructions as appropriate, and make
+     fpgm->instructions point to the right place.  */
+
+  fpgm->num_instructions = directory->length;
+  fpgm->instructions = (unsigned char *) (fpgm + 1);
+
+  /* Read into fpgm.  */
+  rc = read (fd, fpgm->instructions, directory->length);
+  if (rc != directory->length)
+    {
+      xfree (fpgm);
+      return NULL;
+    }
+
+  /* All done.  */
+  return fpgm;
+}
+
+/* Read the prep table from the given font FD, using the table
+   directory specified as SUBTABLE.  Value is NULL upon failure, else
+   the prep table.  */
+
+TEST_STATIC struct sfnt_prep_table *
+sfnt_read_prep_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+  struct sfnt_table_directory *directory;
+  size_t required;
+  ssize_t rc;
+  struct sfnt_prep_table *prep;
+
+  /* Find the table in the directory.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_PREP);
+
+  if (!directory)
+    return NULL;
+
+  /* Seek to the location given in the directory.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  /* Figure out the minimum amount that has to be read.  */
+  if (INT_ADD_WRAPV (sizeof *prep, directory->length, &required))
+    return NULL;
+
+  /* Allocate enough for that much data.  */
+  prep = xmalloc (sizeof *prep + directory->length);
+
+  /* Now set prep->num_instructions as appropriate, and make
+     prep->instructions point to the right place.  */
+
+  prep->num_instructions = directory->length;
+  prep->instructions = (unsigned char *) (prep + 1);
+
+  /* Read into prep.  */
+  rc = read (fd, prep->instructions, directory->length);
+  if (rc != directory->length)
+    {
+      xfree (prep);
+      return NULL;
+    }
+
+  /* All done.  */
+  return prep;
+}
+
+
+
+/* Interpreter execution environment.  */
+
+/* Divide the specified two 26.6 fixed point numbers X and Y.
+   Return the result.  */
+
+static sfnt_f26dot6
+sfnt_div_f26dot6 (sfnt_f26dot6 x, sfnt_f26dot6 y)
+{
+#ifdef INT64_MAX
+  int64_t result;
+
+  result = ((int64_t) x * 64) / y;
+
+  return result;
+#else
+  int sign;
+  unsigned int a, b;
+
+  sign = 1;
+
+  if (x < 0)
+    sign = -sign;
+
+  if (y < 0)
+    sign = -sign;
+
+  a = abs (x);
+  b = abs (y);
+
+  return sfnt_multiply_divide (a, 64, b) * sign;
+#endif
+}
+
+/* Multiply the specified two 26.6 fixed point numbers A and B.
+   Return the result, or an undefined value upon overflow.  */
+
+static sfnt_f26dot6
+sfnt_mul_f26dot6 (sfnt_f26dot6 a, sfnt_f26dot6 b)
+{
+#ifdef INT64_MAX
+  int64_t product;
+
+  product = (int64_t) a * (int64_t) b;
+
+  /* This can be done quickly with int64_t.  */
+  return product / (int64_t) 64;
+#else
+  int sign;
+
+  sign = 1;
+
+  if (a < 0)
+    sign = -sign;
+
+  if (b < 0)
+    sign = -sign;
+
+  return sfnt_multiply_divide (abs (a), abs (b),
+                              64) * sign;
+#endif
+}
+
+/* Multiply the specified 2.14 number with another signed 32 bit
+   number.  Return the result as a signed 32 bit number.  */
+
+static int32_t
+sfnt_mul_f2dot14 (sfnt_f2dot14 a, int32_t b)
+{
+#ifdef INT64_MAX
+  int64_t product;
+
+  product = (int64_t) a * (int64_t) b;
+
+  return product / (int64_t) 16384;
+#else
+  int sign;
+
+  sign = 1;
+
+  if (a < 0)
+    sign = -sign;
+
+  if (b < 0)
+    sign = -sign;
+
+  return sfnt_multiply_divide (abs (a), abs (b),
+                              16384) * sign;
+#endif
+}
+
+/* Multiply the specified 26.6 fixed point number X by the specified
+   16.16 fixed point number Y with symmetric rounding.
+
+   The 26.6 fixed point number must fit inside -32768 to 32767.ffff.
+   Value is otherwise undefined.  */
+
+static sfnt_f26dot6
+sfnt_mul_f26dot6_fixed (sfnt_f26dot6 x, sfnt_fixed y)
+{
+#ifdef INT64_MAX
+  uint64_t product;
+  int sign;
+
+  sign = 1;
+
+  if (x < 0)
+    {
+      x = -x;
+      sign = -sign;
+    }
+
+  if (y < 0)
+    {
+      y = -y;
+      sign = -sign;
+    }
+
+  product = (uint64_t) y * (uint64_t) x;
+
+  /* This can be done quickly with int64_t.  */
+  return ((int64_t) (product + 32676) / (int64_t) 65536) * sign;
+#else
+  struct sfnt_large_integer temp;
+  int sign;
+
+  sign = 1;
+
+  if (x < 0)
+    sign = -sign;
+
+  if (y < 0)
+    sign = -sign;
+
+  sfnt_multiply_divide_1 (abs (x), abs (y), &temp);
+  sfnt_large_integer_add (&temp, 32676);
+  return sfnt_multiply_divide_2 (&temp, 65536) * sign;
+#endif
+}
+
+/* Return the floor of the specified 26.6 fixed point value X.  */
+
+static sfnt_f26dot6
+sfnt_floor_f26dot6 (sfnt_f26dot6 x)
+{
+  return x & 037777777700;
+}
+
+/* Return the ceiling of the specified 26.6 fixed point value X.  */
+
+static sfnt_f26dot6
+sfnt_ceil_f26dot6 (sfnt_f26dot6 x)
+{
+  return (x + 077) & ~077;
+}
+
+/* Return the 26.6 fixed point value X rounded to the nearest integer
+   value.  */
+
+static sfnt_f26dot6
+sfnt_round_f26dot6 (sfnt_f26dot6 x)
+{
+  /* Add 0.5.  */
+  x += 040;
+
+  /* Remove the fractional.  */
+  return x & ~077;
+}
+
+/* Needed by sfnt_init_graphics_state.  */
+
+static void sfnt_validate_gs (struct sfnt_graphics_state *);
+
+/* Set up default values for the interpreter graphics state.  Return
+   them in STATE.  */
+
+static void
+sfnt_init_graphics_state (struct sfnt_graphics_state *state)
+{
+  state->auto_flip = true;
+  state->cvt_cut_in = 0104;
+  state->delta_base = 9;
+  state->delta_shift = 3;
+  state->dual_projection_vector.x = 040000; /* 1.0 */
+  state->dual_projection_vector.y = 0;
+  state->freedom_vector.x = 040000; /* 1.0 */
+  state->freedom_vector.y = 0;
+  state->instruct_control = 0;
+  state->loop = 1;
+  state->minimum_distance = 0100;
+  state->projection_vector.x = 040000; /* 1.0 */
+  state->projection_vector.y = 0;
+  state->round_state = 1;
+  state->rp0 = 0;
+  state->rp1 = 0;
+  state->rp2 = 0;
+  state->scan_control = 0;
+  state->sw_cut_in = 0;
+  state->single_width_value = 0;
+  state->zp0 = 1;
+  state->zp1 = 1;
+  state->zp2 = 1;
+
+  /* Validate the graphics state.  */
+  sfnt_validate_gs (state);
+}
+
+/* Set up an interpreter to be used with a font.  Use the resource
+   limits specified in the MAXP table, the values specified in the
+   CVT, HEAD and FVAR tables, the pixel size PIXEL_SIZE, and the point
+   size POINT_SIZE.  CVT may be NULL, in which case the interpreter
+   will not have access to a control value table.
+
+   POINT_SIZE should be PIXEL_SIZE, converted to 1/72ths of an inch.
+
+   Value is the interpreter, with all state initialized to default
+   values, or NULL upon failure.  */
+
+TEST_STATIC struct sfnt_interpreter *
+sfnt_make_interpreter (struct sfnt_maxp_table *maxp,
+                      struct sfnt_cvt_table *cvt,
+                      struct sfnt_head_table *head,
+                      struct sfnt_fvar_table *fvar,
+                      int pixel_size, int point_size)
+{
+  size_t size, temp, i, storage_size, pad;
+  struct sfnt_interpreter *interpreter;
+
+  /* Detect CFF maxp tables.  */
+  if (maxp->version != 0x00010000)
+    return NULL;
+
+  /* Use the contents of the MAXP table to determine the size of the
+     interpreter structure.  */
+  size = sizeof (*interpreter);
+
+  /* Add program stack.  */
+  if (INT_ADD_WRAPV ((maxp->max_stack_elements
+                     * sizeof *interpreter->stack),
+                    size, &size))
+    return NULL;
+
+  /* Add twilight zone.  */
+
+  if (INT_ADD_WRAPV ((maxp->max_twilight_points
+                     * sizeof *interpreter->twilight_x),
+                    size, &size))
+    return NULL;
+
+  if (INT_ADD_WRAPV ((maxp->max_twilight_points
+                     * sizeof *interpreter->twilight_y),
+                    size, &size))
+    return NULL;
+
+  if (INT_ADD_WRAPV ((maxp->max_twilight_points
+                     * sizeof *interpreter->twilight_y),
+                    size, &size))
+    return NULL;
+
+  if (INT_ADD_WRAPV ((maxp->max_twilight_points
+                     * sizeof *interpreter->twilight_y),
+                    size, &size))
+    return NULL;
+
+  /* Add the storage area.  */
+  storage_size = maxp->max_storage * sizeof *interpreter->storage;
+  if (INT_ADD_WRAPV (storage_size, size, &size))
+    return NULL;
+
+  /* Add padding for the storage area.  */
+  pad = alignof (struct sfnt_interpreter_definition);
+  pad -= size & (pad - 1);
+  if (INT_ADD_WRAPV (pad, size, &size))
+    return NULL;
+
+  /* Add function and instruction definitions.  */
+  if (INT_ADD_WRAPV ((((int) maxp->max_instruction_defs
+                      + maxp->max_function_defs)
+                     * sizeof *interpreter->function_defs),
+                    size, &size))
+    return NULL;
+
+  /* Add control value table.  */
+
+  if (cvt)
+    {
+      if (INT_MULTIPLY_WRAPV (cvt->num_elements,
+                             sizeof *interpreter->cvt,
+                             &temp)
+         || INT_ADD_WRAPV (temp, size, &size))
+       return NULL;
+    }
+
+  /* Allocate the interpreter.  */
+  interpreter = xmalloc (size);
+
+#ifdef TEST
+  interpreter->run_hook = NULL;
+  interpreter->push_hook = NULL;
+  interpreter->pop_hook = NULL;
+#endif
+
+  /* Fill in pointers and default values.  */
+  interpreter->max_stack_elements = maxp->max_stack_elements;
+  interpreter->num_instructions = 0;
+  interpreter->IP = 0;
+  interpreter->storage_size = maxp->max_storage;
+  interpreter->function_defs_size = maxp->max_function_defs;
+  interpreter->instruction_defs_size = maxp->max_instruction_defs;
+  interpreter->twilight_zone_size = maxp->max_twilight_points;
+  interpreter->scale = 0; /* This should be set later.  */
+
+  interpreter->stack = (uint32_t *) (interpreter + 1);
+  interpreter->SP = interpreter->stack;
+  interpreter->instructions = NULL;
+  interpreter->twilight_x
+    = (sfnt_f26dot6 *) (interpreter->stack
+                       + maxp->max_stack_elements);
+  interpreter->twilight_y = (interpreter->twilight_x
+                            + maxp->max_twilight_points);
+  interpreter->twilight_original_x = (interpreter->twilight_y
+                                     + maxp->max_twilight_points);
+  interpreter->twilight_original_y
+    = (interpreter->twilight_original_x
+       + maxp->max_twilight_points);
+  interpreter->glyph_zone = NULL;
+  interpreter->advance_width = 0;
+  interpreter->storage
+    = (uint32_t *) (interpreter->twilight_original_y
+                   + maxp->max_twilight_points);
+  interpreter->function_defs
+    = (struct sfnt_interpreter_definition *) (interpreter->storage
+                                             + maxp->max_storage);
+  interpreter->function_defs
+    = ((struct sfnt_interpreter_definition *)
+       ((unsigned char *) interpreter->function_defs + pad));
+  interpreter->instruction_defs = (interpreter->function_defs
+                                  + maxp->max_function_defs);
+  interpreter->cvt
+    = (sfnt_f26dot6 *) (interpreter->instruction_defs
+                       + maxp->max_instruction_defs);
+
+  if (cvt)
+    interpreter->cvt_size = cvt->num_elements;
+  else
+    interpreter->cvt_size = 0;
+
+  /* Now compute the scale.  Then, scale up the control value table
+     values.  */
+  interpreter->scale
+    = sfnt_div_fixed (pixel_size, head->units_per_em);
+
+  /* Set the PPEM.  */
+  interpreter->ppem = pixel_size;
+  interpreter->point_size = point_size;
+
+  /* Zero out the interpreter state from the stack to the end of the
+     instruction definitions.  */
+  memset (interpreter->stack, 0, size - sizeof *interpreter);
+
+  /* Initialize the interpreter graphics state.  */
+  sfnt_init_graphics_state (&interpreter->state);
+
+  /* Load the control value table.  */
+  for (i = 0; i < interpreter->cvt_size; ++i)
+    interpreter->cvt[i]
+      = sfnt_mul_f26dot6_fixed (cvt->values[i] * 64,
+                               interpreter->scale);
+
+  /* Fill in the default values for phase, period and threshold.  */
+  interpreter->period = 64;
+  interpreter->phase = 0;
+  interpreter->threshold = 0;
+
+  /* Fill in the current call depth.  */
+  interpreter->call_depth = 0;
+
+  /* Clear variation axes.  They will be set upon a call to
+     `sfnt_vary_interpreter'.  */
+  interpreter->n_axis = 0;
+  interpreter->norm_coords = NULL;
+
+  /* Set n_axis now if a fvar table was provided.  This way, GXAXIS
+     pushes the correct number of values even if no blend is
+     provided.  */
+
+  if (fvar)
+    interpreter->n_axis = fvar->axis_count;
+
+  /* Return the interpreter.  */
+  return interpreter;
+}
+
+/* These enums are used to determine why the interpreter is being
+   run.  They have the following meanings:
+
+     - The interpreter is being run to interpret a font program.
+     - The interpreter is being run to interpret the control
+       value program.
+     - The interpreter is being run to fit a glyph.
+     - The interpreter is being run as part of an automated test.  */
+
+enum sfnt_interpreter_run_context
+  {
+    SFNT_RUN_CONTEXT_FONT_PROGRAM,
+    SFNT_RUN_CONTEXT_CONTROL_VALUE_PROGRAM,
+    SFNT_RUN_CONTEXT_GLYPH_PROGRAM,
+#ifdef TEST
+    SFNT_RUN_CONTEXT_TEST,
+#endif
+  };
+
+/* Cancel execution of the program in INTERPRETER with the specified
+   error REASON and reset the loop counter to 1.
+
+   After this is called, it is probably okay to reuse INTERPRETER.
+   However, instructions must always be reloaded.  */
+
+_Noreturn static void
+sfnt_interpret_trap (struct sfnt_interpreter *interpreter,
+                    const char *reason)
+{
+  interpreter->trap_reason = reason;
+  interpreter->call_depth = 0;
+  interpreter->state.loop = 1;
+  longjmp (interpreter->trap, 1);
+}
+
+#define STACKSIZE()                            \
+  (interpreter->SP - interpreter->stack)
+
+#define TRAP(why)                              \
+  sfnt_interpret_trap (interpreter, (why))
+
+#define MOVE(a, b, n)                          \
+  memmove (a, b, (n) * sizeof (uint32_t))
+
+#define CHECK_STACK_ELEMENTS(n)                        \
+  {                                            \
+    if ((interpreter->SP                       \
+        - interpreter->stack) < n)             \
+      TRAP ("stack underflow");                        \
+  }
+
+#define CHECK_STACK_AVAILABLE(n)               \
+  {                                            \
+    char *stack_end;                           \
+                                               \
+    stack_end                                  \
+      = (char *) interpreter->twilight_x;      \
+    if (((char *) (interpreter->SP + (n))      \
+        > stack_end))                          \
+      TRAP ("stack overflow");                 \
+  }
+
+#define CHECK_PREP()                           \
+  if (!is_prep)                                        \
+    TRAP ("instruction executed not valid"     \
+         " outside control value program")     \
+
+#define sfnt_add(a, b)                         \
+  ((int) ((unsigned int) (a) + (unsigned int) (b)))
+
+#define sfnt_sub(a, b)                         \
+  ((int) ((unsigned int) (a) - (unsigned int) (b)))
+
+#define sfnt_mul(a, b)                         \
+  ((int) ((unsigned int) (a) * (unsigned int) (b)))
+
+
+
+/* Register, alu and logic instructions.  */
+
+#ifndef TEST
+
+#define POP()                                  \
+  (interpreter->SP == interpreter->stack       \
+   ? (TRAP ("stack underflow"), 0)             \
+   : (*(--interpreter->SP)))
+
+#define POP_UNCHECKED() (*(--interpreter->SP))
+
+#else
+
+#define POP()                                  \
+  (interpreter->SP == interpreter->stack       \
+   ? (TRAP ("stack underflow"), 0)             \
+   : ({uint32_t _value;                                \
+       _value = *(--interpreter->SP);          \
+       if (interpreter->pop_hook)              \
+        interpreter->pop_hook (interpreter,    \
+                               _value);        \
+       _value;}))
+
+#define POP_UNCHECKED()        POP ()
+
+#endif
+
+#define LOOK()                                 \
+  (interpreter->SP == interpreter->stack       \
+   ? (TRAP ("stack underflow"), 0)             \
+   : *(interpreter->SP - 1))
+
+#if !defined TEST || !0
+
+#define PUSH(value)                            \
+  {                                            \
+    if ((char *) (interpreter->SP + 1)         \
+       > (char *) interpreter->twilight_x)     \
+      TRAP ("stack overflow");                 \
+                                               \
+    *interpreter->SP = (value);                        \
+    interpreter->SP++;                         \
+  }
+
+#define PUSH_UNCHECKED(value)                  \
+  {                                            \
+    *interpreter->SP = (value);                        \
+    interpreter->SP++;                         \
+  }
+
+#else /* TEST && 0 */
+
+#define PUSH(value)                            \
+  {                                            \
+    if ((char *) (interpreter->SP + 1)         \
+       > (char *) interpreter->twilight_x)     \
+      TRAP ("stack overflow");                 \
+                                               \
+    if (interpreter->push_hook)                        \
+      interpreter->push_hook (interpreter,     \
+                             value);           \
+                                               \
+    *interpreter->SP = value;                  \
+    interpreter->SP++;                         \
+  }
+
+#define PUSH_UNCHECKED(value) PUSH (value)
+
+#endif /* TEST && 0 */
+
+#define PUSH2_UNCHECKED(high, low)             \
+  {                                            \
+    int16_t word;                              \
+                                               \
+    word = (((int8_t) high) << 8 | low);       \
+    PUSH_UNCHECKED (word);                     \
+  }                                            \
+
+#define SRP0()                                 \
+  {                                            \
+    uint32_t p;                                        \
+                                               \
+    p = POP ();                                        \
+    interpreter->state.rp0 = p;                        \
+  }
+
+#define SRP1()                                 \
+  {                                            \
+    uint32_t p;                                        \
+                                               \
+    p = POP ();                                        \
+    interpreter->state.rp1 = p;                        \
+  }
+
+#define SRP2()                                 \
+  {                                            \
+    uint32_t p;                                        \
+                                               \
+    p = POP ();                                        \
+    interpreter->state.rp2 = p;                        \
+  }
+
+#define SZP0()                                 \
+  {                                            \
+    uint32_t zone;                             \
+                                               \
+    zone = POP ();                             \
+                                               \
+    if (zone > 1)                              \
+      TRAP ("invalid zone");                   \
+                                               \
+    interpreter->state.zp0 = zone;             \
+  }
+
+#define SZP1()                                 \
+  {                                            \
+    uint32_t zone;                             \
+                                               \
+    zone = POP ();                             \
+                                               \
+    if (zone > 1)                              \
+      TRAP ("invalid zone");                   \
+                                               \
+    interpreter->state.zp1 = zone;             \
+  }
+
+#define SZP2()                                 \
+  {                                            \
+    uint32_t zone;                             \
+                                               \
+    zone = POP ();                             \
+                                               \
+    if (zone > 1)                              \
+      TRAP ("invalid zone");                   \
+                                               \
+    interpreter->state.zp2 = zone;             \
+  }
+
+#define SZPS()                                 \
+  {                                            \
+    uint32_t zone;                             \
+                                               \
+    zone = POP ();                             \
+                                               \
+    if (zone > 1)                              \
+      TRAP ("invalid zone");                   \
+                                               \
+    interpreter->state.zp0 = zone;             \
+    interpreter->state.zp1 = zone;             \
+    interpreter->state.zp2 = zone;             \
+  }
+
+#define SLOOP()                                        \
+  {                                            \
+    uint32_t loop;                             \
+                                               \
+    loop = POP ();                             \
+                                               \
+    if (!loop)                                 \
+      TRAP ("loop set to 0");                  \
+                                               \
+    interpreter->state.loop = loop;            \
+  }
+
+#define SMD()                                  \
+  {                                            \
+    sfnt_f26dot6 md;                           \
+                                               \
+    md = POP ();                               \
+                                               \
+    interpreter->state.minimum_distance = md;  \
+  }
+
+#define ELSE()                                 \
+  {                                            \
+    sfnt_interpret_else (interpreter);         \
+    goto skip_step;                            \
+  }
+
+#define JMPR()                                 \
+  {                                            \
+    int32_t offset;                            \
+                                               \
+    offset = POP ();                           \
+                                               \
+    if (interpreter->IP + offset < 0           \
+       || (interpreter->IP + offset            \
+           > interpreter->num_instructions))   \
+      TRAP ("JMPR out of bounds");             \
+                                               \
+    interpreter->IP += offset;                 \
+    goto skip_step;                            \
+  }
+
+#define SCVTCI()                               \
+  {                                            \
+    sfnt_f26dot6 cutin;                                \
+                                               \
+    cutin = POP ();                            \
+                                               \
+    interpreter->state.cvt_cut_in = cutin;     \
+  }
+
+#define SSWCI()                                        \
+  {                                            \
+    sfnt_f26dot6 cutin;                                \
+                                               \
+    cutin = POP ();                            \
+                                               \
+    interpreter->state.sw_cut_in = cutin;      \
+  }
+
+#define SSW()                                  \
+  {                                            \
+    int32_t single_width;                      \
+                                               \
+    single_width = POP ();                     \
+                                               \
+    interpreter->state.single_width_value      \
+      = (interpreter->scale * single_width     \
+        / 1024);                               \
+  }
+
+#define DUP()                                  \
+  {                                            \
+    uint32_t value;                            \
+                                               \
+    value = LOOK ();                           \
+    PUSH (value);                              \
+  }
+
+#define CLEAR()                                        \
+  {                                            \
+    interpreter->SP = interpreter->stack;      \
+  }
+
+#define SWAP()                                 \
+  {                                            \
+    uint32_t a, b;                             \
+                                               \
+    a = POP ();                                        \
+    b = POP ();                                        \
+                                               \
+    PUSH_UNCHECKED (a);                                \
+    PUSH_UNCHECKED (b);                                \
+  }
+
+#define DEPTH()                                        \
+  {                                            \
+    ptrdiff_t diff;                            \
+                                               \
+    diff = (interpreter->SP                    \
+           - interpreter->stack);              \
+    PUSH (diff);                               \
+  }
+
+#define CINDEX()                               \
+  {                                            \
+    int32_t index;                             \
+                                               \
+    index = POP ();                            \
+                                               \
+    if (index <= 0 || index > STACKSIZE ())    \
+      TRAP ("stack overflow");                 \
+                                               \
+    PUSH_UNCHECKED (*(interpreter->SP          \
+                     - index));                \
+  }
+
+#define MINDEX()                               \
+  {                                            \
+    int32_t index, what;                       \
+                                               \
+    index = POP ();                            \
+                                               \
+    if (index <= 0 || index > STACKSIZE ())    \
+      TRAP ("stack overflow");                 \
+                                               \
+    what = *(interpreter->SP - index);         \
+    MOVE (interpreter->SP - index,             \
+         interpreter->SP - index + 1,          \
+         index - 1);                           \
+    *(interpreter->SP - 1) = what;             \
+  }
+
+#define RAW()                                  \
+  {                                            \
+    if (why != SFNT_RUN_CONTEXT_GLYPH_PROGRAM) \
+      TRAP ("Read Advance Width without loaded"        \
+           " glyph");                          \
+    PUSH (interpreter->advance_width);         \
+  }
+
+#define CALL()                                 \
+  {                                            \
+    uint32_t id, i;                            \
+    struct sfnt_interpreter_definition *def;   \
+                                               \
+    id = POP ();                               \
+                                               \
+    for (i = 0;                                        \
+        i < interpreter->function_defs_size;   \
+        ++i)                                   \
+      {                                                \
+       def = &interpreter->function_defs[i];   \
+                                               \
+       if (!def->instructions)                 \
+         TRAP ("invalid function");            \
+                                               \
+       if (def->opcode == id)                  \
+         {                                     \
+           sfnt_interpret_call (def,           \
+                                interpreter,   \
+                                why);          \
+           goto next_instruction;              \
+         }                                     \
+      }                                                \
+                                               \
+    TRAP ("invalid function");                 \
+  }
+
+#define LOOPCALL()                             \
+  {                                            \
+    uint32_t id;                               \
+    int32_t n;                                 \
+    int i;                                     \
+    struct sfnt_interpreter_definition *def;   \
+                                               \
+    id = POP ();                               \
+    n = POP ();                                        \
+                                               \
+    if (n > 65535)                             \
+      TRAP ("invalid LOOPCALL count");         \
+                                               \
+    for (i = 0;                                        \
+        i < interpreter->function_defs_size;   \
+        ++i)                                   \
+      {                                                \
+       def = &interpreter->function_defs[i];   \
+                                               \
+       if (!def->instructions)                 \
+         TRAP ("invalid function");            \
+                                               \
+       if (def->opcode == id)                  \
+         goto loopcall_begin;                  \
+      }                                                \
+                                               \
+    TRAP ("invalid function");                 \
+                                               \
+  loopcall_begin:                              \
+    if (n-- <= 0)                              \
+      break;                                   \
+                                               \
+    sfnt_interpret_call (def, interpreter,     \
+                        why);                  \
+    goto loopcall_begin;                       \
+  }
+
+#define FDEF()                                 \
+  {                                            \
+    if (why == SFNT_RUN_CONTEXT_GLYPH_PROGRAM) \
+      TRAP ("FDEF inside glyph program");      \
+                                               \
+    sfnt_interpret_fdef (interpreter, POP ()); \
+    goto skip_step;                            \
+  }
+
+#define ENDF()                                 \
+  {                                            \
+    TRAP ("stray ENDF");                       \
+  }
+
+#define NPUSHB()                               \
+  {                                            \
+    int b, nbytes, IP;                         \
+    unsigned char *ip;                         \
+                                               \
+    if ((IP = interpreter->IP + 1)             \
+       >= interpreter->num_instructions)       \
+      TRAP ("Missing arg to NPUSHB");          \
+                                               \
+    nbytes                                     \
+      = interpreter->instructions[IP];         \
+                                               \
+    if (IP + 1 + nbytes                                \
+       > interpreter->num_instructions)        \
+      TRAP ("args to NPUSHB lie outside IS");  \
+                                               \
+    CHECK_STACK_AVAILABLE (nbytes);            \
+    ip = interpreter->instructions;            \
+    for (b = IP + 1; b < IP + 1 + nbytes; ++b) \
+      PUSH_UNCHECKED (ip[b]);                  \
+                                               \
+    interpreter->IP += nbytes + 1;             \
+  }
+
+#define NPUSHW()                               \
+  {                                            \
+    int b, nbytes, IP;                         \
+    unsigned char *ip;                         \
+                                               \
+    if ((IP = interpreter->IP + 1)             \
+       >= interpreter->num_instructions)       \
+      TRAP ("Missing arg to NPUSHW");          \
+                                               \
+    nbytes                                     \
+      = interpreter->instructions[IP] * 2;     \
+                                               \
+    if (IP + 1 + nbytes                                \
+       > interpreter->num_instructions)        \
+      TRAP ("args to NPUSHW lie outside IS");  \
+                                               \
+    CHECK_STACK_AVAILABLE (nbytes / 2);                \
+    ip = interpreter->instructions;            \
+    for (b = IP + 1; b < IP + 1 + nbytes;      \
+        b += 2)                                \
+      PUSH2_UNCHECKED (ip[b], ip[b + 1]);      \
+                                               \
+    interpreter->IP += nbytes + 1;             \
+  }
+
+#define WS()                                   \
+  {                                            \
+    uint32_t address, value;                   \
+                                               \
+    value = POP ();                            \
+    address = POP ();                          \
+                                               \
+    if (address >= interpreter->storage_size)  \
+      TRAP ("invalid WS");                     \
+                                               \
+    interpreter->storage[address] = value;     \
+  }
+
+#define RS()                                   \
+  {                                            \
+    uint32_t address, value;                   \
+                                               \
+    address = POP ();                          \
+                                               \
+    if (address >= interpreter->storage_size)  \
+      TRAP ("invalid RS");                     \
+                                               \
+    value = interpreter->storage[address];     \
+    PUSH_UNCHECKED (value);                    \
+  }
+
+#define WCVTP()                                        \
+  {                                            \
+    sfnt_f26dot6 value;                                \
+    uint32_t location;                         \
+                                               \
+    value = POP ();                            \
+    location = POP ();                         \
+                                               \
+    if (location >= interpreter->cvt_size)     \
+      TRAP ("WCVTP out of bounds");            \
+                                               \
+    interpreter->cvt[location] = value;                \
+  }
+
+#define RCVT()                                 \
+  {                                            \
+    sfnt_f26dot6 value;                                \
+    uint32_t location;                         \
+                                               \
+    location = POP ();                         \
+                                               \
+    if (location >= interpreter->cvt_size)     \
+      TRAP ("out of bounds RCVT");             \
+                                               \
+    value = interpreter->cvt[location];                \
+    PUSH_UNCHECKED (value);                    \
+  }
+
+#define MPPEM()                                        \
+  {                                            \
+    PUSH (interpreter->ppem);                  \
+  }
+
+#define MPS()                                  \
+  {                                            \
+    PUSH (interpreter->point_size);            \
+  }
+
+#define FLIPON()                               \
+  {                                            \
+    interpreter->state.auto_flip = true;       \
+  }
+
+#define FLIPOFF()                              \
+  {                                            \
+    interpreter->state.auto_flip = false;      \
+  }
+
+#define DEBUG()                                        \
+  {                                            \
+    POP (); /* Value is ignored.  */           \
+  }
+
+#define LT()                                   \
+  {                                            \
+    int32_t e1, e2;                            \
+                                               \
+    e2 = POP ();                               \
+    e1 = POP ();                               \
+                                               \
+    PUSH_UNCHECKED (e1 < e2 ? 1 : 0);          \
+  }
+
+#define LTEQ()                                 \
+  {                                            \
+    int32_t e1, e2;                            \
+                                               \
+    e2 = POP ();                               \
+    e1 = POP ();                               \
+                                               \
+    PUSH_UNCHECKED (e1 <= e2 ? 1 : 0);         \
+  }
+
+#define GT()                                   \
+  {                                            \
+    int32_t e1, e2;                            \
+                                               \
+    e2 = POP ();                               \
+    e1 = POP ();                               \
+                                               \
+    PUSH_UNCHECKED (e1 > e2 ? 1 : 0);          \
+  }
+
+#define GTEQ()                                 \
+  {                                            \
+    int32_t e1, e2;                            \
+                                               \
+    e2 = POP ();                               \
+    e1 = POP ();                               \
+                                               \
+    PUSH_UNCHECKED (e1 >= e2 ? 1 : 0);         \
+  }
+
+#define EQ()                                   \
+  {                                            \
+    uint32_t e1, e2;                           \
+                                               \
+    e1 = POP ();                               \
+    e2 = POP ();                               \
+                                               \
+    PUSH_UNCHECKED (e1 == e2 ? 1 : 0);         \
+  }
+
+#define NEQ()                                  \
+  {                                            \
+    uint32_t e1, e2;                           \
+                                               \
+    e1 = POP ();                               \
+    e2 = POP ();                               \
+                                               \
+    PUSH_UNCHECKED (e1 != e2 ? 1 : 0);         \
+  }
+
+#define ODD()                                  \
+  {                                            \
+    sfnt_f26dot6 e1, result;                   \
+                                               \
+    e1 = POP ();                               \
+    result = abs (e1);                         \
+                                               \
+    result                                     \
+      = interpreter->state.round (result,      \
+                                 interpreter); \
+    PUSH_UNCHECKED (((result & 127)            \
+                    == 64) ? 1 : 0);           \
+  }
+
+#define EVEN()                                 \
+  {                                            \
+    sfnt_f26dot6 e1, result;                   \
+    uint32_t value;                            \
+                                               \
+    e1 = POP ();                               \
+    result = abs (e1);                         \
+                                               \
+    result                                     \
+      = interpreter->state.round (result,      \
+                                 interpreter); \
+    value = ((result & 127) == 64) ? 0 : 1;    \
+    PUSH_UNCHECKED (value);                    \
+  }
+
+#define IF()                                   \
+  {                                            \
+    uint32_t condition;                                \
+                                               \
+    condition = POP ();                                \
+    sfnt_interpret_if (interpreter, condition);        \
+    goto skip_step;                            \
+  }
+
+#define EIF()                                  \
+  {                                            \
+                                               \
+  }
+
+#define AND()                                  \
+  {                                            \
+    uint32_t e1, e2;                           \
+                                               \
+    e1 = POP ();                               \
+    e2 = POP ();                               \
+                                               \
+    PUSH_UNCHECKED (e1 && e2 ? 1 : 0);         \
+  }
+
+#define OR()                                   \
+  {                                            \
+    uint32_t e1, e2;                           \
+                                               \
+    e1 = POP ();                               \
+    e2 = POP ();                               \
+                                               \
+    PUSH_UNCHECKED (e1 || e2 ? 1 : 0);         \
+  }
+
+#define NOT()                                  \
+  {                                            \
+    uint32_t e1;                               \
+                                               \
+    e1 = POP ();                               \
+                                               \
+    PUSH_UNCHECKED (!e1 ? 1 : 0);              \
+  }
+
+#define SDB()                                  \
+  {                                            \
+    uint32_t base;                             \
+                                               \
+    base = POP ();                             \
+                                               \
+    interpreter->state.delta_base = base;      \
+  }
+
+#define SDS()                                  \
+  {                                            \
+    uint32_t shift;                            \
+                                               \
+    shift = POP ();                            \
+                                               \
+    if (shift > 6)                             \
+      TRAP ("invalid delta shift");            \
+                                               \
+    interpreter->state.delta_shift = shift;    \
+  }
+
+#define ADD()                                  \
+  {                                            \
+    sfnt_f26dot6 n1, n2;                       \
+                                               \
+    n1 = POP ();                               \
+    n2 = POP ();                               \
+                                               \
+    PUSH_UNCHECKED (sfnt_add (n1, n2));                \
+  }
+
+#define SUB()                                  \
+  {                                            \
+    sfnt_f26dot6 n2, n1;                       \
+                                               \
+    n2 = POP ();                               \
+    n1 = POP ();                               \
+                                               \
+    PUSH_UNCHECKED (sfnt_sub (n1, n2));                \
+  }
+
+#define DIV()                                  \
+  {                                            \
+    sfnt_f26dot6 n2, n1;                       \
+                                               \
+    n2 = POP ();                               \
+    n1 = POP ();                               \
+                                               \
+    if (!n2)                                   \
+      TRAP ("DIV by 0");                       \
+                                               \
+    PUSH_UNCHECKED (sfnt_div_f26dot6 (n1, n2));        \
+  }
+
+#define MUL()                                  \
+  {                                            \
+    sfnt_f26dot6 n2, n1;                       \
+                                               \
+    n2 = POP ();                               \
+    n1 = POP ();                               \
+                                               \
+    PUSH_UNCHECKED (sfnt_mul_f26dot6 (n2, n1));        \
+  }
+
+#define ABS()                                  \
+  {                                            \
+    sfnt_f26dot6 n;                            \
+                                               \
+    n = POP ();                                        \
+                                               \
+    if (n == INT32_MIN)                                \
+      PUSH_UNCHECKED (0)                       \
+    else                                       \
+      PUSH_UNCHECKED (n < 0 ? -n : n)          \
+  }
+
+#define NEG()                                  \
+  {                                            \
+    sfnt_f26dot6 n;                            \
+                                               \
+    n = POP ();                                        \
+                                               \
+    if (n == INT32_MIN)                                \
+      PUSH_UNCHECKED (0)                       \
+    else                                       \
+      PUSH_UNCHECKED (-n)                      \
+  }
+
+#define FLOOR()                                        \
+  {                                            \
+    sfnt_f26dot6 n;                            \
+                                               \
+    n = POP ();                                        \
+    PUSH_UNCHECKED (sfnt_floor_f26dot6 (n));   \
+  }
+
+#define CEILING()                              \
+  {                                            \
+    sfnt_f26dot6 n;                            \
+                                               \
+    n = POP ();                                        \
+    PUSH_UNCHECKED (sfnt_ceil_f26dot6 (n));    \
+  }
+
+#define WCVTF()                                        \
+  {                                            \
+    int32_t value;                             \
+    uint32_t location;                         \
+                                               \
+    value = POP ();                            \
+    location = POP ();                         \
+                                               \
+    if (location >= interpreter->cvt_size)     \
+      TRAP ("WCVTF out of bounds");            \
+                                               \
+    interpreter->cvt[location]                 \
+      = (interpreter->scale * value            \
+        / 1024);                               \
+  }
+
+#define JROT()                                 \
+  {                                            \
+    uint32_t e;                                        \
+    int32_t offset;                            \
+                                               \
+    e = POP ();                                        \
+    offset = POP ();                           \
+                                               \
+    if (!e)                                    \
+      break;                                   \
+                                               \
+    if (interpreter->IP + offset < 0           \
+       || (interpreter->IP + offset            \
+           > interpreter->num_instructions))   \
+      TRAP ("JMPR out of bounds");             \
+                                               \
+    interpreter->IP += offset;                 \
+    goto skip_step;                            \
+  }
+
+#define JROF()                                 \
+  {                                            \
+    uint32_t e;                                        \
+    int32_t offset;                            \
+                                               \
+    e = POP ();                                        \
+    offset = POP ();                           \
+                                               \
+    if (e)                                     \
+      break;                                   \
+                                               \
+    if (interpreter->IP + offset < 0           \
+       || (interpreter->IP + offset            \
+           > interpreter->num_instructions))   \
+      TRAP ("JMPR out of bounds");             \
+                                               \
+    interpreter->IP += offset;                 \
+    goto skip_step;                            \
+  }
+
+#define ILLEGAL_INSTRUCTION()                  \
+  {                                            \
+    TRAP ("MS reserved illegal instruction");  \
+  }
+
+#define SCANCTRL()                             \
+  {                                            \
+    uint32_t value;                            \
+                                               \
+    value = POP ();                            \
+    interpreter->state.scan_control = value;   \
+  }
+
+/* Selector bit 8 is undocumented, but present in the Macintosh
+   rasterizer.  02000 is returned if there is a variation axis in
+   use.  */
+
+#define GETINFO()                              \
+  {                                            \
+    uint32_t selector, k;                      \
+                                               \
+    selector = POP ();                         \
+                                               \
+    k = 0;                                     \
+                                               \
+    if (selector & 1)                          \
+      k |= 02;                                 \
+                                               \
+    if (selector & 8                           \
+       && interpreter->norm_coords)            \
+      k |= 02000;                              \
+                                               \
+    PUSH_UNCHECKED (k);                                \
+  }
+
+#define IDEF()                                 \
+  {                                            \
+    if (why == SFNT_RUN_CONTEXT_GLYPH_PROGRAM) \
+      TRAP ("IDEF inside glyph program");      \
+                                               \
+    sfnt_interpret_idef (interpreter, POP ()); \
+    goto skip_step;                            \
+  }
+
+#define ROLL()                                 \
+  {                                            \
+    uint32_t a, b, c;                          \
+                                               \
+    CHECK_STACK_ELEMENTS (3);                  \
+                                               \
+    a = POP_UNCHECKED ();                      \
+    b = POP_UNCHECKED ();                      \
+    c = POP_UNCHECKED ();                      \
+                                               \
+    PUSH_UNCHECKED (b);                                \
+    PUSH_UNCHECKED (a);                                \
+    PUSH_UNCHECKED (c);                                \
+  }
+
+#define _MAX()                                 \
+  {                                            \
+    int32_t e1, e2;                            \
+                                               \
+    e1 = POP ();                               \
+    e2 = POP ();                               \
+                                               \
+    PUSH_UNCHECKED (MAX (e1, e2));             \
+  }
+
+#define _MIN()                                 \
+  {                                            \
+    int32_t e1, e2;                            \
+                                               \
+    e1 = POP ();                               \
+    e2 = POP ();                               \
+                                               \
+    PUSH_UNCHECKED (MIN (e1, e2));             \
+  }
+
+#define SCANTYPE()                             \
+  {                                            \
+    POP ();                                    \
+  }
+
+#define INSTCTRL()                             \
+  {                                            \
+    uint32_t s, v;                             \
+                                               \
+    CHECK_PREP ();                             \
+    s = POP ();                                        \
+    v = POP ();                                        \
+                                               \
+    if (!s || s > 2)                           \
+      break;                                   \
+                                               \
+    interpreter->state.instruct_control                \
+      &= ~(1 << s);                            \
+                                               \
+    if (v)                                     \
+      interpreter->state.instruct_control      \
+       |= (1 << s);                            \
+  }
+
+/* GXAXIS is undocumented.  It seems to return each axis in shortFrac
+   format.  */
+
+#define GXAXIS()                               \
+  {                                            \
+    uint32_t v;                                        \
+    int i;                                     \
+                                               \
+    for (i = 0; i < interpreter->n_axis; ++i)  \
+      {                                                \
+       if (interpreter->norm_coords)           \
+         v = interpreter->norm_coords[i] / 4;  \
+       else                                    \
+         v = 0;                                \
+                                               \
+       PUSH (v);                               \
+      }                                                \
+  }
+
+#define PUSHB()                                        \
+  {                                            \
+    int b, nbytes, IP;                         \
+    unsigned char *ip;                         \
+                                               \
+    IP = interpreter->IP;                      \
+    nbytes = opcode - 0xb0 + 1;                        \
+                                               \
+    if (IP + nbytes + 1                                \
+       > interpreter->num_instructions)        \
+      TRAP ("args to PUSHB lie outside IS");   \
+                                               \
+    CHECK_STACK_AVAILABLE (nbytes);            \
+    ip = interpreter->instructions;            \
+    for (b = IP + 1; b < IP + nbytes + 1; ++b) \
+      PUSH_UNCHECKED (ip[b]);                  \
+                                               \
+    interpreter->IP += nbytes;                 \
+  }
+
+#define PUSHW()                                        \
+  {                                            \
+    int b, nbytes, IP;                         \
+    unsigned char *ip;                         \
+                                               \
+    IP = interpreter->IP;                      \
+    nbytes = (opcode - 0xb8 + 1) * 2;          \
+                                               \
+    if (IP + 1 + nbytes                                \
+       > interpreter->num_instructions)        \
+      TRAP ("args to PUSHW lie outside IS");   \
+                                               \
+    CHECK_STACK_AVAILABLE (nbytes / 2);                \
+    ip = interpreter->instructions;            \
+    for (b = IP + 1; b < IP + nbytes + 1;      \
+        b += 2)                                \
+      PUSH2_UNCHECKED (ip[b], ip[b + 1]);      \
+                                               \
+    interpreter->IP += nbytes;                 \
+  }
+
+
+
+/* Rounding instructions.  */
+
+#define ROUND()                                        \
+  {                                            \
+    sfnt_f26dot6 n, result;                    \
+                                               \
+    n = POP ();                                        \
+    result = abs (n);                          \
+                                               \
+    result                                     \
+      = interpreter->state.round (result,      \
+                                 interpreter); \
+    PUSH_UNCHECKED (n < 0 ? -result : result); \
+  }
+
+#define NROUND()                               \
+  {                                            \
+    sfnt_f26dot6 n;                            \
+                                               \
+    n = POP ();                                        \
+    PUSH_UNCHECKED (n);                                \
+  }
+
+#define ROFF()                                 \
+  {                                            \
+    interpreter->state.round_state = 5;                \
+    sfnt_validate_gs (&interpreter->state);    \
+  }
+
+#define RUTG()                                 \
+  {                                            \
+    interpreter->state.round_state = 4;                \
+    sfnt_validate_gs (&interpreter->state);    \
+  }
+
+#define RDTG()                                 \
+  {                                            \
+    interpreter->state.round_state = 3;                \
+    sfnt_validate_gs (&interpreter->state);    \
+  }
+
+#define RTG()                                  \
+  {                                            \
+    interpreter->state.round_state = 1;                \
+    sfnt_validate_gs (&interpreter->state);    \
+  }
+
+#define RTHG()                                 \
+  {                                            \
+    interpreter->state.round_state = 0;                \
+    sfnt_validate_gs (&interpreter->state);    \
+  }
+
+#define RTDG()                                 \
+  {                                            \
+    interpreter->state.round_state = 2;                \
+    sfnt_validate_gs (&interpreter->state);    \
+  }
+
+#define SROUND()                               \
+  {                                            \
+    uint32_t operand;                          \
+                                               \
+    operand = POP ();                          \
+    sfnt_set_srounding_state (interpreter,     \
+                             operand,          \
+                             0x4000);          \
+    interpreter->state.round_state = 6;                \
+    sfnt_validate_gs (&interpreter->state);    \
+  }
+
+#define S45ROUND()                             \
+  {                                            \
+    uint32_t operand;                          \
+                                               \
+    operand = POP ();                          \
+    sfnt_set_srounding_state (interpreter,     \
+                             operand,          \
+                             0x5a82);          \
+    interpreter->state.round_state = 7;                \
+    sfnt_validate_gs (&interpreter->state);    \
+  }
+
+
+
+/* CVT and point delta exception instructions.
+
+   ``Exceptions'' can be placed directly inside the control value
+   table, as it is reloaded every time the point size changes.  */
+
+#define DELTAC1()                              \
+  {                                            \
+    uint32_t operand1, operand2, n;            \
+                                               \
+    n = POP ();                                        \
+                                               \
+  deltac1_start:                               \
+    if (!n)                                    \
+      break;                                   \
+                                               \
+    operand1 = POP ();                         \
+    operand2 = POP ();                         \
+    sfnt_deltac (1, interpreter, operand1,     \
+                operand2);                     \
+    n--;                                       \
+    goto deltac1_start;                                \
+  }
+
+#define DELTAC2()                              \
+  {                                            \
+    uint32_t operand1, operand2, n;            \
+                                               \
+    n = POP ();                                        \
+                                               \
+  deltac2_start:                               \
+    if (!n)                                    \
+      break;                                   \
+                                               \
+    operand1 = POP ();                         \
+    operand2 = POP ();                         \
+    sfnt_deltac (2, interpreter, operand1,     \
+                operand2);                     \
+    n--;                                       \
+    goto deltac2_start;                                \
+  }
+
+#define DELTAC3()                              \
+  {                                            \
+    uint32_t operand1, operand2, n;            \
+                                               \
+    n = POP ();                                        \
+                                               \
+  deltac3_start:                               \
+    if (!n)                                    \
+      break;                                   \
+                                               \
+    operand1 = POP ();                         \
+    operand2 = POP ();                         \
+    sfnt_deltac (3, interpreter, operand1,     \
+                operand2);                     \
+    n--;                                       \
+    goto deltac3_start;                                \
+  }
+
+#define DELTAP1()                              \
+  {                                            \
+    uint32_t n, argn, pn;                      \
+                                               \
+    n = POP ();                                        \
+                                               \
+  deltap1_start:                               \
+    if (!n)                                    \
+      break;                                   \
+                                               \
+    pn = POP ();                               \
+    argn = POP ();                             \
+    sfnt_deltap (1, interpreter, argn, pn);    \
+    n--;                                       \
+    goto deltap1_start;                                \
+  }
+
+#define DELTAP2()                              \
+  {                                            \
+    uint32_t n, argn, pn;                      \
+                                               \
+    n = POP ();                                        \
+                                               \
+  deltap2_start:                               \
+    if (!n)                                    \
+      break;                                   \
+                                               \
+    pn = POP ();                               \
+    argn = POP ();                             \
+    sfnt_deltap (2, interpreter, argn, pn);    \
+    n--;                                       \
+    goto deltap2_start;                                \
+  }
+
+#define DELTAP3()                              \
+  {                                            \
+    uint32_t n, argn, pn;                      \
+                                               \
+    n = POP ();                                        \
+                                               \
+  deltap3_start:                               \
+    if (!n)                                    \
+      break;                                   \
+                                               \
+    pn = POP ();                               \
+    argn = POP ();                             \
+    sfnt_deltap (3, interpreter, argn, pn);    \
+    n--;                                       \
+    goto deltap3_start;                                \
+  }
+
+
+
+/* Anachronistic angle instructions.  */
+
+#define AA()                                   \
+  {                                            \
+    POP ();                                    \
+  }
+
+#define SANGW()                                        \
+  {                                            \
+    POP ();                                    \
+  }
+
+
+
+/* Projection and freedom vector operations.  */
+
+#define PROJECT(x, y)                          \
+  sfnt_project_vector (interpreter, x, y)
+
+#define DUAL_PROJECT(x, y)                     \
+  sfnt_dual_project_vector (interpreter, x, y)
+
+#define SVTCAy()                               \
+  {                                            \
+    sfnt_set_freedom_vector (interpreter,      \
+                            0, 040000);        \
+    sfnt_set_projection_vector (interpreter,   \
+                               0, 040000);     \
+  }
+
+#define SVTCAx()                               \
+  {                                            \
+    sfnt_set_freedom_vector (interpreter,      \
+                            040000, 0);        \
+    sfnt_set_projection_vector (interpreter,   \
+                               040000, 0);     \
+  }
+
+#define SPvTCAy()                              \
+  {                                            \
+    sfnt_set_projection_vector (interpreter,   \
+                               0, 040000);     \
+  }
+
+#define SPvTCAx()                              \
+  {                                            \
+    sfnt_set_projection_vector (interpreter,   \
+                               040000, 0);     \
+  }
+
+#define SFvTCAy()                              \
+  {                                            \
+    sfnt_set_freedom_vector (interpreter,      \
+                            0, 040000);        \
+  }
+
+#define SFvTCAx()                              \
+  {                                            \
+    sfnt_set_freedom_vector (interpreter,      \
+                            040000, 0);        \
+  }
+
+#define SPVTL()                                        \
+  {                                            \
+    struct sfnt_unit_vector vector;            \
+    uint32_t p2, p1;                           \
+                                               \
+    p2 = POP ();                               \
+    p1 = POP ();                               \
+                                               \
+    sfnt_line_to_vector (interpreter,          \
+                        p2, p1, &vector,       \
+                        opcode == 0x07,        \
+                        false);                \
+                                               \
+    sfnt_save_projection_vector (interpreter,   \
+                                &vector,       \
+                                false);        \
+  }
+
+#define SFVTL()                                        \
+  {                                            \
+    struct sfnt_unit_vector vector;            \
+    uint32_t p2, p1;                           \
+                                               \
+    p2 = POP ();                               \
+    p1 = POP ();                               \
+                                               \
+    sfnt_line_to_vector (interpreter,          \
+                        p2, p1, &vector,       \
+                        opcode == 0x09,        \
+                        false);                \
+                                               \
+    sfnt_save_freedom_vector (interpreter,     \
+                             &vector);         \
+  }
+
+#define SPVFS()                                        \
+  {                                            \
+    uint32_t y, x;                             \
+                                               \
+    y = POP ();                                        \
+    x = POP ();                                        \
+                                               \
+    sfnt_set_projection_vector (interpreter, x,        \
+                               y);             \
+  }
+
+#define SFVFS()                                        \
+  {                                            \
+    uint16_t y, x;                             \
+                                               \
+    y = POP ();                                        \
+    x = POP ();                                        \
+                                               \
+    sfnt_set_freedom_vector (interpreter, x,   \
+                            y);                \
+  }
+
+#define GPV()                                  \
+  {                                            \
+    struct sfnt_unit_vector vector;            \
+                                               \
+    vector                                     \
+      = interpreter->state.projection_vector;  \
+                                               \
+    PUSH ((uint16_t) vector.x);                        \
+    PUSH ((uint16_t) vector.y);                        \
+  }
+
+#define GFV()                                  \
+  {                                            \
+    struct sfnt_unit_vector vector;            \
+                                               \
+    vector                                     \
+      = interpreter->state.freedom_vector;     \
+                                               \
+    PUSH ((uint16_t) vector.x);                        \
+    PUSH ((uint16_t) vector.y);                        \
+  }
+
+#define SFVTPV()                               \
+  {                                            \
+    interpreter->state.freedom_vector          \
+      = interpreter->state.projection_vector;  \
+                                               \
+    sfnt_validate_gs (&interpreter->state);    \
+  }
+
+#define ISECT()                                        \
+  {                                            \
+    uint32_t a0, a1, b0, b1, p;                        \
+                                               \
+    CHECK_STACK_ELEMENTS (5);                  \
+                                               \
+    a0 = POP_UNCHECKED ();                     \
+    a1 = POP_UNCHECKED ();                     \
+    b0 = POP_UNCHECKED ();                     \
+    b1 = POP_UNCHECKED ();                     \
+    p = POP_UNCHECKED ();                      \
+                                               \
+    sfnt_interpret_isect (interpreter,         \
+                         a0, a1, b0, b1, p);   \
+  }
+
+#define ALIGNPTS()                             \
+  {                                            \
+    uint32_t p1, p2;                           \
+                                               \
+    p1 = POP ();                               \
+    p2 = POP ();                               \
+                                               \
+    sfnt_interpret_alignpts (interpreter, p1,  \
+                            p2);               \
+  }
+
+#define UTP()                                  \
+  {                                            \
+    uint32_t p;                                        \
+                                               \
+    p = POP ();                                        \
+    sfnt_interpret_utp (interpreter, p);       \
+  }
+
+#define MDAP()                                 \
+  {                                            \
+    uint32_t p;                                        \
+                                               \
+    p = POP ();                                        \
+    sfnt_interpret_mdap (interpreter, p,       \
+                        opcode);               \
+  }
+
+#define IUP()                                  \
+  {                                            \
+    sfnt_interpret_iup (interpreter, opcode);  \
+  }
+
+#define SHP()                                  \
+  {                                            \
+    sfnt_interpret_shp (interpreter, opcode);  \
+  }
+
+#define SHC()                                  \
+  {                                            \
+    uint32_t contour;                          \
+                                               \
+    contour = POP ();                          \
+                                               \
+    sfnt_interpret_shc (interpreter, contour,  \
+                       opcode);                \
+  }
+
+#define SHZ()                                  \
+  {                                            \
+    uint32_t e;                                        \
+                                               \
+    e = POP ();                                        \
+                                               \
+    if (e > 1)                                 \
+      TRAP ("invalid zone!");                  \
+                                               \
+    sfnt_interpret_shz (interpreter, e,                \
+                       opcode);                \
+  }
+
+#define SHPIX()                                        \
+  {                                            \
+    sfnt_f26dot6 pixels, dx, dy;               \
+    uint32_t p;                                        \
+                                               \
+    pixels = POP ();                           \
+    sfnt_scale_by_freedom_vector (interpreter, \
+                                 pixels, &dx,  \
+                                 &dy);         \
+                                               \
+    while (interpreter->state.loop--)          \
+      {                                                \
+       p = POP ();                             \
+       sfnt_direct_move_zp2 (interpreter,      \
+                             p, dx, dy);       \
+      }                                                \
+                                               \
+    interpreter->state.loop = 1;               \
+  }
+
+#define IP()                                   \
+  {                                            \
+    sfnt_interpret_ip (interpreter);           \
+  }
+
+#define MSIRP()                                        \
+  {                                            \
+    sfnt_f26dot6 d;                            \
+    uint32_t p;                                        \
+                                               \
+    d = POP ();                                        \
+    p = POP ();                                        \
+                                               \
+    sfnt_interpret_msirp (interpreter, d, p,   \
+                         opcode);              \
+  }
+
+#define ALIGNRP()                              \
+  {                                            \
+    sfnt_interpret_alignrp (interpreter);      \
+  }
+
+#define MIAP()                                 \
+  {                                            \
+    uint32_t cvt;                              \
+    uint32_t p;                                        \
+                                               \
+    cvt = POP ();                              \
+    p = POP ();                                        \
+                                               \
+    sfnt_interpret_miap (interpreter, cvt, p,  \
+                        opcode);               \
+  }
+
+#define GC()                                   \
+  {                                            \
+    uint32_t p;                                        \
+    sfnt_f26dot6 x, y, value;                  \
+    sfnt_f26dot6 org_x, org_y;                 \
+                                               \
+    p = POP ();                                        \
+                                               \
+    sfnt_address_zp2 (interpreter, p, &x, &y,  \
+                     &org_x, &org_y);          \
+                                               \
+    if (opcode == 0x47)                                \
+      value = DUAL_PROJECT (org_x, org_y);     \
+    else                                       \
+      value = PROJECT (x, y);                  \
+                                               \
+    PUSH_UNCHECKED (value);                    \
+  }
+
+#define SCFS()                                 \
+  {                                            \
+    uint32_t p;                                        \
+    sfnt_f26dot6 c;                            \
+                                               \
+    c = POP ();                                        \
+    p = POP ();                                        \
+                                               \
+    sfnt_interpret_scfs (interpreter, p, c);   \
+  }
+
+#define MD()                                   \
+  {                                            \
+    uint32_t p1, p2;                           \
+    sfnt_f26dot6 distance;                     \
+                                               \
+    p2 = POP ();                               \
+    p1 = POP ();                               \
+                                               \
+    distance                                   \
+      = sfnt_measure_distance (interpreter,    \
+                              p1, p2,          \
+                              opcode);         \
+    PUSH_UNCHECKED (distance);                 \
+  }
+
+#define FLIPPT()                               \
+  {                                            \
+    sfnt_interpret_flippt (interpreter);       \
+  }
+
+#define FLIPRGOFF()                            \
+  {                                            \
+    uint32_t h, l;                             \
+                                               \
+    h = POP ();                                        \
+    l = POP ();                                        \
+                                               \
+    sfnt_interpret_fliprgoff (interpreter,     \
+                             h, l);            \
+  }
+
+#define FLIPRGON()                             \
+  {                                            \
+    uint32_t h, l;                             \
+                                               \
+    h = POP ();                                        \
+    l = POP ();                                        \
+                                               \
+    sfnt_interpret_fliprgon (interpreter,      \
+                            h, l);             \
+  }
+
+#define SDPVTL()                               \
+  {                                            \
+    struct sfnt_unit_vector vector;            \
+    uint32_t p2, p1;                           \
+                                               \
+    p2 = POP ();                               \
+    p1 = POP ();                               \
+                                               \
+    sfnt_line_to_vector (interpreter,          \
+                        p2, p1, &vector,       \
+                        opcode == 0x87,        \
+                        false);                \
+                                               \
+    sfnt_save_projection_vector (interpreter,  \
+                                &vector,       \
+                                false);        \
+                                               \
+    sfnt_line_to_vector (interpreter,          \
+                        p2, p1, &vector,       \
+                        opcode == 0x87,        \
+                        true);                 \
+                                               \
+    sfnt_save_projection_vector (interpreter,  \
+                                &vector,       \
+                                true);         \
+  }
+
+#define MIRP()                                 \
+  {                                            \
+    sfnt_interpret_mirp (interpreter, opcode); \
+  }
+
+#define MDRP()                                 \
+  {                                            \
+    sfnt_interpret_mdrp (interpreter, opcode); \
+  }
+
+
+
+#define NOT_IMPLEMENTED()                      \
+  sfnt_interpret_unimplemented (interpreter,   \
+                               opcode, why)
+
+
+
+/* Multiply the specified MAGNITUDE by the contents of INTERPRETER's
+   freedom vector and return the result in *DX and *DY.  */
+
+static void
+sfnt_scale_by_freedom_vector (struct sfnt_interpreter *interpreter,
+                             sfnt_f26dot6 magnitude, sfnt_f26dot6 *dx,
+                             sfnt_f26dot6 *dy)
+{
+  struct sfnt_unit_vector *vector;
+
+  vector = &interpreter->state.freedom_vector;
+  *dx = sfnt_mul_f2dot14 (vector->x, magnitude);
+  *dy = sfnt_mul_f2dot14 (vector->y, magnitude);
+}
+
+/* Interpret a UTP instruction with the point P in INTERPRETER.
+   Unset any ``touched'' flag inside the point P, relative to the
+   zone in INTERPRETER's ZP0 register.
+
+   Trap upon encountering an out of bounds point.  */
+
+static void
+sfnt_interpret_utp (struct sfnt_interpreter *interpreter,
+                   uint32_t p)
+{
+  if (!interpreter->state.zp0)
+    {
+      if (p >= interpreter->twilight_zone_size)
+       TRAP ("UTP[] p lies outside twilight zone");
+
+      /* There are no flags in the twilight zone.  */
+      return;
+    }
+
+  if (!interpreter->glyph_zone
+      || p >= interpreter->glyph_zone->num_points)
+    TRAP ("UTP[] p lies outside glyph zone");
+
+  interpreter->glyph_zone->flags[p] &= ~SFNT_POINT_TOUCHED_X;
+}
+
+/* Save the specified unit VECTOR into INTERPRETER's graphics state as
+   both the projection and the dual projection vectors.
+
+   If not DUAL_ONLY, set VECTOR as both the projection and dual
+   projection vectors.  Otherwise, only set VECTOR as the dual
+   projection vector.  */
+
+static void
+sfnt_save_projection_vector (struct sfnt_interpreter *interpreter,
+                            struct sfnt_unit_vector *vector,
+                            bool dual_only)
+{
+  if (!dual_only)
+    interpreter->state.projection_vector = *vector;
+
+  interpreter->state.dual_projection_vector = *vector;
+
+  sfnt_validate_gs (&interpreter->state);
+}
+
+/* Save the specified unit VECTOR into INTERPRETER's graphics
+   state.  */
+
+static void
+sfnt_save_freedom_vector (struct sfnt_interpreter *interpreter,
+                         struct sfnt_unit_vector *vector)
+{
+  interpreter->state.freedom_vector = *vector;
+
+  sfnt_validate_gs (&interpreter->state);
+}
+
+/* Return the values of the point NUMBER in the zone pointed to by
+   INTERPRETER's ZP2 register.
+
+   If X_ORG and Y_ORG are set, return the original values (prior to
+   any instruction interpretations) in those two locations.
+
+   Trap if NUMBER is out of bounds or the zone is inaccessible.  */
+
+static void
+sfnt_address_zp2 (struct sfnt_interpreter *interpreter,
+                 uint32_t number,
+                 sfnt_f26dot6 *x, sfnt_f26dot6 *y,
+                 sfnt_f26dot6 *x_org, sfnt_f26dot6 *y_org)
+{
+  if (!interpreter->state.zp2)
+    {
+      /* Address the twilight zone.  */
+      if (number >= interpreter->twilight_zone_size)
+       TRAP ("address to ZP2 (twilight zone) out of bounds");
+
+      *x = interpreter->twilight_x[number];
+      *y = interpreter->twilight_y[number];
+
+      if (!x_org || !y_org)
+       return;
+
+      /* The twilight zone is initially all zero, but initial
+        positions can still be changed.  */
+      *x_org = interpreter->twilight_original_x[number];
+      *y_org = interpreter->twilight_original_y[number];
+      return;
+    }
+
+  /* Address the glyph zone.  */
+  if (!interpreter->glyph_zone)
+    TRAP ("address to ZP2 (glyph zone) points into unset"
+         " zone");
+
+  if (number >= interpreter->glyph_zone->num_points)
+    TRAP ("address to ZP2 (glyph zone) out of bounds");
+
+  *x = interpreter->glyph_zone->x_current[number];
+  *y = interpreter->glyph_zone->y_current[number];
+
+  if (x_org && y_org)
+    {
+      *x_org = interpreter->glyph_zone->x_points[number];
+      *y_org = interpreter->glyph_zone->y_points[number];
+    }
+}
+
+/* Return the values of the point NUMBER in the zone pointed to by
+   INTERPRETER's ZP1 register.
+
+   Trap if NUMBER is out of bounds or the zone is inaccessible.  */
+
+static void
+sfnt_address_zp1 (struct sfnt_interpreter *interpreter,
+                 uint32_t number,
+                 sfnt_f26dot6 *x, sfnt_f26dot6 *y,
+                 sfnt_f26dot6 *x_org, sfnt_f26dot6 *y_org)
+{
+  if (!interpreter->state.zp1)
+    {
+      /* Address the twilight zone.  */
+      if (number >= interpreter->twilight_zone_size)
+       TRAP ("address to ZP1 (twilight zone) out of bounds");
+
+      *x = interpreter->twilight_x[number];
+      *y = interpreter->twilight_y[number];
+
+      if (!x_org || !y_org)
+       return;
+
+      /* The twilight zone is initially all zero, but initial
+        positions can still be changed.  */
+      *x_org = interpreter->twilight_original_x[number];
+      *y_org = interpreter->twilight_original_y[number];
+      return;
+    }
+
+  /* Address the glyph zone.  */
+  if (!interpreter->glyph_zone)
+    TRAP ("address to ZP1 (glyph zone) points into unset"
+         " zone");
+
+  if (number >= interpreter->glyph_zone->num_points)
+    TRAP ("address to ZP1 (glyph zone) out of bounds");
+
+  *x = interpreter->glyph_zone->x_current[number];
+  *y = interpreter->glyph_zone->y_current[number];
+
+  if (x_org && y_org)
+    {
+      *x_org = interpreter->glyph_zone->x_points[number];
+      *y_org = interpreter->glyph_zone->y_points[number];
+    }
+}
+
+/* Return the values of the point NUMBER in the zone pointed to by
+   INTERPRETER's ZP0 register.
+
+   Trap if NUMBER is out of bounds or the zone is inaccessible.  */
+
+static void
+sfnt_address_zp0 (struct sfnt_interpreter *interpreter,
+                 uint32_t number,
+                 sfnt_f26dot6 *x, sfnt_f26dot6 *y,
+                 sfnt_f26dot6 *x_org, sfnt_f26dot6 *y_org)
+{
+  if (!interpreter->state.zp0)
+    {
+      /* Address the twilight zone.  */
+      if (number >= interpreter->twilight_zone_size)
+       TRAP ("address to ZP0 (twilight zone) out of bounds");
+
+      *x = interpreter->twilight_x[number];
+      *y = interpreter->twilight_y[number];
+
+      if (!x_org || !y_org)
+       return;
+
+      /* The twilight zone is initially all zero, but initial
+        positions can still be changed.  */
+      *x_org = interpreter->twilight_original_x[number];
+      *y_org = interpreter->twilight_original_y[number];
+      return;
+    }
+
+  /* Address the glyph zone.  */
+  if (!interpreter->glyph_zone)
+    TRAP ("address to ZP0 (glyph zone) points into unset"
+         " zone");
+
+  if (number >= interpreter->glyph_zone->num_points)
+    TRAP ("address to ZP0 (glyph zone) out of bounds");
+
+  *x = interpreter->glyph_zone->x_current[number];
+  *y = interpreter->glyph_zone->y_current[number];
+
+  if (x_org && y_org)
+    {
+      *x_org = interpreter->glyph_zone->x_points[number];
+      *y_org = interpreter->glyph_zone->y_points[number];
+    }
+}
+
+/* Set the point NUMBER in the zone referenced by INTERPRETER's ZP2
+   register to the specified X and Y.
+
+   Apply FLAGS to NUMBER's flags in that zone.  Trap if NUMBER is out
+   of bounds.  */
+
+static void
+sfnt_store_zp2 (struct sfnt_interpreter *interpreter,
+               uint32_t number, sfnt_f26dot6 x, sfnt_f26dot6 y,
+               int flags)
+{
+  if (!interpreter->state.zp2)
+    {
+      /* Address the twilight zone.  */
+      if (number >= interpreter->twilight_zone_size)
+       TRAP ("address to ZP2 (twilight zone) out of bounds");
+
+      interpreter->twilight_x[number] = x;
+      interpreter->twilight_y[number] = y;
+      return;
+    }
+
+  /* Address the glyph zone.  */
+  if (!interpreter->glyph_zone)
+    TRAP ("address to ZP0 (glyph zone) points into unset"
+         " zone");
+
+  if (number >= interpreter->glyph_zone->num_points)
+    TRAP ("address to ZP0 (glyph zone) out of bounds");
+
+  interpreter->glyph_zone->x_current[number] = x;
+  interpreter->glyph_zone->y_current[number] = y;
+  interpreter->glyph_zone->flags[number] |= flags;
+}
+
+#if 0
+
+/* Convert the line between the points X1, Y1 and X2, Y2 to standard
+   form.
+
+   Return the two coefficients in *A0 and *B0, and the constant in
+   *C.  */
+
+static void
+sfnt_line_to_standard_form (sfnt_f26dot6 x1, sfnt_f26dot6 y1,
+                           sfnt_f26dot6 x2, sfnt_f26dot6 y2,
+                           sfnt_f26dot6 *a, sfnt_f26dot6 *b,
+                           sfnt_f26dot6 *c)
+{
+  sfnt_f26dot6 a_temp, b_temp, c_temp;
+
+  a_temp = sfnt_sub (y2, y1);
+  b_temp = sfnt_sub (x1, x2);
+  c_temp = sfnt_sub (sfnt_mul_f26dot6 (x1, y2),
+                    sfnt_mul_f26dot6 (x2, y1));
+
+  *a = a_temp;
+  *b = b_temp;
+  *c = c_temp;
+}
+
+#endif
+
+/* Check that the specified POINT lies within the zone addressed by
+   INTERPRETER's ZP2 register.  Trap if it does not.  */
+
+static void
+sfnt_check_zp2 (struct sfnt_interpreter *interpreter, uint32_t point)
+{
+  if (!interpreter->state.zp2)
+    {
+      if (point >= interpreter->twilight_zone_size)
+       TRAP ("point lies outside twilight zone (ZP2)");
+    }
+  else if (!interpreter->glyph_zone
+          || point >= interpreter->glyph_zone->num_points)
+    TRAP ("point lies outside glyph zone (ZP2)");
+}
+
+/* Check that the specified POINT lies within the zone addressed by
+   INTERPRETER's ZP0 register.  Trap if it does not.  */
+
+static void
+sfnt_check_zp0 (struct sfnt_interpreter *interpreter, uint32_t point)
+{
+  if (!interpreter->state.zp0)
+    {
+      if (point >= interpreter->twilight_zone_size)
+       TRAP ("point lies outside twilight zone (ZP0)");
+    }
+  else if (!interpreter->glyph_zone
+          || point >= interpreter->glyph_zone->num_points)
+    TRAP ("point lies outside glyph zone (ZP0)");
+}
+
+/* Check that the specified POINT lies within the zone addressed by
+   INTERPRETER's ZP1 register.  Trap if it does not.  */
+
+static void
+sfnt_check_zp1 (struct sfnt_interpreter *interpreter, uint32_t point)
+{
+  if (!interpreter->state.zp1)
+    {
+      if (point >= interpreter->twilight_zone_size)
+       TRAP ("point lies outside twilight zone (ZP0)");
+    }
+  else if (!interpreter->glyph_zone
+          || point >= interpreter->glyph_zone->num_points)
+    TRAP ("point lies outside glyph zone (ZP0)");
+}
+
+/* Move N points starting from the specified POINT in the zone
+   addressed by INTERPRETER's ZP0 register by the given DISTANCE along
+   the freedom vector.
+
+   No checking is done to ensure that POINT lies inside the zone, or
+   even that the zone exists at all.  */
+
+static void
+sfnt_move_zp0 (struct sfnt_interpreter *interpreter, uint32_t point,
+              size_t n, sfnt_f26dot6 distance)
+{
+  if (!interpreter->state.zp0)
+    interpreter->state.move (&interpreter->twilight_x[point],
+                            &interpreter->twilight_y[point],
+                            n, interpreter, distance, NULL);
+  else
+    interpreter->state.move (&interpreter->glyph_zone->x_current[point],
+                            &interpreter->glyph_zone->y_current[point],
+                            n, interpreter, distance,
+                            &interpreter->glyph_zone->flags[point]);
+}
+
+/* Move N points starting from the specified POINT in the zone
+   addressed by INTERPRETER's ZP1 register by the given DISTANCE along
+   the freedom vector.
+
+   No checking is done to ensure that POINT lies inside the zone, or
+   even that the zone exists at all.  */
+
+static void
+sfnt_move_zp1 (struct sfnt_interpreter *interpreter, uint32_t point,
+              size_t n, sfnt_f26dot6 distance)
+{
+  if (!interpreter->state.zp1)
+    interpreter->state.move (&interpreter->twilight_x[point],
+                            &interpreter->twilight_y[point],
+                            n, interpreter, distance, NULL);
+  else
+    interpreter->state.move (&interpreter->glyph_zone->x_current[point],
+                            &interpreter->glyph_zone->y_current[point],
+                            n, interpreter, distance,
+                            &interpreter->glyph_zone->flags[point]);
+}
+
+/* Move N points starting from the specified POINT in the zone
+   addressed by INTERPRETER's ZP2 register by the given DISTANCE along
+   the freedom vector.
+
+   No checking is done to ensure that POINT lies inside the zone, or
+   even that the zone exists at all.  */
+
+static void
+sfnt_move_zp2 (struct sfnt_interpreter *interpreter, uint32_t point,
+              size_t n, sfnt_f26dot6 distance)
+{
+  if (!interpreter->state.zp2)
+    interpreter->state.move (&interpreter->twilight_x[point],
+                            &interpreter->twilight_y[point],
+                            n, interpreter, distance, NULL);
+  else
+    interpreter->state.move (&interpreter->glyph_zone->x_current[point],
+                            &interpreter->glyph_zone->y_current[point],
+                            n, interpreter, distance,
+                            &interpreter->glyph_zone->flags[point]);
+}
+
+/* Move N points from the specified POINT in INTERPRETER's glyph zone
+   by the given DISTANCE along the freedom vector.
+
+   Do not touch the points that are moved.
+
+   No checking is done to ensure that POINT lies inside the zone, or
+   even that the zone exists at all.  */
+
+static void
+sfnt_move_glyph_zone (struct sfnt_interpreter *interpreter, uint32_t point,
+                     size_t n, sfnt_f26dot6 distance)
+{
+  interpreter->state.move (&interpreter->glyph_zone->x_current[point],
+                          &interpreter->glyph_zone->y_current[point],
+                          n, interpreter, distance, NULL);
+}
+
+/* Move N points from the specified POINT in INTERPRETER's twilight
+   zone by the given DISTANCE along the freedom vector.
+
+   Do not touch the points that are moved.
+
+   No checking is done to ensure that POINT lies inside the zone, or
+   even that the zone exists at all.  */
+
+static void
+sfnt_move_twilight_zone (struct sfnt_interpreter *interpreter, uint32_t point,
+                        size_t n, sfnt_f26dot6 distance)
+{
+  interpreter->state.move (&interpreter->twilight_x[point],
+                          &interpreter->twilight_y[point],
+                          n, interpreter, distance, NULL);
+}
+
+/* Move the point P in the zone pointed to by the ZP2 register in
+   INTERPRETER's graphics state by DX, and DY.
+
+   Touch the point P in the directions of the movement.
+
+   Check that P is valid; if not, trap.  Else, perform the move
+   directly without converting it from the projection vector or to the
+   freedom vector.  */
+
+static void
+sfnt_direct_move_zp2 (struct sfnt_interpreter *interpreter, uint32_t p,
+                     sfnt_f26dot6 dx, sfnt_f26dot6 dy)
+{
+  if (!interpreter->state.zp2)
+    {
+      if (p >= interpreter->twilight_zone_size)
+       TRAP ("point out of bounds");
+
+      interpreter->twilight_x[p]
+       = sfnt_add (interpreter->twilight_x[p], dx);
+      interpreter->twilight_y[p]
+       = sfnt_add (interpreter->twilight_y[p], dy);
+    }
+  else
+    {
+      if (!interpreter->glyph_zone
+         || p >= interpreter->glyph_zone->num_points)
+       TRAP ("point out of bounds");
+
+      interpreter->glyph_zone->x_current[p]
+       = sfnt_add (interpreter->glyph_zone->x_current[p], dx);
+      interpreter->glyph_zone->y_current[p]
+       = sfnt_add (interpreter->glyph_zone->y_current[p], dy);
+
+      if (dx)
+       interpreter->glyph_zone->flags[p] |= SFNT_POINT_TOUCHED_X;
+
+      if (dy)
+       interpreter->glyph_zone->flags[p] |= SFNT_POINT_TOUCHED_Y;
+    }
+}
+
+/* Project the vector VX, VY onto INTERPRETER's projection vector.
+   Return the magnitude of the projection.  */
+
+static sfnt_f26dot6
+sfnt_project_vector (struct sfnt_interpreter *interpreter,
+                    sfnt_f26dot6 vx, sfnt_f26dot6 vy)
+{
+  return interpreter->state.project (vx, vy, interpreter);
+}
+
+/* Project the vector VX, VY onto INTERPRETER's dual projection
+   vector.  Return the magnitude of the projection.  */
+
+static sfnt_f26dot6
+sfnt_dual_project_vector (struct sfnt_interpreter *interpreter,
+                         sfnt_f26dot6 vx, sfnt_f26dot6 vy)
+{
+  return interpreter->state.dual_project (vx, vy, interpreter);
+}
+
+/* Interpret a FLIPRGOFF instruction in INTERPRTER.  Make each point
+   in ZP0 between L and H an off-curve point.  */
+
+static void
+sfnt_interpret_fliprgoff (struct sfnt_interpreter *interpreter,
+                         uint32_t l, uint32_t h)
+{
+  uint32_t i;
+
+  sfnt_check_zp0 (interpreter, l);
+  sfnt_check_zp0 (interpreter, h);
+
+  if (!interpreter->state.zp0)
+    return;
+
+  for (i = l; i < h; ++i)
+    interpreter->glyph_zone->flags[i] &= ~01;
+}
+
+/* Interpret a FLIPRGON instruction in INTERPRTER.  Make each point in
+   ZP0 between L and H an on-curve point.  */
+
+static void
+sfnt_interpret_fliprgon (struct sfnt_interpreter *interpreter,
+                        uint32_t l, uint32_t h)
+{
+  uint32_t i;
+
+  sfnt_check_zp0 (interpreter, l);
+  sfnt_check_zp0 (interpreter, h);
+
+  if (!interpreter->state.zp0)
+    return;
+
+  for (i = l; i < h; ++i)
+    interpreter->glyph_zone->flags[i] |= ~01;
+}
+
+/* Interpret a FLIPPT instruction in INTERPRETER.  For loop times, pop
+   a point in ZP0.  If it is an on-curve point, make it an off-curve
+   one, and vice versa.  */
+
+static void
+sfnt_interpret_flippt (struct sfnt_interpreter *interpreter)
+{
+  uint32_t point;
+
+  while (interpreter->state.loop--)
+    {
+      point = POP ();
+
+      /* There are no flags in the twilight zone.
+         But first check that the point is within bounds.  */
+
+      sfnt_check_zp0 (interpreter, point);
+
+      if (!interpreter->state.zp0)
+       continue;
+
+      /* If POINT is on the curve, make it off the curve and vice
+        versa.  */
+
+      if (interpreter->glyph_zone->flags[point] & 01)
+       interpreter->glyph_zone->flags[point] &= ~01;
+      else
+       interpreter->glyph_zone->flags[point] |= 01;
+    }
+
+  /* Restore loop.  */
+  interpreter->state.loop = 1;
+}
+
+/* Interpret an SCFS instruction.
+   Move P in ZP2 along the freedom vector until its projection is
+   equal to C.
+
+   If ZP2 is the twilight zone, ``create'' P by setting its original
+   position to the projection.  */
+
+static void
+sfnt_interpret_scfs (struct sfnt_interpreter *interpreter,
+                    uint32_t p, sfnt_f26dot6 c)
+{
+  sfnt_f26dot6 x, y, distance;
+
+  sfnt_address_zp2 (interpreter, p, &x, &y, NULL, NULL);
+  distance = PROJECT (x, y);
+  sfnt_move_zp2 (interpreter, p, 1, sfnt_sub (c, distance));
+
+  if (!interpreter->state.zp2)
+    {
+      interpreter->twilight_original_x[p] = interpreter->twilight_x[p];
+      interpreter->twilight_original_y[p] = interpreter->twilight_y[p];
+    }
+}
+
+/* Symmetrically round the 26.6 fixed point value X using the rounding
+   mode in INTERPRETER.  Return the result.  */
+
+static sfnt_f26dot6
+sfnt_round_symmetric (struct sfnt_interpreter *interpreter, sfnt_f26dot6 x)
+{
+  int sign;
+
+  sign = 1;
+
+  if (x < 0)
+    {
+      sign = -1;
+      x = -x;
+    }
+
+  return interpreter->state.round (x, interpreter) * sign;
+}
+
+/* Interpret an MIAP (``Move Indirect Absolute Point'') instruction
+   using INTERPRETER.
+
+   Move P in ZP0 along the freedom vector until its projection on the
+   projection vector is equal to CVT units in the projection vector.
+
+   Finally, set RP0 and RP1 to P.
+
+   If ZP0 is the twilight zone, then first create that point in the
+   twilight zone by setting its ``original position'' to the
+   projection of the value.
+
+   If OPCODE is 0x3f, then in addition check the CVT value against the
+   control value cut-in, and round the magnitudes of the movement.  */
+
+static void
+sfnt_interpret_miap (struct sfnt_interpreter *interpreter,
+                    uint32_t cvt, uint32_t p, unsigned char opcode)
+{
+  sfnt_f26dot6 x, y, distance, value, delta;
+
+  /* Read the cvt value.  */
+
+  if (cvt >= interpreter->cvt_size)
+    TRAP ("out of bounds read to cvt");
+
+  value = interpreter->cvt[cvt];
+
+  /* Now load the point.  */
+  sfnt_address_zp0 (interpreter, p, &x, &y, NULL, NULL);
+
+  /* Create the twilight zone point if necessary.
+     Note that the value used is not rounded.  */
+
+  if (!interpreter->state.zp0)
+    {
+      x = interpreter->twilight_x[p]
+       = interpreter->twilight_original_x[p]
+       = sfnt_mul_f2dot14 (interpreter->state.projection_vector.x,
+                           value);
+
+      y = interpreter->twilight_y[p]
+       = interpreter->twilight_original_y[p]
+       = sfnt_mul_f2dot14 (interpreter->state.projection_vector.y,
+                           value);
+    }
+
+  /* Obtain the original distance.  */
+  distance = sfnt_project_vector (interpreter, x, y);
+
+  /* Round the distance and apply the cvt cut in if necessary.  */
+
+  if (opcode == 0x3f)
+    {
+      delta = sfnt_sub (value, distance);
+
+      if (delta < 0)
+       delta = -delta;
+
+      /* If delta is more than the cvt cut in (more aptly named ``cut
+        out''), use the original distance.  */
+
+      if (delta > interpreter->state.cvt_cut_in)
+       value = distance;
+
+      /* Round value.  */
+      value = sfnt_round_symmetric (interpreter, value);
+    }
+
+  /* Move the point by the distance.  */
+  sfnt_move_zp0 (interpreter, p, 1, sfnt_sub (value, distance));
+
+  /* Set reference points.  */
+  interpreter->state.rp0 = p;
+  interpreter->state.rp1 = p;
+}
+
+/* Perform a single iteration of sfnt_interpret_alignrp.  RP0X and
+   RP0Y should be the position of the reference point RP0 in ZP0.  */
+
+static void
+sfnt_interpret_alignrp_1 (struct sfnt_interpreter *interpreter,
+                         sfnt_f26dot6 rp0x, sfnt_f26dot6 rp0y)
+{
+  sfnt_f26dot6 distance, x, y;
+  uint32_t point;
+
+  point = POP ();
+
+  /* Load this point.  */
+  sfnt_address_zp1 (interpreter, point, &x, &y, NULL, NULL);
+
+  /* Measure the distance from here to rp0.  */
+  distance = sfnt_project_vector (interpreter, sfnt_sub (x, rp0x),
+                                 sfnt_sub (y, rp0y));
+
+  /* Move by the opposite.  */
+  sfnt_move_zp1 (interpreter, point, 1, -distance);
+}
+
+/* For loop times, pop a point in ZP1 and align it to RP0 in ZP0 by
+   moving it along the freedom vector until its projected distance
+   from RP0 becomes 0.  */
+
+static void
+sfnt_interpret_alignrp (struct sfnt_interpreter *interpreter)
+{
+  sfnt_f26dot6 rp0x, rp0y;
+
+  sfnt_address_zp0 (interpreter, interpreter->state.rp0,
+                   &rp0x, &rp0y, NULL, NULL);
+
+  while (interpreter->state.loop--)
+    {
+      sfnt_interpret_alignrp_1 (interpreter, rp0x, rp0y);
+
+      /* Reload RP0 if it is in the same zone as ZP1.  */
+      if (interpreter->state.zp0 == interpreter->state.zp1)
+       sfnt_address_zp0 (interpreter, interpreter->state.rp0,
+                         &rp0x, &rp0y, NULL, NULL);
+    }
+
+  interpreter->state.loop = 1;
+}
+
+/* Align the two points P1 and P2 relative to the projection vector.
+   P1 is addressed relative to ZP0, and P2 is addressed relative to
+   ZP1.
+
+   Move both points along the freedom vector by half the magnitude of
+   the the projection of a vector formed by P1.x - P2.x, P1.y - P2.y,
+   upon the projection vector.  */
+
+static void
+sfnt_interpret_alignpts (struct sfnt_interpreter *interpreter,
+                        uint32_t p1, uint32_t p2)
+{
+  sfnt_f26dot6 p1x, p1y, p2x, p2y;
+  sfnt_f26dot6 magnitude;
+
+  sfnt_address_zp0 (interpreter, p1, &p1x, &p1y, NULL, NULL);
+  sfnt_address_zp1 (interpreter, p2, &p2x, &p2y, NULL, NULL);
+
+  magnitude = sfnt_project_vector (interpreter,
+                                  sfnt_sub (p1x, p2x),
+                                  sfnt_sub (p1y, p2y));
+  magnitude = magnitude / 2;
+
+  /* Now move both points along the freedom vector.  */
+  sfnt_move_zp0 (interpreter, p1, 1, magnitude);
+  sfnt_move_zp1 (interpreter, p2, 1, -magnitude);
+}
+
+/* Set the point P in the zone referenced in INTERPRETER's ZP2
+   register to the intersection between the line formed by the points
+   POINT_A0 to POINT_A1 in ZP0 and another line formed by POINT_B0 to
+   POINT_B1 in ZP1.
+
+   Touch the point P.  */
+
+static void
+sfnt_interpret_isect (struct sfnt_interpreter *interpreter,
+                     uint32_t point_a0, uint32_t point_a1,
+                     uint32_t point_b0, uint32_t point_b1,
+                     uint32_t p)
+{
+  sfnt_f26dot6 a0x, a0y, a1x, a1y;
+  sfnt_f26dot6 b0x, b0y, b1x, b1y;
+#if 0
+  sfnt_f26dot6 determinant, dx, dy;
+  sfnt_f26dot6 a0, b0, a1, b1;
+  sfnt_f26dot6 c0, c1, px, py;
+#else
+  sfnt_f26dot6 dx, dy, dax, day, dbx, dby;
+  sfnt_f26dot6 discriminant, val, dot_product;
+  sfnt_f26dot6 px, py;
+#endif
+
+  /* Load points.  */
+  sfnt_address_zp0 (interpreter, point_a0, &a0x, &a0y, NULL, NULL);
+  sfnt_address_zp0 (interpreter, point_a1, &a1x, &a1y, NULL, NULL);
+  sfnt_address_zp1 (interpreter, point_b0, &b0x, &b0y, NULL, NULL);
+  sfnt_address_zp1 (interpreter, point_b1, &b1x, &b1y, NULL, NULL);
+
+#if 0
+  /* The system is determined from the standard form (look this up) of
+     both lines.
+
+     (the variables below have no relation to C identifiers
+      unless otherwise specified.)
+
+       a0*x + b0*y = c0
+       a1*x + b1*y = c1
+
+     The coefficient matrix is thus
+
+       [ a0 b0
+         a1 b1 ]
+
+     the vector of constants (also just dubbed the ``column vector''
+     by some people)
+
+       [ c0
+         c1 ]
+
+     and the solution vector becomes
+
+       [ x
+         y ]
+
+     Since there are exactly two equations and two unknowns, Cramer's
+     rule applies, and there is no need for any Gaussian elimination.
+
+     The determinant for the coefficient matrix is:
+
+       D = a0*b1 - b0*a1
+
+     the first and second determinants are:
+
+       Dx = c0*b1 - a0*c1
+       Dy = a1*c1 - c0*b1
+
+     and x = Dx / D, y = Dy / D.
+
+     If the system is indeterminate, D will be 0.  */
+
+  sfnt_line_to_standard_form (a0x, a0y, a1x, a1y,
+                             &a0, &b0, &c0);
+  sfnt_line_to_standard_form (b0x, b0y, b1x, b1y,
+                             &a1, &b1, &c1);
+
+
+  /* Compute determinants.  */
+  determinant = sfnt_sub (sfnt_mul_fixed (a0, b1),
+                         sfnt_mul_fixed (b0, a1));
+  dx = sfnt_sub (sfnt_mul_fixed (c0, b1),
+                sfnt_mul_fixed (a1, c1));
+  dy = sfnt_sub (sfnt_mul_fixed (a0, c1),
+                sfnt_mul_fixed (c0, b0));
+
+  /* Detect degenerate cases.  */
+
+  if (determinant == 0)
+    goto degenerate_case;
+#else
+  /* The algorithm above would work with floating point, but overflows
+     too easily with fixed point numbers.
+
+     Instead, use the modified vector projection algorithm found in
+     FreeType.  */
+
+  dbx = sfnt_sub (b1x, b0x);
+  dby = sfnt_sub (b1y, b0y);
+  dax = sfnt_sub (a1x, a0x);
+  day = sfnt_sub (a1y, a0y);
+
+  /* Compute vector cross product.  */
+  discriminant = sfnt_add (sfnt_mul_f26dot6 (dax, -dby),
+                          sfnt_mul_f26dot6 (day, dbx));
+  dot_product = sfnt_add (sfnt_mul_f26dot6 (dax, dbx),
+                         sfnt_mul_f26dot6 (day, dby));
+
+  /* Reject any non-intersections and grazing intersections.  */
+  if (!(sfnt_mul (19, abs (discriminant)) > abs (dot_product)))
+    return;
+
+  /* Reject any non-intersections.  */
+  if (!discriminant)
+    goto degenerate_case;
+
+  dx = sfnt_sub (b0x, a0x);
+  dy = sfnt_sub (b0y, a0y);
+  val = sfnt_add (sfnt_mul_f26dot6 (dx, -dby),
+                 sfnt_mul_f26dot6 (dy, dbx));
+
+  /* Project according to these values.  */
+  dx = sfnt_add (a0x, sfnt_multiply_divide_signed (val, dax,
+                                                  discriminant));
+  dy = sfnt_add (a0y, sfnt_multiply_divide_signed (val, day,
+                                                  discriminant));
+#endif
+
+  sfnt_store_zp2 (interpreter, p,
+#if 0
+                 sfnt_div_fixed (dx, determinant),
+                 sfnt_div_fixed (dy, determinant),
+#else
+                 dx, dy,
+#endif
+                 SFNT_POINT_TOUCHED_BOTH);
+  return;
+
+ degenerate_case:
+
+  /* Apple says that in this case:
+
+     Px = (a0x + a1x) / 2 + (b0x + b1x) / 2
+          ---------------------------------
+                         2
+     Py = (a0y + a1y) / 2 + (b0y + b1y) / 2
+          ---------------------------------
+                         2  */
+
+  px = (sfnt_add (a0x, a1x) / 2 + sfnt_add (b0x, b1x) / 2) / 2;
+  py = (sfnt_add (a0y, a1y) / 2 + sfnt_add (b0y, b1y) / 2) / 2;
+  sfnt_store_zp2 (interpreter, p, px, py,
+                 SFNT_POINT_TOUCHED_BOTH);
+}
+
+/* Compute the square root of the 16.16 fixed point number N.  */
+
+static sfnt_fixed
+sfnt_sqrt_fixed (sfnt_fixed n)
+{
+  int count;
+  unsigned int root, rem_hi, rem_lo, possible;
+
+  root = 0;
+
+  if (n > 0)
+    {
+      rem_hi = 0;
+      rem_lo = n;
+      count = 24;
+
+      do
+       {
+         rem_hi = (rem_hi << 2) | (rem_lo >> 30);
+         rem_lo <<= 2;
+         root <<= 1;
+         possible = (root << 1) + 1;
+
+         if (rem_hi >= possible)
+           {
+             rem_hi -= possible;
+             root += 1;
+           }
+       }
+      while (--count);
+    }
+
+  return root;
+}
+
+/* Compute a unit vector describing a vector VX, VY.  Return the value
+   in *VECTOR.  */
+
+static void
+sfnt_normalize_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
+                      struct sfnt_unit_vector *vector)
+{
+  sfnt_f26dot6 x_squared, y_squared;
+  sfnt_fixed n, magnitude;
+
+  if (!vx && !vy)
+    {
+      /* If vx and vy are both zero, then just project
+        horizontally.  */
+
+    fail:
+      vector->x = 04000;
+      vector->y = 0;
+      return;
+    }
+
+  /* Scale vx and vy up if they won't at least make 1.  */
+
+  while (!(vx < -32 || vx > 32) && !(vy < -32 || vy > 32))
+    {
+      vx = vx * 2;
+      vy = vy * 2;
+    }
+
+  /* Compute the magnitude of this vector.  */
+  x_squared = sfnt_mul_f26dot6 (vx, vx);
+  y_squared = sfnt_mul_f26dot6 (vy, vy);
+
+  /* x_squared and y_squared can end up too large to fit in a 16.16
+     fixed.  Scale both values down until they fit.  */
+
+  while (x_squared > 0x200000 || y_squared > 0x200000
+        || x_squared < -0x200000 || y_squared < -0x200000)
+    {
+      x_squared /= 2;
+      y_squared /= 2;
+    }
+
+  /* Convert to 16.16 for greater precision.  */
+  n = sfnt_add (x_squared, y_squared) * 1024;
+
+  /* Get hypotenuse of the triangle from vx, 0, to 0, vy.  */
+  magnitude = sfnt_sqrt_fixed (n);
+
+  /* Avoid division by zero.  */
+  if (!magnitude)
+    goto fail;
+
+  /* Long division.. eek! */
+  vector->x = (sfnt_div_fixed (vx * 1024, magnitude) / 4);
+  vector->y = (sfnt_div_fixed (vy * 1024, magnitude) / 4);
+}
+
+/* Compute a unit vector describing the direction of a line from the
+   point P2 to the point P1.  Save the result in *VECTOR.
+
+   P2 is the address of a point in the zone specified in the ZP2
+   register.  P1 is the address of a point in the zone specified in
+   the ZP1 register.  Take the values of both registers from the
+   specified INTERPRETER's graphics state.
+
+   If PERPENDICULAR, then *VECTOR will be rotated 90 degrees
+   counter-clockwise.  Else, *VECTOR will be parallel to the line.
+
+   If ORIGINAL, then the coordinates used to calculate the line will
+   be those prior to instructing.  Otherwise, the current coordinates
+   will be used.  */
+
+static void
+sfnt_line_to_vector (struct sfnt_interpreter *interpreter,
+                    uint32_t p2, uint32_t p1,
+                    struct sfnt_unit_vector *vector,
+                    bool perpendicular, bool original)
+{
+  sfnt_f26dot6 x2, y2, original_x2, original_y2;
+  sfnt_f26dot6 x1, y1, original_x1, original_y1;
+  sfnt_f26dot6 a, b, temp;
+
+  sfnt_address_zp2 (interpreter, p2, &x2, &y2, &original_x2,
+                   &original_y2);
+  sfnt_address_zp1 (interpreter, p1, &x1, &y1, &original_x1,
+                   &original_y1);
+
+  /* Use original coordinates if specified.  */
+
+  if (original)
+    {
+      x2 = original_x2;
+      y2 = original_y2;
+      x1 = original_x1;
+      y1 = original_y1;
+    }
+
+  /* Calculate the vector between X2, Y2, and X1, Y1.  */
+  a = sfnt_sub (x1, x2);
+  b = sfnt_sub (y1, y2);
+
+  /* Rotate counterclockwise if necessary.  */
+
+  if (perpendicular)
+    {
+      temp = b;
+      b = a;
+      a = -temp;
+    }
+
+  /* Normalize this vector, turning it into a unit vector.  */
+  sfnt_normalize_vector (a, b, vector);
+}
+
+/* Measure the distance between P1 in ZP0 and P2 in ZP1,
+   relative to the projection or dual projection vector.
+
+   Return the distance of P1 and P2 relative to their original
+   un-instructed positions should OPCODE be 0x4A, and to their
+   instructed positions should OPCODE be 0x49.  */
+
+static sfnt_f26dot6
+sfnt_measure_distance (struct sfnt_interpreter *interpreter,
+                      uint32_t p1, uint32_t p2,
+                      unsigned char opcode)
+{
+  sfnt_f26dot6 p1x, p1y, p1_original_x, p1_original_y;
+  sfnt_f26dot6 p2x, p2y, p2_original_x, p2_original_y;
+
+  /* P1 is relative to ZP0 and P2 is relative to ZP1.
+     Apple's manual says this, Microsoft's does not.  */
+
+  sfnt_address_zp0 (interpreter, p1, &p1x, &p1y,
+                   &p1_original_x, &p1_original_y);
+  sfnt_address_zp1 (interpreter, p2, &p2x, &p2y,
+                   &p2_original_x, &p2_original_y);
+
+  if (opcode == 0x4A)
+    return DUAL_PROJECT (sfnt_sub (p1_original_x, p2_original_x),
+                        sfnt_sub (p1_original_y, p2_original_y));
+
+  return PROJECT (sfnt_sub (p1x, p2x),
+                 sfnt_sub (p1y, p2y));
+}
+
+/* Interpret an MSIRP instruction in INTERPRETER.
+   Take a point P, and make the distance between P in ZP1 and the
+   current position of RP0 in ZP0 equal to D.
+
+   If ZP1 is the twilight zone, then create the point P by setting its
+   position and relative positions.
+
+   Then, if OPCODE is equal to 0x3b, make P RP0.  */
+
+static void
+sfnt_interpret_msirp (struct sfnt_interpreter *interpreter,
+                     sfnt_f26dot6 d, uint32_t p, unsigned char opcode)
+{
+  sfnt_f26dot6 rp0x, rp0y, rp0_original_x, rp0_original_y;
+  sfnt_f26dot6 x, y;
+  sfnt_f26dot6 old_distance, temp;
+
+  sfnt_address_zp0 (interpreter, interpreter->state.rp0,
+                   &rp0x, &rp0y, &rp0_original_x,
+                   &rp0_original_y);
+  sfnt_address_zp1 (interpreter, p, &x, &y, NULL, NULL);
+
+  if (!interpreter->state.zp1)
+    {
+      /* Create this point in the twilight zone at RP0.  */
+
+      x = interpreter->twilight_x[p] = rp0x;
+      y = interpreter->twilight_y[p] = rp0y;
+
+      /* Now set the original positions to the projected difference
+         from rp0.  This makes sense once you think about it.  */
+      temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.x, d);
+      temp = sfnt_add (temp, rp0_original_x);
+      interpreter->twilight_original_x[p] = temp;
+
+      temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.y, d);
+      temp = sfnt_add (temp, rp0_original_y);
+      interpreter->twilight_original_y[p] = temp;
+    }
+
+  /* Compute the original distance.  */
+  old_distance = sfnt_project_vector (interpreter,
+                                     sfnt_sub (x, rp0x),
+                                     sfnt_sub (y, rp0y));
+
+  /* Move the point.  */
+  sfnt_move_zp1 (interpreter, p, 1, sfnt_sub (d, old_distance));
+
+  /* Nothing in the TrueType reference manual says directly that this
+     instruction should change rp1 and rp2.  However, it says this
+     instruction is ``very similar to the MIRP[] instruction
+     except...'', and FreeType seems to do this, so do it as well.  */
+
+  interpreter->state.rp1 = interpreter->state.rp0;
+  interpreter->state.rp2 = p;
+
+  if (opcode == 0x3b)
+    interpreter->state.rp0 = p;
+}
+
+/* Interpret an IP instruction in INTERPRETER.  For loop times, pop a
+   single point in ZP2, and interpolate it so that its original
+   relationship to the points RP1 in ZP0 and RP2 in ZP1 as measured
+   along the dual projection vector continues to hold true.  */
+
+static void
+sfnt_interpret_ip (struct sfnt_interpreter *interpreter)
+{
+  sfnt_f26dot6 rp1x, rp1y, rp1_original_x, rp1_original_y;
+  sfnt_f26dot6 rp2x, rp2y, rp2_original_x, rp2_original_y;
+  sfnt_f26dot6 range, new_range, org_distance, cur_distance;
+  sfnt_f26dot6 new_distance;
+  uint32_t p;
+  sfnt_f26dot6 x, y, original_x, original_y;
+
+  /* First load both reference points.  */
+  sfnt_address_zp0 (interpreter, interpreter->state.rp1,
+                   &rp1x, &rp1y, &rp1_original_x,
+                   &rp1_original_y);
+  sfnt_address_zp1 (interpreter, interpreter->state.rp2,
+                   &rp2x, &rp2y, &rp2_original_x,
+                   &rp2_original_y);
+
+  /* Get the original distance between of RP1 and RP2 measured
+     relative to the dual projection vector.  */
+  range = sfnt_dual_project_vector (interpreter,
+                                   sfnt_sub (rp2_original_x,
+                                             rp1_original_x),
+                                   sfnt_sub (rp2_original_y,
+                                             rp1_original_y));
+
+  /* Get the new distance.  */
+  new_range = sfnt_dual_project_vector (interpreter,
+                                       sfnt_sub (rp2x, rp1x),
+                                       sfnt_sub (rp2y, rp1y));
+
+  while (interpreter->state.loop--)
+    {
+      p = POP ();
+
+      /* Load this point relative to zp2.  */
+      sfnt_address_zp2 (interpreter, p, &x, &y, &original_x,
+                       &original_y);
+
+      /* Now compute the old distance from this point to rp1.  */
+      org_distance
+       = sfnt_dual_project_vector (interpreter,
+                                   sfnt_sub (original_x,
+                                             rp1_original_x),
+                                   sfnt_sub (original_y,
+                                             rp1_original_y));
+
+      /* And the current distance from this point to rp1, so
+         how much to move can be determined.  */
+      cur_distance
+       = sfnt_project_vector (interpreter,
+                              sfnt_sub (x, rp1x),
+                              sfnt_sub (y, rp1y));
+
+      /* Finally, apply the ratio of the new distance between RP1 and
+        RP2 to that of the old distance between the two reference
+        points to org_distance, making new_distance.
+
+         If both reference points occupy the same position on the dual
+         projection vector, then simply use the old distance.  */
+
+      if (org_distance)
+       {
+         if (range)
+           new_distance
+             = sfnt_multiply_divide_signed (org_distance,
+                                            new_range, range);
+         else
+           new_distance = org_distance;
+       }
+      else
+       new_distance = 0;
+
+      /* And move the point along the freedom vector to reflect the
+        change in distance.  */
+      sfnt_move_zp2 (interpreter, p, 1,
+                    sfnt_sub (new_distance, cur_distance));
+    }
+
+  interpreter->state.loop = 1;
+}
+
+/* Apply the delta specified by OPERAND to the control value table
+   entry at INDEX currently loaded inside INTERPRETER.
+
+   Trap if INDEX is out of bounds.
+
+   NUMBER is the number of the specific DELTAC instruction this delta
+   is being applied on behalf of.  It must be between 1 and 3.  */
+
+static void
+sfnt_deltac (int number, struct sfnt_interpreter *interpreter,
+            unsigned int index, unsigned char operand)
+{
+  int ppem, delta;
+
+  /* Make sure INDEX is a valid cvt entry.  */
+
+  if (index >= interpreter->cvt_size)
+    TRAP ("DELTACn instruction out of bounds");
+
+  /* operand is an 8 bit number.  The most significant 4 bits
+     represent a specific PPEM size at which to apply the delta
+     specified in the low 4 bits, summed with an instruction specific
+     delta, and the current delta base.  */
+
+  ppem = (operand >> 4) + interpreter->state.delta_base;
+
+  switch (number)
+    {
+    case 1:
+      break;
+
+    case 2:
+      ppem += 16;
+      break;
+
+    case 3:
+      ppem += 32;
+      break;
+    }
+
+  /* Don't apply the delta if the ppem size doesn't match.  */
+
+  if (interpreter->ppem != ppem)
+    return;
+
+  /* Now, determine the delta using the low 4 bits.  The low 4 bits
+     actually specify a ``magnitude'' to apply to the delta, and do
+     not have an encoding for the delta 0.  */
+
+  switch (operand & 0xf)
+    {
+    case 0:
+      delta = -8;
+      break;
+
+    case 1:
+      delta = -7;
+      break;
+
+    case 2:
+      delta = -6;
+      break;
+
+    case 3:
+      delta = -5;
+      break;
+
+    case 4:
+      delta = -4;
+      break;
+
+    case 5:
+      delta = -3;
+      break;
+
+    case 6:
+      delta = -2;
+      break;
+
+    case 7:
+      delta = -1;
+      break;
+
+    case 8:
+      delta = 1;
+      break;
+
+    case 9:
+      delta = 2;
+      break;
+
+    case 10:
+      delta = 3;
+      break;
+
+    case 11:
+      delta = 4;
+      break;
+
+    case 12:
+      delta = 5;
+      break;
+
+    case 13:
+      delta = 6;
+      break;
+
+    case 14:
+      delta = 7;
+      break;
+
+    case 15:
+      delta = 8;
+      break;
+
+      /* To pacify -fanalyzer.  */
+    default:
+      abort ();
+    }
+
+  /* Now, scale up the delta by the step size, which is determined by
+     the delta shift.  */
+  delta *= 1l << (6 - interpreter->state.delta_shift);
+
+  /* Finally, apply the delta to the CVT entry.  */
+  interpreter->cvt[index] = sfnt_add (interpreter->cvt[index],
+                                     delta);
+}
+
+/* Interpret an MDAP (Move Direct Absolute Point) instruction with the
+   opcode OPCODE and the operand P in INTERPRETER.
+
+   Touch the point P (within the zone specified in zp0) in the
+   directions specified in the freedom vector.  Then, if OPCODE is
+   0x7f, round the point and move it the rounded distance along the
+   freedom vector.
+
+   Finally, set the RP0 and RP1 registers to P.  */
+
+static void
+sfnt_interpret_mdap (struct sfnt_interpreter *interpreter,
+                    uint32_t p, uint32_t opcode)
+{
+  sfnt_f26dot6 here, distance, px, py;
+
+  sfnt_address_zp0 (interpreter, p, &px, &py, NULL, NULL);
+
+  /* Measure the current distance.  */
+  here = sfnt_project_vector (interpreter, px, py);
+
+  if (opcode == 0x7f)
+    {
+      /* Measure distance, round, then move to the distance.  */
+      distance = sfnt_project_vector (interpreter, px, py);
+      distance = sfnt_round_symmetric (interpreter, distance);
+      distance = sfnt_sub (distance, here);
+    }
+  else
+    /* Don't move.  Just touch the point.  */
+    distance = 0;
+
+  sfnt_move_zp0 (interpreter, p, 1, distance);
+
+  interpreter->state.rp0 = p;
+  interpreter->state.rp1 = p;
+}
+
+/* Apply the delta specified by OPERAND to the point P in ZP0
+   currently loaded inside INTERPRETER.
+
+   Trap if P is out of bounds.
+
+   NUMBER is the number of the specific DELTAP instruction this delta
+   is being applied on behalf of.  It must be between 1 and 3.  */
+
+static void
+sfnt_deltap (int number, struct sfnt_interpreter *interpreter,
+            unsigned char operand, unsigned int index)
+{
+  int ppem, delta;
+
+  return;
+
+  /* Extract the ppem from OPERAND.  The format is the same as in
+     sfnt_deltac.  */
+
+  ppem = (operand >> 4) + interpreter->state.delta_base;
+
+  switch (number)
+    {
+    case 1:
+      break;
+
+    case 2:
+      ppem += 16;
+      break;
+
+    case 3:
+      ppem += 32;
+      break;
+    }
+
+  /* Don't apply the delta if the ppem size doesn't match.  */
+
+  if (interpreter->ppem != ppem)
+    return;
+
+  /* Now, determine the magnitude of the movement and find the
+     delta.  */
+
+  switch (operand & 0xf)
+    {
+    case 0:
+      delta = -8;
+      break;
+
+    case 1:
+      delta = -7;
+      break;
+
+    case 2:
+      delta = -6;
+      break;
+
+    case 3:
+      delta = -5;
+      break;
+
+    case 4:
+      delta = -4;
+      break;
+
+    case 5:
+      delta = -3;
+      break;
+
+    case 6:
+      delta = -2;
+      break;
+
+    case 7:
+      delta = -1;
+      break;
+
+    case 8:
+      delta = 1;
+      break;
+
+    case 9:
+      delta = 2;
+      break;
+
+    case 10:
+      delta = 3;
+      break;
+
+    case 11:
+      delta = 4;
+      break;
+
+    case 12:
+      delta = 5;
+      break;
+
+    case 13:
+      delta = 6;
+      break;
+
+    case 14:
+      delta = 7;
+      break;
+
+    case 15:
+      delta = 8;
+      break;
+
+      /* To pacify -fanalyzer.  */
+    default:
+      abort ();
+    }
+
+  /* Now, scale up the delta by the step size, which is determined by
+     the delta shift.  */
+  delta *= 1l << (6 - interpreter->state.delta_shift);
+
+  /* Move the point.  */
+  sfnt_check_zp0 (interpreter, index);
+  sfnt_move_zp0 (interpreter, index, 1, delta);
+}
+
+/* Needed by sfnt_interpret_call.  */
+static void sfnt_interpret_run (struct sfnt_interpreter *,
+                               enum sfnt_interpreter_run_context);
+
+/* Call DEFINITION inside INTERPRETER.
+
+   Save INTERPRETER->IP, INTERPRETER->instructions, and
+   INTERPRETER->num_instructions onto the C stack.
+
+   Then, load the instructions in DEFINITION, and run the interpreter
+   again with the context CONTEXT.
+
+   Finally, restore all values.  */
+
+static void
+sfnt_interpret_call (struct sfnt_interpreter_definition *definition,
+                    struct sfnt_interpreter *interpreter,
+                    enum sfnt_interpreter_run_context context)
+{
+  uint16_t num_instructions;
+  int IP;
+  unsigned char *instructions;
+
+  /* Check that no recursion is going on.  */
+  if (interpreter->call_depth++ >= 128)
+    TRAP ("CALL called CALL more than 127 times");
+
+  /* Save the old IP, instructions and number of instructions.  */
+  num_instructions = interpreter->num_instructions;
+  IP = interpreter->IP;
+  instructions = interpreter->instructions;
+
+  /* Load and run the definition.  */
+  interpreter->num_instructions = definition->instruction_count;
+  interpreter->instructions = definition->instructions;
+  interpreter->IP = 0;
+  sfnt_interpret_run (interpreter, context);
+
+  /* Restore the old values.  */
+  interpreter->num_instructions = num_instructions;
+  interpreter->IP = IP;
+  interpreter->instructions = instructions;
+  interpreter->call_depth--;
+}
+
+/* Set the detailed rounding state in interpreter, on behalf of either
+   an SROUND or S45ROUND instruction that has been given the operand
+   OPERAND.
+
+   Use the specified GRID_PERIOD to determine the period.  It is is a
+   18.14 fixed point number, but the rounding state set will be a 26.6
+   fixed point number.  */
+
+static void
+sfnt_set_srounding_state (struct sfnt_interpreter *interpreter,
+                         uint32_t operand, sfnt_f18dot14 grid_period)
+{
+  sfnt_f18dot14 period, phase, threshold;
+
+  /* The most significant 2 bits in the 8 bit OPERAND determine the
+     period.  */
+
+  switch ((operand & 0xc0) >> 6)
+    {
+    case 0:
+      period = grid_period / 2;
+      break;
+
+    case 1:
+      period = grid_period;
+      break;
+
+    case 2:
+      period = grid_period * 2;
+      break;
+
+    case 3:
+    default:
+      TRAP ("reserved period given to SROUND");
+    }
+
+  /* The next two bits determine the phase.  */
+
+  switch ((operand & 0x30) >> 4)
+    {
+    case 0:
+      phase = 0;
+      break;
+
+    case 1:
+      phase = period / 4;
+      break;
+
+    case 2:
+      phase = period / 2;
+      break;
+
+    case 3:
+    default:
+      phase = period * 3 / 2;
+      break;
+    }
+
+  /* And the least significant 4 bits determine the threshold.  */
+
+  if (operand & 0x0f)
+    threshold = (((int) (operand & 0x0f) - 4)
+                * period / 8);
+  else
+    threshold = period - 1;
+
+  /* Now extend these values to 26.6 format and set them.  */
+  interpreter->period = period >> 8;
+  interpreter->phase = phase >> 8;
+  interpreter->threshold = threshold >> 8;
+}
+
+/* Move to the next opcode in INTERPRETER's instruction stream.
+   Value is the opcode originally at INTERPRETER->IP.  */
+
+static unsigned char
+sfnt_skip_code (struct sfnt_interpreter *interpreter)
+{
+  unsigned char opcode;
+  int nbytes;
+
+  if (interpreter->IP == interpreter->num_instructions)
+    TRAP ("IP at end of instruction stream");
+
+  /* Load opcode at IP.  */
+  opcode = interpreter->instructions[interpreter->IP];
+
+  if (opcode == 0x40 || opcode == 0x41)
+    {
+      if (interpreter->IP + 1 >= interpreter->num_instructions)
+       TRAP ("Missing arg to NPUSHB or NPUSHW");
+
+      /* Figure out how many bytes or words to push.  */
+
+      nbytes = interpreter->instructions[interpreter->IP + 1];
+
+      if (opcode == 0x41)
+       nbytes *= 2;
+
+      if (interpreter->IP + 2 + nbytes > interpreter->num_instructions)
+       TRAP ("args to NPUSH instruction lie outside IS");
+
+      /* Increment IP by so much.  */
+      interpreter->IP += 2 + nbytes;
+    }
+  else if (opcode >= 0xb0 && opcode <= 0xb7)
+    {
+      nbytes = opcode - 0xb0 + 1;
+
+      if (interpreter->IP + 1 + nbytes > interpreter->num_instructions)
+       TRAP ("args to PUSHB instruction lie outide IS");
+
+      interpreter->IP += 1 + nbytes;
+    }
+  else if (opcode >= 0xb8 && opcode <= 0xbf)
+    {
+      nbytes = (opcode - 0xb8 + 1) * 2;
+
+      if (interpreter->IP + 1 + nbytes > interpreter->num_instructions)
+       TRAP ("args to PUSHW instruction lie outide IS");
+
+      interpreter->IP += 1 + nbytes;
+    }
+  else
+    interpreter->IP++;
+
+  return opcode;
+}
+
+/* Interpret the unimplemented operation OPCODE using INTERPRETER, and
+   the context WHY.  If there is no instruction definition named
+   OPCODE, trap.  */
+
+static void
+sfnt_interpret_unimplemented (struct sfnt_interpreter *interpreter,
+                             unsigned char opcode,
+                             enum sfnt_interpreter_run_context why)
+{
+  uint32_t i;
+  struct sfnt_interpreter_definition *def;
+
+  for (i = 0; i < interpreter->instruction_defs_size; ++i)
+    {
+      def = &interpreter->instruction_defs[i];
+
+      if (def->opcode == opcode)
+       {
+         if (!def->instructions)
+           TRAP ("** ERROR ** malformed internal instruction"
+                 " definition");
+
+         sfnt_interpret_call (def, interpreter, why);
+         return;
+       }
+    }
+
+  TRAP ("invalid instruction");
+}
+
+/* Start a function definition in INTERPRETER, with the function
+   opcode OPCODE.  */
+
+static void
+sfnt_interpret_fdef (struct sfnt_interpreter *interpreter,
+                    uint32_t opcode)
+{
+  size_t i, num_fdefs;
+  int IP;
+  unsigned char instruction;
+
+  IP = interpreter->IP + 1;
+  num_fdefs = 0;
+
+  /* Now find an ENDF.  */
+
+  while ((instruction = sfnt_skip_code (interpreter)) != 0x2d)
+    {
+      if (interpreter->IP >= interpreter->num_instructions)
+       TRAP ("missing ENDF");
+
+      /* If this is an FDEF or IDEF instruction, increment num_fdefs.
+        Prohibit nested FDEFs or IDEFS.  */
+      if (instruction == 0x2c || instruction == 0x89)
+       ++num_fdefs;
+
+      if (num_fdefs > 1)
+       TRAP ("IDEF or FDEF before ENDF");
+    }
+
+  /* ENDF has been found.  Now save the function definition.  Try to
+     find an existing function definition with this opcode.  If that
+     fails, make i the first available function definition.  */
+
+  for (i = 0; i < interpreter->function_defs_size; ++i)
+    {
+      if (interpreter->function_defs[i].opcode == opcode
+         || !interpreter->function_defs[i].instructions)
+       break;
+    }
+
+  if (i == interpreter->function_defs_size)
+    TRAP ("number of fdefs exceeded maxp->max_function_defs");
+
+  /* Save the opcode of this function definition.  */
+  interpreter->function_defs[i].opcode = opcode;
+
+  /* Make sure to ignore the trailing ENDF instruction.  */
+  interpreter->function_defs[i].instruction_count
+    = interpreter->IP - IP - 1;
+
+  /* Now save a pointer to the instructions.  */
+  interpreter->function_defs[i].instructions = interpreter->instructions + IP;
+}
+
+/* Start an instruction definition in INTERPRETER, with the
+   instruction opcode OPCODE.  */
+
+static void
+sfnt_interpret_idef (struct sfnt_interpreter *interpreter,
+                    uint32_t opcode)
+{
+  size_t i, num_fdefs;
+  int IP;
+  unsigned char instruction;
+
+  IP = interpreter->IP + 1;
+  num_fdefs = 0;
+
+  /* Now find an ENDF.  */
+
+  while ((instruction = sfnt_skip_code (interpreter)) != 0x2d)
+    {
+      if (interpreter->IP >= interpreter->num_instructions)
+       TRAP ("missing ENDF");
+
+      /* If this is an FDEF or IDEF instruction, increment num_fdefs.
+        Prohibit nested FDEFs or IDEFS.  */
+      if (instruction == 0x2c || instruction == 0x89)
+       ++num_fdefs;
+
+      if (num_fdefs > 1)
+       TRAP ("IDEF or FDEF before ENDF");
+    }
+
+  /* ENDF has been found.  Now save the instruction definition.  Try to
+     find an existing instruction definition with this opcode.  If that
+     fails, make i the first available instruction definition.  */
+
+  for (i = 0; i < interpreter->instruction_defs_size; ++i)
+    {
+      if (interpreter->instruction_defs[i].opcode == opcode
+         || !interpreter->instruction_defs[i].instructions)
+       break;
+    }
+
+  if (i == interpreter->instruction_defs_size)
+    TRAP ("number of defs exceeded maxp->max_instruction_defs");
+
+  /* Save the opcode of this instruction definition.  */
+  interpreter->instruction_defs[i].opcode = opcode;
+
+  /* Make sure to ignore the trailing ENDF instruction.  */
+  interpreter->instruction_defs[i].instruction_count
+    = interpreter->IP - IP - 1;
+
+  /* Now save a pointer to the instructions.  */
+  interpreter->instruction_defs[i].instructions
+    = interpreter->instructions + IP;
+}
+
+/* Interpret the specified conditional at INTERPRETER->IP.
+   If CONDITION, evaluate this branch up until the next ELSE or ENDIF.
+   Else, evaluate the branch from a matching ELSE condition, if
+   one exists.  */
+
+static void
+sfnt_interpret_if (struct sfnt_interpreter *interpreter,
+                  bool condition)
+{
+  int nifs;
+  bool need_break;
+  unsigned char opcode;
+
+  if (condition)
+    {
+      interpreter->IP++;
+      return;
+    }
+
+  /* Number of ifs.  */
+  nifs = 0;
+  need_break = false;
+
+  /* Break past the matching else condition.  */
+  do
+    {
+      /* Load the current opcode, then increase IP.  */
+      opcode = sfnt_skip_code (interpreter);
+
+      if (interpreter->IP >= interpreter->num_instructions)
+       break;
+
+      switch (opcode)
+       {
+       case 0x58: /* IF */
+         nifs++;
+         break;
+
+       case 0x1B: /* ELSE */
+         if (nifs == 1)
+           need_break = true;
+
+         break;
+
+       case 0x59: /* EIF */
+         nifs--;
+         if (nifs == 0)
+           need_break = true;
+
+         break;
+       }
+    }
+  while (!need_break);
+}
+
+/* Interpret the specified ELSE branch at INTERPRETER->IP.
+   Evaluate starting from a matching ENDIF instruction.
+
+   If IF has set INTERPRETER->IP to a code within an ELSE branch, this
+   will not be called.  */
+
+static void
+sfnt_interpret_else (struct sfnt_interpreter *interpreter)
+{
+  int nifs;
+  unsigned char opcode;
+
+  /* Number of ifs.  */
+  nifs = 1;
+
+  /* Break past the matching ENDIF condition.  */
+  do
+    {
+      /* Load the current opcode, then increase IP.  */
+      opcode = sfnt_skip_code (interpreter);
+
+      if (interpreter->IP >= interpreter->num_instructions)
+       break;
+
+      switch (opcode)
+       {
+       case 0x58: /* IF */
+         nifs++;
+         break;
+
+       case 0x59: /* EIF */
+         nifs--;
+
+         break;
+       }
+    }
+  while (nifs > 0);
+}
+
+/* ``Add engine compensation to X''.  Since engine compensation is not
+   implemented here, this simply returns X.  INTERPRETER is
+   unused.  */
+
+static sfnt_f26dot6
+sfnt_round_none (sfnt_f26dot6 x, struct sfnt_interpreter *interpreter)
+{
+  return x;
+}
+
+/* Round X to the grid after adding engine compensation.  Return the
+   result.  INTERPRETER is unused.  */
+
+static sfnt_f26dot6
+sfnt_round_to_grid (sfnt_f26dot6 x, struct sfnt_interpreter *interpreter)
+{
+  return sfnt_round_f26dot6 (x);
+}
+
+/* Round X to the nearest half integer or integer and return the
+   result.  INTERPRETER is unused.  */
+
+static sfnt_f26dot6
+sfnt_round_to_double_grid (sfnt_f26dot6 x,
+                          struct sfnt_interpreter *interpreter)
+{
+  return (x + 020) & ~037;
+}
+
+/* Take the floor of X and return the result.  INTERPRETER is
+   unused.  */
+
+static sfnt_f26dot6
+sfnt_round_down_to_grid (sfnt_f26dot6 x,
+                        struct sfnt_interpreter *interpreter)
+{
+  return sfnt_floor_f26dot6 (x);
+}
+
+/* Take the ceiling of X and return the result.  INTERPRETER is
+   unused.  */
+
+static sfnt_f26dot6
+sfnt_round_up_to_grid (sfnt_f26dot6 x,
+                      struct sfnt_interpreter *interpreter)
+{
+  return sfnt_ceil_f26dot6 (x);
+}
+
+/* Round X to only the nearest half integer and return the result.
+   INTERPRETER is unused.  */
+
+static sfnt_f26dot6
+sfnt_round_to_half_grid (sfnt_f26dot6 x,
+                        struct sfnt_interpreter *interpreter)
+{
+  return sfnt_floor_f26dot6 (x) + 32;
+}
+
+/* Round X using the detailed rounding information ``super rounding
+   state'' in INTERPRETER.  Value is the result.  */
+
+static sfnt_f26dot6
+sfnt_round_super (sfnt_f26dot6 x,
+                 struct sfnt_interpreter *interpreter)
+{
+  sfnt_f26dot6 value;
+
+  /* Compute the rounded value.  */
+  value = sfnt_add ((interpreter->threshold
+                    - interpreter->phase), x);
+  value = sfnt_add (value & -interpreter->period,
+                   interpreter->phase);
+
+  /* Remember that since the phase is specified by font instructions,
+     it is possible for the sign to be changed.  In that case, return
+     the phase itself.  */
+
+  return value < 0 ? interpreter->phase : value;
+}
+
+/* Round X using the detailed rounding information ``super rounding
+   state'' in INTERPRETER, but suitably for values that are multiples
+   of the sqrt of 2.  Value is the result.  */
+
+static sfnt_f26dot6
+sfnt_round_super45 (sfnt_f26dot6 x,
+                   struct sfnt_interpreter *interpreter)
+{
+  sfnt_f26dot6 value;
+
+  /* Compute the rounded value.  */
+
+  value = ((sfnt_add (x, (interpreter->threshold
+                         - interpreter->phase))
+           / interpreter->period)
+          * interpreter->period);
+  value = sfnt_add (value, interpreter->phase);
+
+  /* Remember that since the phase is specified by font instructions,
+     it is possible for the sign to be changed.  In that case, return
+     the phase itself.  */
+
+  return value < 0 ? interpreter->phase : value;
+}
+
+/* Project the specified vector VX and VY onto the unit vector that is
+   INTERPRETER's projection vector, assuming that INTERPRETER's
+   projection vector is on the X axis.
+
+   Value is the magnitude of the projected vector.  */
+
+static sfnt_f26dot6
+sfnt_project_onto_x_axis_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
+                                struct sfnt_interpreter *interpreter)
+{
+  return vx;
+}
+
+/* Project the specified vector VX and VY onto the unit vector that is
+   INTERPRETER's projection vector, assuming that INTERPRETER's
+   projection vector is on the Y axis.
+
+   Value is the magnitude of the projected vector.  */
+
+static sfnt_f26dot6
+sfnt_project_onto_y_axis_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
+                                struct sfnt_interpreter *interpreter)
+{
+  return vy;
+}
+
+/* Calculate AX * BX + AY * BY divided by 16384.  */
+
+static int32_t
+sfnt_dot_fix_14 (int32_t ax, int32_t ay, int bx, int by)
+{
+#ifndef INT64_MAX
+  int32_t m, s, hi1, hi2, hi;
+  uint32_t l, lo1, lo2, lo;
+
+
+  /* Compute ax*bx as 64-bit value.  */
+  l = (uint32_t) ((ax & 0xffffu) * bx);
+  m = (ax >> 16) * bx;
+
+  lo1 = l + ((uint32_t) m << 16);
+  hi1 = (m >> 16) + ((int32_t) l >> 31) + (lo1 < l);
+
+  /* Compute ay*by as 64-bit value.  */
+  l = (uint32_t) ((ay & 0xffffu) * by);
+  m = (ay >> 16) * by;
+
+  lo2 = l + ((uint32_t) m << 16);
+  hi2 = (m >> 16) + ((int32_t) l >> 31) + (lo2 < l);
+
+  /* Add them.  */
+  lo = lo1 + lo2;
+  hi = hi1 + hi2 + (lo < lo1);
+
+  /* Divide the result by 2^14 with rounding.  */
+  s = hi >> 31;
+  l = lo + (uint32_t) s;
+  hi += s + (l < lo);
+  lo = l;
+
+  l = lo + 0x2000u;
+  hi += (l < lo);
+
+  return (int32_t) (((uint32_t) hi << 18) | (l >> 14));
+#else
+  int64_t xx, yy;
+
+  xx = (int64_t) ax * bx;
+  yy = (int64_t) ay * by;
+
+  xx += yy;
+  yy = xx >> 63;
+  xx += 0x2000 + yy;
+
+  return (int32_t) (xx / (2 << 14));
+#endif
+}
+
+/* Project the specified vector VX and VY onto the unit vector that is
+   INTERPRETER's projection vector, making only the assumption that the
+   projection vector is a valid unit vector.
+
+   Value is the magnitude of the projected vector.  */
+
+static sfnt_f26dot6
+sfnt_project_onto_any_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
+                             struct sfnt_interpreter *interpreter)
+{
+  return sfnt_dot_fix_14 (vx, vy,
+                         interpreter->state.projection_vector.x,
+                         interpreter->state.projection_vector.y);
+}
+
+/* Project the specified vector VX and VY onto the unit vector that is
+   INTERPRETER's dual projection vector, making only the assumption
+   that the dual projection vector is a valid unit vector.
+
+   The dual projection vector is a vector that is normally the
+   projection vector, but can be set using the original unscaled
+   coordinates of two points as well.
+
+   Value is the magnitude of the projected vector.  */
+
+static sfnt_f26dot6
+sfnt_dual_project_onto_any_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
+                                  struct sfnt_interpreter *interpreter)
+{
+  return sfnt_dot_fix_14 (vx, vy,
+                         interpreter->state.dual_projection_vector.x,
+                         interpreter->state.dual_projection_vector.y);
+}
+
+/* Move N points at *X, *Y by DISTANCE along INTERPRETER's freedom
+   vector.  Set N flags in *FLAGS where appropriate and when non-NULL.
+
+   Assume both vectors are aligned to the X axis.  */
+
+static void
+sfnt_move_x (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y,
+            size_t n, struct sfnt_interpreter *interpreter,
+            sfnt_f26dot6 distance, unsigned char *flags)
+{
+  while (n--)
+    {
+      *x = sfnt_add (*x, distance);
+      x++;
+
+      if (flags)
+       *flags++ |= SFNT_POINT_TOUCHED_X;
+    }
+}
+
+/* Move N points at *X, *Y by DISTANCE along INTERPRETER's freedom
+   vector.  Set N flags in *FLAGS where appropriate and when non-NULL.
+
+   Assume both vectors are aligned to the Y axis.  */
+
+static void
+sfnt_move_y (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y,
+            size_t n, struct sfnt_interpreter *interpreter,
+            sfnt_f26dot6 distance, unsigned char *flags)
+{
+  while (n--)
+    {
+      *y = sfnt_add (*y, distance);
+      y++;
+
+      if (flags)
+       *flags++ |= SFNT_POINT_TOUCHED_Y;
+    }
+}
+
+/* Move N points at *X, *Y by DISTANCE along INTERPRETER's freedom
+   vector.  Set N flags in *FLAGS where appropriate and when
+   non-NULL.  */
+
+static void
+sfnt_move (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y,
+          size_t n, struct sfnt_interpreter *interpreter,
+          sfnt_f26dot6 distance, unsigned char *flags)
+{
+  sfnt_f26dot6 versor, k;
+  sfnt_f2dot14 dot_product;
+  size_t num;
+
+  dot_product = interpreter->state.vector_dot_product;
+
+  /* If the vectors are orthogonal, it is impossible to move anywhere,
+     so simply return.  */
+  if (!dot_product)
+    return;
+
+  /* Not actually 26.6, but the multiply-divisions below cancel each
+     other out, so the result is 26.6.  */
+  versor = interpreter->state.freedom_vector.x;
+
+  if (versor)
+    {
+      /* Move along X axis, converting the distance to the freedom
+        vector.  */
+      num = n;
+      k = sfnt_multiply_divide_signed (distance,
+                                      versor,
+                                      dot_product);
+
+      while (num--)
+       {
+         *x = sfnt_add (*x, k);
+         x++;
+
+         if (flags)
+           *flags++ |= SFNT_POINT_TOUCHED_X;
+       }
+    }
+
+  versor = interpreter->state.freedom_vector.y;
+
+  if (versor)
+    {
+      /* Move along X axis, converting the distance to the freedom
+        vector.  */
+      num = n;
+      k = sfnt_multiply_divide_signed (distance,
+                                      versor,
+                                      dot_product);
+
+      while (num--)
+       {
+         *y = sfnt_add (*y, k);
+         y++;
+
+         if (flags)
+           *flags++ |= SFNT_POINT_TOUCHED_Y;
+       }
+    }
+}
+
+/* Validate the graphics state GS.
+   Establish function pointers for rounding and projection.
+   Establish dot product used to convert vector distances between
+   each other.  */
+
+static void
+sfnt_validate_gs (struct sfnt_graphics_state *gs)
+{
+  /* Establish the function used for rounding based on the round
+     state.  */
+
+  switch (gs->round_state)
+    {
+    case 5: /* Rounding off.  */
+      gs->round = sfnt_round_none;
+      break;
+
+    case 0: /* Round to half grid.  */
+      gs->round = sfnt_round_to_half_grid;
+      break;
+
+    case 1: /* Round to grid.  */
+      gs->round = sfnt_round_to_grid;
+      break;
+
+    case 2: /* Round to double grid.  */
+      gs->round = sfnt_round_to_double_grid;
+      break;
+
+    case 4: /* Round up to grid.  */
+      gs->round = sfnt_round_up_to_grid;
+      break;
+
+    case 3: /* Round down to grid.  */
+      gs->round = sfnt_round_down_to_grid;
+      break;
+
+    case 6: /* Fine grained rounding.  */
+      gs->round = sfnt_round_super;
+      break;
+
+    case 7: /* Fine grained rounding 45 degree variant.  */
+      gs->round = sfnt_round_super45;
+      break;
+    }
+
+  /* Establish the function used for vector projection.
+     When the projection vector is an axis vector, a fast
+     version can be used.  */
+
+  if (gs->projection_vector.x == 040000)
+    gs->project = sfnt_project_onto_x_axis_vector;
+  else if (gs->projection_vector.y == 040000)
+    gs->project = sfnt_project_onto_y_axis_vector;
+  else
+    gs->project = sfnt_project_onto_any_vector;
+
+  /* Do the same for the dual projection vector.  */
+
+  if (gs->dual_projection_vector.x == 040000)
+    gs->dual_project = sfnt_project_onto_x_axis_vector;
+  else if (gs->dual_projection_vector.y == 040000)
+    gs->dual_project = sfnt_project_onto_y_axis_vector;
+  else
+    gs->dual_project = sfnt_dual_project_onto_any_vector;
+
+  /* Compute dot product of the freedom and projection vectors.
+     Handle the common case where the freedom vector is aligned
+     to an axis.  */
+
+  if (gs->freedom_vector.x == 040000)
+    gs->vector_dot_product = gs->projection_vector.x;
+  else if (gs->freedom_vector.y == 040000)
+    gs->vector_dot_product = gs->projection_vector.y;
+  else
+    /* Actually calculate the dot product.  */
+    gs->vector_dot_product = ((((long) gs->projection_vector.x
+                               * gs->freedom_vector.x)
+                              + ((long) gs->projection_vector.y
+                                 * gs->freedom_vector.y))
+                             / 16384);
+
+  /* Now figure out which function to use to move distances.  Handle
+     the common case where both the freedom and projection vectors are
+     aligned to an axis.  */
+
+  if (gs->freedom_vector.x == 040000
+      && gs->projection_vector.x == 040000)
+    gs->move = sfnt_move_x;
+  else if (gs->freedom_vector.y == 040000
+          && gs->projection_vector.y == 040000)
+    gs->move = sfnt_move_y;
+  else
+    gs->move = sfnt_move;
+}
+
+/* Set the X and Y versors of the freedom vector of INTERPRETER's
+   graphics state to the specified X and Y, in 2.14 fixed point
+   format.  */
+
+static void
+sfnt_set_freedom_vector (struct sfnt_interpreter *interpreter,
+                        sfnt_f2dot14 x, sfnt_f2dot14 y)
+{
+  interpreter->state.freedom_vector.x = x;
+  interpreter->state.freedom_vector.y = y;
+
+  sfnt_validate_gs (&interpreter->state);
+}
+
+/* Set the X and Y versors of the projection vector of INTERPRETER's
+   graphics state to the specified X and Y, in 2.14 fixed point
+   format.  */
+
+static void
+sfnt_set_projection_vector (struct sfnt_interpreter *interpreter,
+                           sfnt_f2dot14 x, sfnt_f2dot14 y)
+{
+  interpreter->state.projection_vector.x = x;
+  interpreter->state.projection_vector.y = y;
+  interpreter->state.dual_projection_vector.x = x;
+  interpreter->state.dual_projection_vector.y = y;
+
+  sfnt_validate_gs (&interpreter->state);
+}
+
+/* Interpret an SHZ instruction with the specified OPCODE.  Like
+   sfnt_interpret_shc, but do the move for each point in the entire
+   specified ZONE.  */
+
+static void
+sfnt_interpret_shz (struct sfnt_interpreter *interpreter,
+                   uint32_t zone, unsigned int opcode)
+{
+  sfnt_f26dot6 x, y, original_x, original_y;
+  sfnt_f26dot6 magnitude;
+
+  if (zone != 0 && !interpreter->glyph_zone)
+    /* There are no points in the glyph zone.  */
+    return;
+
+  if (opcode == 0x37)
+    sfnt_address_zp0 (interpreter, interpreter->state.rp1,
+                     &x, &y, &original_x, &original_y);
+  else
+    sfnt_address_zp1 (interpreter, interpreter->state.rp2,
+                     &x, &y, &original_x, &original_y);
+
+  magnitude = sfnt_project_vector (interpreter,
+                                  sfnt_sub (x, original_x),
+                                  sfnt_sub (y, original_y));
+
+  if (zone == 0)
+    sfnt_move_twilight_zone (interpreter, 0,
+                            interpreter->twilight_zone_size,
+                            magnitude);
+  else
+    sfnt_move_glyph_zone (interpreter, 0,
+                         interpreter->glyph_zone->num_points,
+                         magnitude);
+}
+
+/* Interpret an SHC instruction with the specified OPCODE and CONTOUR.
+   Like sfnt_interpret_shp, but do the move for each point in the
+   specified contour.  */
+
+static void
+sfnt_interpret_shc (struct sfnt_interpreter *interpreter,
+                   uint32_t contour, unsigned int opcode)
+{
+  sfnt_f26dot6 x, y, original_x, original_y;
+  sfnt_f26dot6 magnitude;
+  size_t start, end, n;
+
+  if (!interpreter->glyph_zone)
+    TRAP ("SHC without glyph zone");
+
+  /* Check that the contour is within bounds.  */
+  if (contour >= interpreter->glyph_zone->num_contours)
+    TRAP ("contour out of bounds");
+
+  /* Figure out the magnitude of the change, measured from the
+     projection vector.  */
+
+  if (opcode == 0x35)
+    sfnt_address_zp0 (interpreter, interpreter->state.rp1,
+                     &x, &y, &original_x, &original_y);
+  else
+    sfnt_address_zp1 (interpreter, interpreter->state.rp2,
+                     &x, &y, &original_x, &original_y);
+
+  magnitude = sfnt_project_vector (interpreter,
+                                  sfnt_sub (x, original_x),
+                                  sfnt_sub (y, original_y));
+
+  /* Now obtain the start and end of the contour.
+     Verify that both are valid.  */
+
+  if (contour)
+    start = interpreter->glyph_zone->contour_end_points[contour - 1];
+  else
+    start = 0;
+
+  end = interpreter->glyph_zone->contour_end_points[contour];
+
+  if (start > end || end >= interpreter->glyph_zone->num_points)
+    TRAP ("invalid contour data in glyph");
+
+  /* Compute the number of points to move.  */
+  n = end - start + 1;
+
+  /* Move that many points.  */
+  sfnt_move_glyph_zone (interpreter, start, n, magnitude);
+}
+
+/* Interpret an SHP instruction with the specified OPCODE.  Move a
+   popped point in ZP2 along the freedom vector by the distance
+   between a specified point from its original position, which is RP1
+   in ZP0 if OPCODE is 0x33, and RP2 in ZP1 if OPCODE is 0x32.
+
+   Repeat for the number of iterations specified by a prior SLOOP
+   instruction.  */
+
+static void
+sfnt_interpret_shp (struct sfnt_interpreter *interpreter,
+                   unsigned int opcode)
+{
+  sfnt_f26dot6 x, y, original_x, original_y;
+  sfnt_f26dot6 magnitude;
+  uint32_t point;
+
+  /* Figure out the magnitude of the change, measured from the
+     projection vector.  */
+
+  if (opcode == 0x33)
+    sfnt_address_zp0 (interpreter, interpreter->state.rp1,
+                     &x, &y, &original_x, &original_y);
+  else
+    sfnt_address_zp1 (interpreter, interpreter->state.rp2,
+                     &x, &y, &original_x, &original_y);
+
+  magnitude = sfnt_project_vector (interpreter,
+                                  sfnt_sub (x, original_x),
+                                  sfnt_sub (y, original_y));
+
+  /* Now project it onto the freedom vector and move the point that
+     much for loop variable times.  */
+
+  while (interpreter->state.loop--)
+    {
+      point = POP ();
+
+      sfnt_check_zp2 (interpreter, point);
+      sfnt_move_zp2 (interpreter, point, 1, magnitude);
+    }
+
+  /* Restore interpreter->state.loop to 1.  */
+  interpreter->state.loop = 1;
+}
+
+#define load_point(p)                          \
+  (opcode == 0x31                              \
+   ? interpreter->glyph_zone->x_current[p]     \
+   : interpreter->glyph_zone->y_current[p])
+
+#define store_point(p, val)                            \
+  (opcode == 0x31                                      \
+   ? (interpreter->glyph_zone->x_current[p] = (val))   \
+   : (interpreter->glyph_zone->y_current[p] = (val)))
+
+#define load_original(p)                       \
+  (opcode == 0x31                              \
+   ? interpreter->glyph_zone->x_points[p]      \
+   : interpreter->glyph_zone->y_points[p])
+
+#define IUP_SINGLE_PAIR()                                              \
+  /* Now make touch_start the first point before, i.e. the first       \
+     touched point in this pair.  */                                   \
+                                                                       \
+  if (touch_start == start)                                            \
+    touch_start = end;                                                 \
+  else                                                                 \
+    touch_start = touch_start - 1;                                     \
+                                                                       \
+  /* Set point_min and point_max based on which glyph is at a          \
+     lower value.  */                                                  \
+                                                                       \
+  if (load_original (touch_start) < load_original (touch_end))         \
+    {                                                                  \
+      point_min = touch_start;                                         \
+      point_max = touch_end;                                           \
+    }                                                                  \
+  else                                                                 \
+    {                                                                  \
+      point_max = touch_start;                                         \
+      point_min = touch_end;                                           \
+    }                                                                  \
+                                                                       \
+  min_pos = load_point (point_min);                                    \
+  max_pos = load_point (point_max);                                    \
+                                                                       \
+  /* This is needed for interpolation.  */                             \
+  original_max_pos = load_original (point_max);                                
\
+  original_min_pos = load_original (point_min);                                
\
+                                                                       \
+  /* Now process points between touch_start and touch_end.  */         \
+                                                                       \
+  i = touch_start + 1;                                                 \
+                                                                       \
+  /* touch_start might be the last point in the contour.  */           \
+                                                                       \
+  if (i > end)                                                         \
+    i = start;                                                         \
+                                                                       \
+  while (i != touch_end)                                               \
+    {                                                                  \
+      /* Movement is always relative to the original position of       \
+        the point.  */                                                 \
+      position = load_original (i);                                    \
+                                                                       \
+      /* If i is in between touch_start and touch_end...  */           \
+      if (position >= original_min_pos                                 \
+         && position <= original_max_pos)                              \
+       {                                                               \
+         /* Handle the degenerate case where original_min_pos and      \
+            original_max_pos have not changed by placing the point in  \
+            the middle.  */                                            \
+         if (original_min_pos == original_max_pos)                     \
+           ratio = 077777;                                             \
+         else                                                          \
+           /* ... preserve the ratio of i between min_pos and          \
+              max_pos...  */                                           \
+           ratio = sfnt_div_fixed ((sfnt_sub (position,                \
+                                              original_min_pos)        \
+                                    * 1024),                           \
+                                   (sfnt_sub (original_max_pos,        \
+                                              original_min_pos)        \
+                                    * 1024));                          \
+                                                                       \
+         delta = sfnt_sub (max_pos, min_pos);                          \
+         delta = sfnt_mul_fixed (ratio, delta);                        \
+         store_point (i, sfnt_add (min_pos, delta));                   \
+       }                                                               \
+      else                                                             \
+       {                                                               \
+         /* ... otherwise, move i by how much the nearest touched      \
+            point moved.  */                                           \
+                                                                       \
+         if (position >= original_max_pos)                             \
+           delta = sfnt_sub (max_pos, original_max_pos);               \
+         else                                                          \
+           delta = sfnt_sub (min_pos, original_min_pos);               \
+                                                                       \
+         store_point (i, sfnt_add (position, delta));                  \
+       }                                                               \
+                                                                       \
+      if (++i > end)                                                   \
+       i = start;                                                      \
+    }                                                                  \
+
+/* Interpolate untouched points in the contour between and including
+   START and END inside INTERPRETER's glyph zone according to the
+   rules specified for an IUP instruction.  Perform interpolation on
+   the axis specified by OPCODE and MASK.  */
+
+static void
+sfnt_interpret_iup_1 (struct sfnt_interpreter *interpreter,
+                     size_t start, size_t end,
+                     unsigned char opcode, int mask)
+{
+  size_t point;
+  size_t touch_start, touch_end;
+  size_t first_point;
+  size_t point_min, point_max, i;
+  sfnt_f26dot6 position, min_pos, max_pos, delta, ratio;
+  sfnt_f26dot6 original_max_pos;
+  sfnt_f26dot6 original_min_pos;
+
+  /* Find the first touched point.  If none is found, simply
+     return.  */
+
+  for (point = start; point <= end; ++point)
+    {
+      if (interpreter->glyph_zone->flags[point] & mask)
+       goto touched;
+    }
+
+  goto untouched;
+
+ touched:
+
+  point = start;
+
+  /* Find the first touched point.  */
+  while (!(interpreter->glyph_zone->flags[point] & mask))
+    {
+      point++;
+
+      /* There are no touched points.  */
+      if (point > end)
+       goto untouched;
+    }
+
+  first_point = point;
+
+  while (point <= end)
+    {
+      /* Find the next untouched point.  */
+      while (interpreter->glyph_zone->flags[point] & mask)
+       {
+         point++;
+
+         if (point > end)
+           goto wraparound;
+       }
+
+      /* touch_start is now the first untouched point.  */
+      touch_start = point;
+
+      /* Find the next touched point.  */
+      while (!(interpreter->glyph_zone->flags[point] & mask))
+       {
+         point++;
+
+         /* Move back to start if point has gone past the end of the
+            contour.  */
+         if (point > end)
+           goto wraparound_1;
+       }
+
+      /* touch_end is now the next touched point.  */
+      touch_end = point;
+
+      /* Do the interpolation.  */
+      IUP_SINGLE_PAIR ();
+    }
+
+  goto untouched;
+
+ wraparound:
+  /* This is like wraparound_1, except that no untouched points have
+     yet to be found.
+
+     This means the first untouched point is start.  */
+  touch_start = start;
+
+ wraparound_1:
+  /* If point > end, wrap around.  Here, touch_start is set
+     properly, so touch_end must be first_point.  */
+
+  touch_end = first_point;
+  IUP_SINGLE_PAIR ();
+
+ untouched:
+  /* No points were touched or all points have been considered, so
+     return immediately.  */
+  return;
+}
+
+#undef load_point
+#undef store_point
+#undef load_original
+
+/* Interpret an IUP (``interpolate untouched points'') instruction.
+   INTERPRETER is the interpreter, and OPCODE is the instruction
+   number.  See the TrueType Reference Manual for more details.  */
+
+static void
+sfnt_interpret_iup (struct sfnt_interpreter *interpreter,
+                   unsigned char opcode)
+{
+  int mask;
+  size_t i, point, end, first_point;
+
+  /* Check that the zone is the glyph zone.  */
+
+  if (!interpreter->state.zp2)
+    TRAP ("trying to iup in twilight zone");
+
+  if (!interpreter->glyph_zone)
+    TRAP ("iup without loaded glyph!");
+
+  /* Figure out what axis to interpolate in based on the opcode.  */
+  if (opcode == 0x30)
+    mask = SFNT_POINT_TOUCHED_Y;
+  else
+    mask = SFNT_POINT_TOUCHED_X;
+
+  /* Now, for each contour, interpolate untouched points.  */
+  point = 0;
+  for (i = 0; i < interpreter->glyph_zone->num_contours; ++i)
+    {
+      first_point = point;
+      end = interpreter->glyph_zone->contour_end_points[i];
+
+      if (point >= interpreter->glyph_zone->num_points
+         || end >= interpreter->glyph_zone->num_points)
+       TRAP ("glyph contains out of bounds contour end point"
+             " data!");
+
+      sfnt_interpret_iup_1 (interpreter, first_point, end,
+                           opcode, mask);
+      point = end + 1;
+
+      /* Skip the subsequent phantom points, which may end up
+        intermixed with contours inside a compound glyph.  */
+
+      while (point < interpreter->glyph_zone->num_points
+            && interpreter->glyph_zone->flags[point] & SFNT_POINT_PHANTOM)
+       point++;
+    }
+}
+
+/* Interpret an MIRP instruction with the specified OPCODE in
+   INTERPRETER.  Pop a point in ZP1 and CVT index, and move the point
+   until its distance from RP0 in ZP0 is the same as in the control
+   value.  If the point lies in the twilight zone, then ``create'' it
+   as well.
+
+   OPCODE contains a great many flags.
+   They are all described in the TrueType reference manual.  */
+
+static void
+sfnt_interpret_mirp (struct sfnt_interpreter *interpreter,
+                    uint32_t opcode)
+{
+  uint32_t n;
+  uint32_t p;
+  sfnt_f26dot6 distance, delta, temp;
+  sfnt_f26dot6 current_projection, original_projection;
+  sfnt_f26dot6 x, y, org_x, org_y;
+  sfnt_f26dot6 rx, ry, org_rx, org_ry;
+
+  /* CVT index.  */
+  n = POP ();
+
+  /* Point number.  */
+  p = POP ();
+
+  /* Now get the distance from the CVT.  */
+  if (n >= interpreter->cvt_size)
+    TRAP ("cvt index out of bounds");
+
+  distance = interpreter->cvt[n];
+
+  /* Test against the single width value.  */
+
+  delta = sfnt_sub (distance,
+                   interpreter->state.single_width_value);
+
+  if (delta < 0)
+    delta = -delta;
+
+  if (delta < interpreter->state.sw_cut_in)
+    {
+      /* Use the single width instead, as the CVT entry is too
+        small.  */
+
+      if (distance >= 0)
+       distance = interpreter->state.single_width_value;
+      else
+       distance = -interpreter->state.single_width_value;
+    }
+
+  /* Load the reference point.  */
+  sfnt_address_zp0 (interpreter, interpreter->state.rp0,
+                   &rx, &ry, &org_rx, &org_ry);
+
+  /* Create the point in the twilight zone, should that be ZP1.  */
+
+  if (!interpreter->state.zp1)
+    {
+      /* Since P hasn't been loaded yet, whether or not it is valid is
+        not known.  */
+      sfnt_check_zp1 (interpreter, p);
+
+      interpreter->twilight_x[p] = rx;
+      interpreter->twilight_y[p] = ry;
+
+      temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.x,
+                              distance);
+      temp = sfnt_add (temp, org_rx);
+      interpreter->twilight_original_x[p] = temp;
+
+      temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.y,
+                              distance);
+      temp = sfnt_add (temp, org_ry);
+      interpreter->twilight_original_y[p] = temp;
+    }
+
+  /* Load P.  */
+  sfnt_address_zp1 (interpreter, p, &x, &y, &org_x, &org_y);
+
+  /* If distance would be negative and auto_flip is on, flip it.  */
+
+  original_projection = DUAL_PROJECT (org_x - org_rx,
+                                     org_y - org_ry);
+  current_projection = PROJECT (x - rx, y - ry);
+
+  if (interpreter->state.auto_flip)
+    {
+      if ((original_projection ^ distance) < 0)
+       distance = -distance;
+    }
+
+  /* Flag B means look at the cvt cut in and round the
+     distance.  */
+
+  if (opcode & 4)
+    {
+      delta = sfnt_sub (distance, original_projection);
+
+      if (delta < 0)
+       delta = -delta;
+
+      if (delta > interpreter->state.cvt_cut_in)
+       distance = original_projection;
+
+      /* Now, round the distance.  */
+      distance = sfnt_round_symmetric (interpreter, distance);
+    }
+
+  /* Flag C means look at the minimum distance.  */
+
+  if (opcode & 8)
+    {
+      if (original_projection >= 0
+         && distance < interpreter->state.minimum_distance)
+       distance = interpreter->state.minimum_distance;
+      else if (original_projection < 0
+              && distance > -interpreter->state.minimum_distance)
+       distance = -interpreter->state.minimum_distance;
+    }
+
+  /* Finally, move the point.  */
+  sfnt_move_zp1 (interpreter, p, 1,
+                sfnt_sub (distance, current_projection));
+
+  /* Set RP1 to RP0 and RP2 to the point.  If flag 3 is set, also make
+     it RP0.  */
+  interpreter->state.rp1 = interpreter->state.rp0;
+  interpreter->state.rp2 = p;
+
+  if (opcode & 16)
+    interpreter->state.rp0 = p;
+}
+
+/* Interpret an MDRP instruction with the specified OPCODE in
+   INTERPRETER.  Pop a point in ZP1, and move the point until its
+   distance from RP0 in ZP0 is the same as in the original outline.
+
+   This is almost like MIRP[abcde].
+
+   OPCODE contains a great many flags.
+   They are all described in the TrueType reference manual.  */
+
+static void
+sfnt_interpret_mdrp (struct sfnt_interpreter *interpreter,
+                    uint32_t opcode)
+{
+  uint32_t p;
+  sfnt_f26dot6 distance, delta;
+  sfnt_f26dot6 current_projection, original_projection;
+  sfnt_f26dot6 x, y, org_x, org_y;
+  sfnt_f26dot6 rx, ry, org_rx, org_ry;
+
+  /* Point number.  */
+  p = POP ();
+
+  /* Load the points.  */
+  sfnt_address_zp1 (interpreter, p, &x, &y, &org_x, &org_y);
+  sfnt_address_zp0 (interpreter, interpreter->state.rp0,
+                   &rx, &ry, &org_rx, &org_ry);
+
+  distance = DUAL_PROJECT (org_x - org_rx,
+                          org_y - org_ry);
+  original_projection = distance;
+  current_projection = PROJECT (x - rx, y - ry);
+
+  /* Test against the single width value.  */
+
+  delta = sfnt_sub (distance,
+                   interpreter->state.single_width_value);
+
+  if (delta < 0)
+    delta = -delta;
+
+  if (delta < interpreter->state.sw_cut_in)
+    {
+      /* Use the single width instead, as the CVT entry is too
+        small.  */
+
+      if (distance >= 0)
+       distance = interpreter->state.single_width_value;
+      else
+       distance = -interpreter->state.single_width_value;
+    }
+
+  /* Flag B means look at the cvt cut in and round the
+     distance.  */
+
+  if (opcode & 4)
+    {
+      delta = sfnt_sub (distance, original_projection);
+
+      if (delta < 0)
+       delta = -delta;
+
+      if (delta > interpreter->state.cvt_cut_in)
+       distance = original_projection;
+
+      /* Now, round the distance.  */
+      distance = sfnt_round_symmetric (interpreter, distance);
+    }
+
+  /* Flag C means look at the minimum distance.  */
+
+  if (opcode & 8)
+    {
+      if (original_projection >= 0
+         && distance < interpreter->state.minimum_distance)
+       distance = interpreter->state.minimum_distance;
+      else if (original_projection < 0
+              && distance > -interpreter->state.minimum_distance)
+       distance = -interpreter->state.minimum_distance;
+    }
+
+  /* Finally, move the point.  */
+  sfnt_move_zp1 (interpreter, p, 1,
+                sfnt_sub (distance, current_projection));
+
+  /* Set RP1 to RP0 and RP2 to the point.  If flag 3 is set, also make
+     it RP0.  */
+  interpreter->state.rp1 = interpreter->state.rp0;
+  interpreter->state.rp2 = p;
+
+  if (opcode & 16)
+    interpreter->state.rp0 = p;
+}
+
+/* Execute the program now loaded into INTERPRETER.
+   WHY specifies why the interpreter is being run, and is used to
+   control the behavior of instructions such IDEF[] and FDEF[].
+
+   Transfer control to INTERPRETER->trap if interpretation is aborted
+   due to an error, and set INTERPRETER->trap_reason to a string
+   describing the error.
+
+   INTERPRETER->glyph_zone should be cleared before calling this
+   function.  */
+
+static void
+sfnt_interpret_run (struct sfnt_interpreter *interpreter,
+                   enum sfnt_interpreter_run_context why)
+{
+  unsigned char opcode;
+  bool is_prep;
+
+  /* Determine whether or not this is the control value program.  */
+  is_prep = (why == SFNT_RUN_CONTEXT_CONTROL_VALUE_PROGRAM);
+
+#ifdef TEST
+  /* Allow testing control value program instructions as well.  */
+  if (why == SFNT_RUN_CONTEXT_TEST)
+    is_prep = true;
+#endif
+
+  while (interpreter->IP < interpreter->num_instructions)
+    {
+      opcode = interpreter->instructions[interpreter->IP];
+
+#ifdef TEST
+      if (interpreter->run_hook)
+       interpreter->run_hook (interpreter);
+#endif
+
+      switch (opcode)
+       {
+       case 0x00:  /* SVTCA y  */
+         SVTCAy ();
+         break;
+
+       case 0x01:  /* SVTCA x  */
+         SVTCAx ();
+         break;
+
+       case 0x02:  /* SPvTCA y */
+         SPvTCAy ();
+         break;
+
+       case 0x03:  /* SPvTCA x */
+         SPvTCAx ();
+         break;
+
+       case 0x04:  /* SFvTCA y */
+         SFvTCAy ();
+         break;
+
+       case 0x05:  /* SFvTCA x */
+         SFvTCAx ();
+         break;
+
+       case 0x06: /* SPvTL // */
+       case 0x07: /* SPvTL +  */
+         SPVTL ();
+         break;
+
+       case 0x08:  /* SFvTL // */
+       case 0x09:  /* SFvTL +  */
+         SFVTL ();
+         break;
+
+       case 0x0A:  /* SPvFS */
+         SPVFS ();
+         break;
+
+       case 0x0B:  /* SFvFS */
+         SFVFS ();
+         break;
+
+       case 0x0C:  /* GPv */
+         GPV ();
+         break;
+
+       case 0x0D:  /* GFv */
+         GFV ();
+         break;
+
+       case 0x0E:  /* SFvTPv */
+         SFVTPV ();
+         break;
+
+       case 0x0F:  /* ISECT  */
+         ISECT ();
+         break;
+
+       case 0x10:  /* SRP0 */
+         SRP0 ();
+         break;
+
+       case 0x11:  /* SRP1 */
+         SRP1 ();
+         break;
+
+       case 0x12:  /* SRP2 */
+         SRP2 ();
+         break;
+
+       case 0x13:  /* SZP0 */
+         SZP0 ();
+         break;
+
+       case 0x14:  /* SZP1 */
+         SZP1 ();
+         break;
+
+       case 0x15:  /* SZP2 */
+         SZP2 ();
+         break;
+
+       case 0x16:  /* SZPS */
+         SZPS ();
+         break;
+
+       case 0x17:  /* SLOOP */
+         SLOOP ();
+         break;
+
+       case 0x18:  /* RTG */
+         RTG ();
+         break;
+
+       case 0x19:  /* RTHG */
+         RTHG ();
+         break;
+
+       case 0x1A:  /* SMD */
+         SMD ();
+         break;
+
+       case 0x1B:  /* ELSE */
+         ELSE ();
+         break;
+
+       case 0x1C:  /* JMPR */
+         JMPR ();
+         break;
+
+       case 0x1D:  /* SCVTCI */
+         SCVTCI ();
+         break;
+
+       case 0x1E:  /* SSWCI */
+         SSWCI ();
+         break;
+
+       case 0x1F:  /* SSW */
+         SSW ();
+         break;
+
+       case 0x20:  /* DUP */
+         DUP ();
+         break;
+
+       case 0x21:  /* POP */
+         POP ();
+         break;
+
+       case 0x22:  /* CLEAR */
+         CLEAR ();
+         break;
+
+       case 0x23:  /* SWAP */
+         SWAP ();
+         break;
+
+       case 0x24:  /* DEPTH */
+         DEPTH ();
+         break;
+
+       case 0x25:  /* CINDEX */
+         CINDEX ();
+         break;
+
+       case 0x26:  /* MINDEX */
+         MINDEX ();
+         break;
+
+       case 0x27:  /* ALIGNPTS */
+         ALIGNPTS ();
+         break;
+
+       case 0x28:  /* RAW */
+         RAW ();
+         break;
+
+       case 0x29:  /* UTP */
+         UTP ();
+         break;
+
+       case 0x2A:  /* LOOPCALL */
+         LOOPCALL ();
+         break;
+
+       case 0x2B:  /* CALL */
+         CALL ();
+         break;
+
+       case 0x2C:  /* FDEF */
+         FDEF ();
+         break;
+
+       case 0x2D:  /* ENDF */
+         ENDF ();
+         break;
+
+       case 0x2E:  /* MDAP */
+       case 0x2F:  /* MDAP */
+         MDAP ();
+         break;
+
+       case 0x30:  /* IUP */
+       case 0x31:  /* IUP */
+         IUP ();
+         break;
+
+       case 0x32:  /* SHP */
+       case 0x33:  /* SHP */
+         SHP ();
+         break;
+
+       case 0x34:  /* SHC */
+       case 0x35:  /* SHC */
+         SHC ();
+         break;
+
+       case 0x36:  /* SHZ */
+       case 0x37:  /* SHZ */
+         SHZ ();
+         break;
+
+       case 0x38:  /* SHPIX */
+         SHPIX ();
+         break;
+
+       case 0x39:  /* IP    */
+         IP ();
+         break;
+
+       case 0x3A:  /* MSIRP */
+       case 0x3B:  /* MSIRP */
+         MSIRP ();
+         break;
+
+       case 0x3C:  /* ALIGNRP */
+         ALIGNRP ();
+         break;
+
+       case 0x3D:  /* RTDG */
+         RTDG ();
+         break;
+
+       case 0x3E:  /* MIAP */
+       case 0x3F:  /* MIAP */
+         MIAP ();
+         break;
+
+       case 0x40:  /* NPUSHB */
+         NPUSHB ();
+         break;
+
+       case 0x41:  /* NPUSHW */
+         NPUSHW ();
+         break;
+
+       case 0x42:  /* WS */
+         WS ();
+         break;
+
+       case 0x43:  /* RS */
+         RS ();
+         break;
+
+       case 0x44:  /* WCVTP */
+         WCVTP ();
+         break;
+
+       case 0x45:  /* RCVT */
+         RCVT ();
+         break;
+
+       case 0x46:  /* GC */
+       case 0x47:  /* GC */
+         GC ();
+         break;
+
+       case 0x48:  /* SCFS */
+         SCFS ();
+         break;
+
+       case 0x49:  /* MD */
+       case 0x4A:  /* MD */
+         MD ();
+         break;
+
+       case 0x4B:  /* MPPEM */
+         MPPEM ();
+         break;
+
+       case 0x4C:  /* MPS */
+         MPS ();
+         break;
+
+       case 0x4D:  /* FLIPON */
+         FLIPON ();
+         break;
+
+       case 0x4E:  /* FLIPOFF */
+         FLIPOFF ();
+         break;
+
+       case 0x4F:  /* DEBUG */
+         DEBUG ();
+         break;
+
+       case 0x50:  /* LT */
+         LT ();
+         break;
+
+       case 0x51:  /* LTEQ */
+         LTEQ ();
+         break;
+
+       case 0x52:  /* GT */
+         GT ();
+         break;
+
+       case 0x53:  /* GTEQ */
+         GTEQ ();
+         break;
+
+       case 0x54:  /* EQ */
+         EQ ();
+         break;
+
+       case 0x55:  /* NEQ */
+         NEQ ();
+         break;
+
+       case 0x56:  /* ODD */
+         ODD ();
+         break;
+
+       case 0x57:  /* EVEN */
+         EVEN ();
+         break;
+
+       case 0x58:  /* IF */
+         IF ();
+         break;
+
+       case 0x59:  /* EIF */
+         EIF ();
+         break;
+
+       case 0x5A:  /* AND */
+         AND ();
+         break;
+
+       case 0x5B:  /* OR */
+         OR ();
+         break;
+
+       case 0x5C:  /* NOT */
+         NOT ();
+         break;
+
+       case 0x5D:  /* DELTAP1 */
+         DELTAP1 ();
+         break;
+
+       case 0x5E:  /* SDB */
+         SDB ();
+         break;
+
+       case 0x5F:  /* SDS */
+         SDS ();
+         break;
+
+       case 0x60:  /* ADD */
+         ADD ();
+         break;
+
+       case 0x61:  /* SUB */
+         SUB ();
+         break;
+
+       case 0x62:  /* DIV */
+         DIV ();
+         break;
+
+       case 0x63:  /* MUL */
+         MUL ();
+         break;
+
+       case 0x64:  /* ABS */
+         ABS ();
+         break;
+
+       case 0x65:  /* NEG */
+         NEG ();
+         break;
+
+       case 0x66:  /* FLOOR */
+         FLOOR ();
+         break;
+
+       case 0x67:  /* CEILING */
+         CEILING ();
+         break;
+
+       case 0x68:  /* ROUND */
+       case 0x69:  /* ROUND */
+       case 0x6A:  /* ROUND */
+       case 0x6B:  /* ROUND */
+         ROUND ();
+         break;
+
+       case 0x6C:  /* NROUND */
+       case 0x6D:  /* NROUND */
+       case 0x6E:  /* NRRUND */
+       case 0x6F:  /* NROUND */
+         NROUND ();
+         break;
+
+       case 0x70:  /* WCVTF */
+         WCVTF ();
+         break;
+
+       case 0x71:  /* DELTAP2 */
+         DELTAP2 ();
+         break;
+
+       case 0x72:  /* DELTAP3 */
+         DELTAP3 ();
+         break;
+
+       case 0x73:  /* DELTAC1 */
+         DELTAC1 ();
+         break;
+
+       case 0x74:  /* DELTAC2 */
+         DELTAC2 ();
+         break;
+
+       case 0x75:  /* DELTAC3 */
+         DELTAC3 ();
+         break;
+
+       case 0x76:  /* SROUND */
+         SROUND ();
+         break;
+
+       case 0x77:  /* S45Round */
+         S45ROUND ();
+         break;
+
+       case 0x78:  /* JROT */
+         JROT ();
+         break;
+
+       case 0x79:  /* JROF */
+         JROF ();
+         break;
+
+       case 0x7A:  /* ROFF */
+         ROFF ();
+         break;
+
+       case 0x7B:  /* ILLEGAL_INSTRUCTION */
+         ILLEGAL_INSTRUCTION ();
+         break;
+
+       case 0x7C:  /* RUTG */
+         RUTG ();
+         break;
+
+       case 0x7D:  /* RDTG */
+         RDTG ();
+         break;
+
+       case 0x7E:  /* SANGW */
+         SANGW ();
+         break;
+
+       case 0x7F:  /* AA */
+         AA ();
+         break;
+
+       case 0x80:  /* FLIPPT */
+         FLIPPT ();
+         break;
+
+       case 0x81:  /* FLIPRGON */
+         FLIPRGON ();
+         break;
+
+       case 0x82:  /* FLIPRGOFF */
+         FLIPRGOFF ();
+         break;
+
+       case 0x83:  /* RMVT */
+       case 0x84:  /* WMVT */
+         NOT_IMPLEMENTED ();
+         break;
+
+       case 0x85:  /* SCANCTRL */
+         SCANCTRL ();
+         break;
+
+       case 0x86:  /* SDPVTL */
+       case 0x87:  /* SDPVTL */
+         SDPVTL ();
+         break;
+
+       case 0x88:  /* GETINFO */
+         GETINFO ();
+         break;
+
+       case 0x89:  /* IDEF */
+         IDEF ();
+         break;
+
+       case 0x8A:  /* ROLL */
+         ROLL ();
+         break;
+
+       case 0x8B:  /* MAX */
+         _MAX ();
+         break;
+
+       case 0x8C:  /* MIN */
+         _MIN ();
+         break;
+
+         /* Scan or dropout control is not implemented.  Instead, 256
+            grays are used to display pixels which are partially
+            turned on.  */
+       case 0x8D:  /* SCANTYPE */
+         SCANTYPE ();
+         break;
+
+       case 0x8E:  /* INSTCTRL */
+         INSTCTRL ();
+         break;
+
+       case 0x8F:  /* ADJUST */
+       case 0x90:  /* ADJUST */
+         NOT_IMPLEMENTED ();
+         break;
+
+       case 0x91:  /* GXAXIS */
+         GXAXIS ();
+         break;
+
+       default:
+         if (opcode >= 0xE0) /* MIRP */
+           {
+             MIRP ();
+           }
+         else if (opcode >= 0xC0) /* MDRP */
+           {
+             MDRP ();
+           }
+         else if (opcode >= 0xB8) /* PUSHW */
+           {
+             PUSHW ();
+           }
+         else if (opcode >= 0xB0) /* PUSHB */
+           {
+             PUSHB ();
+           }
+         else
+           NOT_IMPLEMENTED ();
+       }
+
+    next_instruction:
+      /* In the case of an NPUSHB or NPUSHW instruction,
+        interpreter->IP has only been increased to skip over the
+        extra bytes, and not the byte containing the instruction
+        itself.  */
+      interpreter->IP++;
+
+      /* This label is used by instructions to continue without
+        incrementing IP.  It is used by instructions which set IP
+        themselves, such as ELSE, IF, FDEF, IDEF and JMPR.  */
+    skip_step:
+      continue;
+    }
+}
+
+/* Execute the font program FPGM using INTERPRETER.
+   This must only be called once per interpreter, else behavior is
+   undefined.
+
+   Value is NULL upon success, else it is a string describing the
+   reason for failure.
+
+   The caller must save the graphics state after interpreting the font
+   program and restore it prior to instructing each glyph.  */
+
+TEST_STATIC const char *
+sfnt_interpret_font_program (struct sfnt_interpreter *interpreter,
+                            struct sfnt_fpgm_table *fpgm)
+{
+  if (setjmp (interpreter->trap))
+    return interpreter->trap_reason;
+
+  /* Set up the interpreter to evaluate the font program.  */
+  interpreter->IP = 0;
+  interpreter->SP = interpreter->stack;
+  interpreter->instructions = fpgm->instructions;
+  interpreter->num_instructions = fpgm->num_instructions;
+  interpreter->glyph_zone = NULL;
+
+  sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_FONT_PROGRAM);
+  return NULL;
+}
+
+/* Execute the control value program PREP using INTERPRETER.
+
+   Return NULL and the graphics state after the execution of the
+   program in *STATE, or a string describing the reason for a failure
+   to interpret the program.
+
+   The caller must save the graphics state after interpreting the
+   control value program and restore it prior to instructing each
+   glyph.  */
+
+TEST_STATIC const char *
+sfnt_interpret_control_value_program (struct sfnt_interpreter *interpreter,
+                                     struct sfnt_prep_table *prep,
+                                     struct sfnt_graphics_state *state)
+{
+  if (setjmp (interpreter->trap))
+    return interpreter->trap_reason;
+
+  /* Set up the interpreter to evaluate the control value program.  */
+  interpreter->IP = 0;
+  interpreter->SP = interpreter->stack;
+  interpreter->instructions = prep->instructions;
+  interpreter->num_instructions = prep->num_instructions;
+  interpreter->glyph_zone = NULL;
+
+  sfnt_interpret_run (interpreter,
+                     SFNT_RUN_CONTEXT_CONTROL_VALUE_PROGRAM);
+
+  /* If instruct_control & 4, then changes to the graphics state made
+     in this program should be reverted.  */
+
+  if (interpreter->state.instruct_control & 4)
+    sfnt_init_graphics_state (&interpreter->state);
+
+  /* Save the graphics state upon success.  */
+  memcpy (state, &interpreter->state, sizeof *state);
+  return NULL;
+}
+
+
+
+/* Glyph hinting.  The routines here perform hinting on simple and
+   compound glyphs.
+
+   In order to keep the hinting mechanism separate from the rest of
+   the code, the routines here perform outline decomposition and
+   scaling separately.  It might be nice to fix that in the
+   future.  */
+
+/* Instructed glyph outline decomposition.  This is separate from
+   sfnt_decompose_glyph because this file should be able to be built
+   with instructions disabled.  */
+
+/* Decompose OUTLINE, an instructed outline, into its individual
+   components.
+
+   Call MOVE_TO to move to a specific location.  For each line
+   encountered, call LINE_TO to draw a line to that location.  For
+   each spline encountered, call CURVE_TO to draw the curves
+   comprising the spline.  Call each of those functions with 16.16
+   fixed point coordinates.
+
+   Call all functions with DCONTEXT as an argument.
+
+   The winding rule used to fill the resulting lines is described in
+   chapter 2 of the TrueType reference manual, under the heading
+   "distinguishing the inside from the outside of a glyph."
+
+   Value is 0 upon success, or some non-zero value upon failure, which
+   can happen if the glyph is invalid.  */
+
+static int
+sfnt_decompose_instructed_outline (struct sfnt_instructed_outline *outline,
+                                  sfnt_move_to_proc move_to,
+                                  sfnt_line_to_proc line_to,
+                                  sfnt_curve_to_proc curve_to,
+                                  void *dcontext)
+{
+  size_t here, last, n;
+
+  if (!outline->num_contours)
+    return 0;
+
+  here = 0;
+
+  for (n = 0; n < outline->num_contours; ++n)
+    {
+      /* here is the first index into the glyph's point arrays
+        belonging to the contour in question.  last is the index
+        of the last point in the contour.  */
+      last = outline->contour_end_points[n];
+
+      /* Make sure here and last make sense.  */
+
+      if (here > last || last >= outline->num_points)
+       goto fail;
+
+      if (sfnt_decompose_glyph_2 (here, last, move_to,
+                                 line_to, curve_to, dcontext,
+                                 outline->x_points,
+                                 outline->y_points,
+                                 outline->flags, 1024))
+       goto fail;
+
+      /* Move forward to the start of the next contour.  */
+      here = last + 1;
+
+      /* here may be a phantom point when outlining a compound glyph,
+        as they can have phantom points mixed in with contours.
+
+        When that happens, skip past all the phantom points.  */
+
+      while (here < outline->num_points
+            && outline->flags[here] & SFNT_POINT_PHANTOM)
+       here++;
+    }
+
+  return 0;
+
+ fail:
+  return 1;
+}
+
+/* Decompose and build an outline for the specified instructed outline
+   INSTRUCTED.  Return the outline data with a refcount of 0 upon
+   success, or NULL upon failure.
+
+   This function is not reentrant.  */
+
+TEST_STATIC struct sfnt_glyph_outline *
+sfnt_build_instructed_outline (struct sfnt_instructed_outline *instructed)
+{
+  struct sfnt_glyph_outline *outline;
+  int rc;
+
+  memset (&build_outline_context, 0, sizeof build_outline_context);
+
+  /* Allocate the outline now with enough for 44 words at the end.  */
+  outline = xmalloc (sizeof *outline + 40 * sizeof (*outline->outline));
+  outline->outline_size = 40;
+  outline->outline_used = 0;
+  outline->refcount = 0;
+  outline->outline
+    = (struct sfnt_glyph_outline_command *) (outline + 1);
+
+  /* Clear outline bounding box.  */
+  outline->xmin = 0;
+  outline->ymin = 0;
+  outline->xmax = 0;
+  outline->ymax = 0;
+
+  /* Set up the context.  */
+  build_outline_context.outline = outline;
+  build_outline_context.factor = 0177777;
+
+  /* Start decomposing.  */
+  rc = sfnt_decompose_instructed_outline (instructed,
+                                         sfnt_move_to_and_build,
+                                         sfnt_line_to_and_build,
+                                         sfnt_curve_to_and_build,
+                                         NULL);
+
+  /* Synchronize the outline object with what might have changed
+     inside sfnt_decompose_glyph.  */
+  outline = build_outline_context.outline;
+
+  /* Finally, obtain the origin point of the glyph after it has been
+     instructed.  */
+
+  if (instructed->num_points > 1)
+    outline->origin
+      = instructed->x_points[instructed->num_points - 2];
+  else
+    outline->origin = 0;
+
+  if (rc)
+    {
+      xfree (outline);
+      return NULL;
+    }
+
+  return outline;
+}
+
+
+
+/* Compute phantom points for the specified glyph GLYPH.  Use the
+   unscaled metrics specified in METRICS, and the 16.16 fixed point
+   scale SCALE.
+
+   Place the X and Y coordinates of the first phantom point in *X1 and
+   *Y1, and those of the second phantom point in *X2 and *Y2.  */
+
+static void
+sfnt_compute_phantom_points (struct sfnt_glyph *glyph,
+                            struct sfnt_glyph_metrics *metrics,
+                            sfnt_fixed scale,
+                            sfnt_f26dot6 *x1, sfnt_f26dot6 *y1,
+                            sfnt_f26dot6 *x2, sfnt_f26dot6 *y2)
+{
+  sfnt_fword f1, f2;
+
+  /* Two ``phantom points'' are appended to each outline by the scaler
+     prior to instruction interpretation.  One of these points
+     represents the left-side bearing distance from xmin, while the
+     other represents the advance width.  Both are then used after the
+     hinting process to ensure that the reported glyph metrics are
+     consistent with the instructed outline.  */
+
+  /* First compute both values in fwords.  */
+  f1 = glyph->xmin - metrics->lbearing;
+  f2 = f1 + metrics->advance;
+
+  /* Apply the metrics distortion.  */
+  f1 += glyph->origin_distortion;
+  f2 += glyph->advance_distortion;
+
+  /* Next, scale both up.  */
+  *x1 = sfnt_mul_f26dot6_fixed (f1 * 64, scale);
+  *x2 = sfnt_mul_f26dot6_fixed (f2 * 64, scale);
+
+  /* Clear y1 and y2.  */
+  *y1 = 0;
+  *y2 = 0;
+}
+
+/* Load the simple glyph GLYPH into the specified INTERPRETER, scaling
+   it up by INTERPRETER's scale, and run its glyph program if
+   present.  Use the unscaled metrics specified in METRICS.
+
+   Upon success, return NULL and the resulting points and contours in
+   *VALUE.  Else, value is the reason interpretation failed.  */
+
+TEST_STATIC const char *
+sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph,
+                            struct sfnt_interpreter *interpreter,
+                            struct sfnt_glyph_metrics *metrics,
+                            struct sfnt_instructed_outline **value)
+{
+  size_t zone_size, temp, outline_size, i;
+  struct sfnt_interpreter_zone *zone;
+  struct sfnt_interpreter_zone *volatile preserved_zone;
+  sfnt_f26dot6 phantom_point_1_x;
+  sfnt_f26dot6 phantom_point_1_y;
+  sfnt_f26dot6 phantom_point_2_x;
+  sfnt_f26dot6 phantom_point_2_y;
+  sfnt_f26dot6 tem;
+  volatile bool zone_was_allocated;
+  struct sfnt_instructed_outline *outline;
+
+  zone_size = 0;
+  zone_was_allocated = false;
+
+  /* Calculate the size of the zone structure.  */
+
+  if (INT_MULTIPLY_WRAPV (glyph->simple->number_of_points + 2,
+                         sizeof *zone->x_points * 4,
+                         &temp)
+      || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+      || INT_MULTIPLY_WRAPV (glyph->number_of_contours,
+                            sizeof *zone->contour_end_points,
+                            &temp)
+      || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+      || INT_MULTIPLY_WRAPV (glyph->simple->number_of_points + 2,
+                            sizeof *zone->flags,
+                            &temp)
+      || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+      || INT_ADD_WRAPV (sizeof *zone, zone_size, &zone_size))
+    return "Glyph exceeded maximum permissible size";
+
+  /* Don't use malloc if possible.  */
+
+  if (zone_size <= 1024 * 16)
+    zone = alloca (zone_size);
+  else
+    {
+      zone = xmalloc (zone_size);
+      zone_was_allocated = true;
+    }
+
+  /* Now load the zone with data.  */
+  zone->num_points = glyph->simple->number_of_points + 2;
+  zone->num_contours = glyph->number_of_contours;
+  zone->contour_end_points = (size_t *) (zone + 1);
+  zone->x_points = (sfnt_f26dot6 *) (zone->contour_end_points
+                                    + zone->num_contours);
+  zone->x_current = zone->x_points + zone->num_points;
+  zone->y_points = zone->x_current + zone->num_points;
+  zone->y_current = zone->y_points + zone->num_points;
+  zone->flags = (unsigned char *) (zone->y_current
+                                  + zone->num_points);
+
+  /* Load x_points and x_current.  */
+  for (i = 0; i < glyph->simple->number_of_points; ++i)
+    {
+      /* Load the fword.  */
+      tem = glyph->simple->x_coordinates[i];
+
+      /* Scale that fword.  */
+      tem = sfnt_mul_f26dot6_fixed (tem * 64, interpreter->scale);
+
+      /* Set x_points and x_current.  */
+      zone->x_points[i] = tem;
+      zone->x_current[i] = tem;
+    }
+
+  /* Compute phantom points.  */
+  sfnt_compute_phantom_points (glyph, metrics, interpreter->scale,
+                              &phantom_point_1_x, &phantom_point_1_y,
+                              &phantom_point_2_x, &phantom_point_2_y);
+
+  /* Load phantom points.  */
+  zone->x_points[i] = phantom_point_1_x;
+  zone->x_points[i + 1] = phantom_point_2_x;
+  zone->x_current[i] = phantom_point_1_x;
+  zone->x_current[i + 1] = phantom_point_2_x;
+
+  /* Load y_points and y_current, along with flags.  */
+  for (i = 0; i < glyph->simple->number_of_points; ++i)
+    {
+      /* Load the fword.  */
+      tem = glyph->simple->y_coordinates[i];
+
+      /* Scale that fword.  Make sure not to round Y, as this could
+        lead to Y spilling over to the next line.  */
+      tem = sfnt_mul_fixed (tem * 64, interpreter->scale);
+
+      /* Set y_points and y_current.  */
+      zone->y_points[i] = tem;
+      zone->y_current[i] = tem;
+
+      /* Set flags.  */
+      zone->flags[i] = (glyph->simple->flags[i]
+                       & ~SFNT_POINT_TOUCHED_BOTH);
+
+      /* Make sure to clear the phantom points flag.  */
+      zone->flags[i] &= ~SFNT_POINT_PHANTOM;
+    }
+
+  /* Load phantom points.  */
+  zone->y_points[i] = phantom_point_1_y;
+  zone->y_points[i + 1] = phantom_point_2_y;
+  zone->y_current[i] = phantom_point_1_x;
+  zone->y_current[i + 1] = phantom_point_2_x;
+
+  /* Load phantom point flags.  */
+  zone->flags[i] = SFNT_POINT_PHANTOM;
+  zone->flags[i + 1] = SFNT_POINT_PHANTOM;
+
+  /* Load contour end points.  */
+  for (i = 0; i < zone->num_contours; ++i)
+    zone->contour_end_points[i]
+      = glyph->simple->end_pts_of_contours[i];
+
+  /* Load the glyph program.  */
+  interpreter->IP = 0;
+  interpreter->SP = interpreter->stack;
+  interpreter->instructions = glyph->simple->instructions;
+  interpreter->num_instructions = glyph->simple->instruction_length;
+  interpreter->glyph_zone = zone;
+
+  /* Copy zone over to this volatile variable.  */
+  preserved_zone = zone;
+
+  if (setjmp (interpreter->trap))
+    {
+      if (zone_was_allocated)
+       xfree (preserved_zone);
+
+      interpreter->glyph_zone = NULL;
+      return interpreter->trap_reason;
+    }
+
+  sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_GLYPH_PROGRAM);
+  interpreter->glyph_zone = NULL;
+
+  /* Move preserved_zone back to zone.  */
+  zone = preserved_zone;
+
+  /* Now that the program has been run, build the scaled outline.  */
+
+  outline_size = sizeof (*outline);
+  outline_size += (zone->num_contours
+                  * sizeof *outline->contour_end_points);
+  outline_size += (zone->num_points
+                  * sizeof *outline->x_points * 2);
+  outline_size += zone->num_points;
+
+  /* Allocate the outline.  */
+  outline = xmalloc (outline_size);
+  outline->num_points = zone->num_points;
+  outline->num_contours = zone->num_contours;
+  outline->contour_end_points = (size_t *) (outline + 1);
+  outline->x_points = (sfnt_f26dot6 *) (outline->contour_end_points
+                                       + outline->num_contours);
+  outline->y_points = outline->x_points + outline->num_points;
+  outline->flags = (unsigned char *) (outline->y_points
+                                     + outline->num_points);
+
+  /* Copy over the contour endpoints, points, and flags.  */
+  memcpy (outline->contour_end_points, zone->contour_end_points,
+         zone->num_contours * sizeof *outline->contour_end_points);
+  memcpy (outline->x_points, zone->x_current,
+         zone->num_points * sizeof *outline->x_points);
+  memcpy (outline->y_points, zone->y_current,
+         zone->num_points * sizeof *outline->y_points);
+  memcpy (outline->flags, zone->flags, zone->num_points);
+
+  /* Free the zone if necessary.  */
+  if (zone_was_allocated)
+    xfree (zone);
+
+  /* Return the outline and NULL.  */
+  *value = outline;
+  return NULL;
+}
+
+/* Apply the transform in the compound glyph component COMPONENT to
+   the array of points of length NUM_COORDINATES given as X and Y.
+
+   Treat X and Y as arrays of 26.6 fixed point values.
+
+   Also, apply the 26.6 fixed point offsets X_OFF and Y_OFF to each X
+   and Y coordinate.
+
+   See sfnt_decompose_compound_glyph for an explanation of why offsets
+   might be applied here, and not while reading the subglyph
+   itself.  */
+
+static void
+sfnt_transform_f26dot6 (struct sfnt_compound_glyph_component *component,
+                       sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y,
+                       size_t num_coordinates,
+                       sfnt_f26dot6 x_off, sfnt_f26dot6 y_off)
+{
+  double m1, m2, m3;
+  double m4, m5, m6;
+  size_t i;
+
+  if (component->flags & 010) /* WE_HAVE_A_SCALE */
+    {
+      for (i = 0; i < num_coordinates; ++i)
+       {
+         x[i] *= component->u.scale / 16384.0;
+         y[i] *= component->u.scale / 16384.0;
+         x[i] += x_off;
+         y[i] += y_off;
+       }
+    }
+  else if (component->flags & 0100) /* WE_HAVE_AN_X_AND_Y_SCALE */
+    {
+      for (i = 0; i < num_coordinates; ++i)
+       {
+         x[i] *= component->u.a.xscale / 16384.0;
+         y[i] *= component->u.a.yscale / 16384.0;
+         x[i] += x_off;
+         y[i] += y_off;
+       }
+    }
+  else if (component->flags & 0200) /* WE_HAVE_A_TWO_BY_TWO */
+    {
+      /* Apply the specified affine transformation.
+        A transform looks like:
+
+          M1 M2 M3     X
+          M4 M5 M6   * Y
+
+          =
+
+          M1*X + M2*Y + M3*1 = X1
+          M4*X + M5*Y + M6*1 = Y1
+
+        (In most transforms, there is another row at the bottom for
+         mathematical reasons.  Since Z1 is always 1.0, the row is
+         simply implied to be 0 0 1, because 0 * x + 0 * y + 1 * 1 =
+         1.0.  See the definition of matrix3x3 in image.c for some
+         more explanations about this.) */
+      m1 = component->u.b.xscale / 16384.0;
+      m2 = component->u.b.scale01 / 16384.0;
+      m3 = 0;
+      m4 = component->u.b.scale10 / 16384.0;
+      m5 = component->u.b.yscale / 16384.0;
+      m6 = 0;
+
+      for (i = 0; i < num_coordinates; ++i)
+       {
+         x[i] = m1 * x[i] + m2 * y[i] + m3 * 1;
+         y[i] = m4 * x[i] + m5 * y[i] + m6 * 1;
+         x[i] += x_off;
+         y[i] += y_off;
+       }
+    }
+}
+
+/* Internal helper for sfnt_interpret_compound_glyph_3.
+
+   Instruct the compound glyph GLYPH using INTERPRETER after all of
+   its components have been instructed.
+
+   Use the unscaled METRICS to compute the phantom points of this
+   glyph.
+
+   CONTEXT contains the points and contours of this compound glyph,
+   numbered starting from BASE_INDEX and BASE_CONTOUR respectively.
+
+   Value is NULL upon success, or a description of the error upon
+   failure.  */
+
+static const char *
+sfnt_interpret_compound_glyph_2 (struct sfnt_glyph *glyph,
+                                struct sfnt_interpreter *interpreter,
+                                struct sfnt_compound_glyph_context *context,
+                                size_t base_index, size_t base_contour,
+                                struct sfnt_glyph_metrics *metrics)
+{
+  size_t num_points, num_contours, i;
+  size_t zone_size, temp;
+  struct sfnt_interpreter_zone *zone;
+  struct sfnt_interpreter_zone *volatile preserved_zone;
+  sfnt_f26dot6 phantom_point_1_x;
+  sfnt_f26dot6 phantom_point_1_y;
+  sfnt_f26dot6 phantom_point_2_x;
+  sfnt_f26dot6 phantom_point_2_y;
+  volatile bool zone_was_allocated;
+  int rc;
+  sfnt_f26dot6 *x_base, *y_base;
+  size_t *contour_base;
+  unsigned char *flags_base;
+
+  /* Figure out how many points and contours there are to
+     instruct.  */
+  num_points = context->num_points - base_index;
+  num_contours = context->num_end_points - base_contour;
+
+  /* Nothing to instruct! */
+  if (!num_points && !num_contours)
+    return NULL;
+
+  /* Build the zone.  First, calculate the size of the zone
+     structure.  */
+
+  zone_size = 0;
+  zone_was_allocated = false;
+
+  if (INT_MULTIPLY_WRAPV (num_points + 2,
+                         sizeof *zone->x_points * 4,
+                         &temp)
+      || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+      || INT_MULTIPLY_WRAPV (num_contours,
+                            sizeof *zone->contour_end_points,
+                            &temp)
+      || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+      || INT_MULTIPLY_WRAPV (num_points + 2,
+                            sizeof *zone->flags,
+                            &temp)
+      || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+      || INT_ADD_WRAPV (sizeof *zone, zone_size, &zone_size))
+    return "Glyph exceeded maximum permissible size";
+
+  /* Don't use malloc if possible.  */
+
+  if (zone_size <= 1024 * 16)
+    zone = alloca (zone_size);
+  else
+    {
+      zone = xmalloc (zone_size);
+      zone_was_allocated = true;
+    }
+
+  /* Now load the zone with data.  */
+  zone->num_points = num_points + 2;
+  zone->num_contours = num_contours;
+  zone->contour_end_points = (size_t *) (zone + 1);
+  zone->x_points = (sfnt_f26dot6 *) (zone->contour_end_points
+                                    + zone->num_contours);
+  zone->x_current = zone->x_points + zone->num_points;
+  zone->y_points = zone->x_current + zone->num_points;
+  zone->y_current = zone->y_points + zone->num_points;
+  zone->flags = (unsigned char *) (zone->y_current
+                                  + zone->num_points);
+
+  /* Copy and renumber all contour end points to start from
+     base_index.  */
+
+  for (i = 0; i < zone->num_contours; ++i)
+    zone->contour_end_points[i]
+      = (context->contour_end_points[base_contour + i]
+        - base_index);
+
+  /* Now copy over x_points, x_current, y_points and y_current.  */
+
+  for (i = 0; i < num_points; ++i)
+    {
+      zone->x_current[i] = context->x_coordinates[i + base_index];
+      zone->x_points[i] = context->x_coordinates[i + base_index];
+    }
+
+  /* Compute phantom points.  */
+  sfnt_compute_phantom_points (glyph, metrics, interpreter->scale,
+                              &phantom_point_1_x, &phantom_point_1_y,
+                              &phantom_point_2_x, &phantom_point_2_y);
+
+  /* Load phantom points.  */
+  zone->x_points[i] = phantom_point_1_x;
+  zone->x_points[i + 1] = phantom_point_2_x;
+  zone->x_current[i] = phantom_point_1_x;
+  zone->x_current[i + 1] = phantom_point_2_x;
+
+  for (i = 0; i < num_points; ++i)
+    {
+      zone->y_current[i] = context->y_coordinates[i + base_index];
+      zone->y_points[i] = context->y_coordinates[i + base_index];
+
+      /* Set flags.  */
+      zone->flags[i] = (context->flags[i + base_index]
+                       & ~SFNT_POINT_TOUCHED_BOTH);
+    }
+
+    /* Load phantom points.  */
+  zone->y_points[i] = phantom_point_1_y;
+  zone->y_points[i + 1] = phantom_point_2_y;
+  zone->y_current[i] = phantom_point_1_x;
+  zone->y_current[i + 1] = phantom_point_2_x;
+
+  /* Load phantom point flags.  */
+  zone->flags[i] = SFNT_POINT_PHANTOM;
+  zone->flags[i + 1] = SFNT_POINT_PHANTOM;
+
+  /* Load the compound glyph program.  */
+  interpreter->IP = 0;
+  interpreter->SP = interpreter->stack;
+  interpreter->instructions = glyph->compound->instructions;
+  interpreter->num_instructions = glyph->compound->instruction_length;
+  interpreter->glyph_zone = zone;
+
+  /* Copy zone over to this volatile variable.  */
+  preserved_zone = zone;
+
+  if (setjmp (interpreter->trap))
+    {
+      if (zone_was_allocated)
+       xfree (preserved_zone);
+
+      interpreter->glyph_zone = NULL;
+      return interpreter->trap_reason;
+    }
+
+  sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_GLYPH_PROGRAM);
+  interpreter->glyph_zone = NULL;
+
+  /* Move preserved_zone back to zone.  */
+  zone = preserved_zone;
+
+  /* Now copy the instructed points back, and add the two phantom
+     points to the end.  */
+
+  for (i = 0; i < num_points; ++i)
+    {
+      context->x_coordinates[base_index + i] = zone->x_current[i];
+      context->y_coordinates[base_index + i] = zone->y_current[i];
+    }
+
+  /* Grow various arrays to fit the phantom points.  */
+  rc = sfnt_expand_compound_glyph_context (context, 0, 2,
+                                          &x_base, &y_base,
+                                          &flags_base,
+                                          &contour_base);
+
+  if (rc)
+    {
+      if (zone_was_allocated)
+       xfree (zone);
+
+      return "Failed to expand arrays for phantom points";
+    }
+
+  /* Copy over the phantom points.  */
+  x_base[0] = zone->x_current[num_points - 2];
+  x_base[1] = zone->x_current[num_points - 1];
+  y_base[0] = zone->y_current[num_points - 2];
+  y_base[1] = zone->y_current[num_points - 1];
+  flags_base[0] = zone->flags[num_points - 2];
+  flags_base[1] = zone->flags[num_points - 1];
+
+  /* Free the zone if needed.  */
+  if (zone_was_allocated)
+    xfree (zone);
+
+  return NULL;
+}
+
+/* Internal helper for sfnt_interpret_compound_glyph.
+   RECURSION_COUNT is the number of times this function has called itself.
+   OFF_X and OFF_Y are the offsets to apply to the glyph outline.
+
+   METRICS are the unscaled metrics of this compound glyph.
+
+   Other arguments mean the same as they do in
+   `sfnt_interpret_compound_glyph'.  Value is NULL upon success, or a
+   string describing the reason for failure.  */
+
+static const char *
+sfnt_interpret_compound_glyph_1 (struct sfnt_glyph *glyph,
+                                struct sfnt_interpreter *interpreter,
+                                struct sfnt_graphics_state *state,
+                                struct sfnt_compound_glyph_context *context,
+                                sfnt_get_glyph_proc get_glyph,
+                                sfnt_free_glyph_proc free_glyph,
+                                struct sfnt_hmtx_table *hmtx,
+                                struct sfnt_hhea_table *hhea,
+                                struct sfnt_maxp_table *maxp,
+                                struct sfnt_glyph_metrics *metrics,
+                                sfnt_fixed off_x, sfnt_fixed off_y,
+                                int recursion_count,
+                                void *dcontext)
+{
+  struct sfnt_glyph *subglyph;
+  int i, j, rc;
+  const char *error;
+  bool need_free;
+  struct sfnt_compound_glyph_component *component;
+  sfnt_f26dot6 x, y, xtemp, ytemp;
+  size_t point, point2;
+  size_t last_point, number_of_contours;
+  sfnt_f26dot6 *x_base, *y_base;
+  size_t *contour_base;
+  unsigned char *flags_base;
+  size_t base_index, contour_start, base_contour;
+  bool defer_offsets;
+  struct sfnt_instructed_outline *value;
+  struct sfnt_glyph_metrics sub_metrics;
+
+  error = NULL;
+
+  /* Set up the base index.  This is the index from where on point
+     renumbering starts.
+
+     In other words, point 0 in this glyph will be 0 + base_index,
+     point 1 will be 1 + base_index, and so on.  */
+  base_index = context->num_points;
+
+  /* And this is the index of the first contour in this glyph.  */
+  base_contour = context->num_end_points;
+
+  /* Prevent infinite loops.  Simply limit the level of nesting to the
+     maximum valid value of `max_component_depth', which is 16.  */
+
+  if (recursion_count > 16)
+    return "Overly deep recursion in compound glyph data";
+
+  /* Pacify -Wmaybe-uninitialized.  */
+  point = point2 = 0;
+
+  for (j = 0; j < glyph->compound->num_components; ++j)
+    {
+      /* Look up the associated subglyph.  */
+      component = &glyph->compound->components[j];
+      subglyph = get_glyph (component->glyph_index,
+                           dcontext, &need_free);
+
+      if (!subglyph)
+       return "Failed to obtain component glyph";
+
+      /* Don't defer offsets.  This variable is set if the component
+        glyph is a compound glyph that is anchored to a previously
+        decomposed point, and needs its coordinates adjusted after
+        decomposition completes.  */
+      defer_offsets = false;
+
+      /* Record the size of the point array before expansion.  This
+        will be the base to apply to all points coming from this
+        subglyph.  */
+      contour_start = context->num_points;
+
+      /* Compute the offset for the component.  */
+      if (component->flags & 02) /* ARGS_ARE_XY_VALUES */
+       {
+         /* Component offsets are X/Y values as opposed to points
+            GLYPH.  */
+
+         if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */
+           {
+             /* X and Y are signed bytes.  */
+             x = component->argument1.b * 64;
+             y = component->argument2.b * 64;
+           }
+         else
+           {
+             /* X and Y are signed words.  */
+             x = component->argument1.d * 64;
+             y = component->argument2.d * 64;
+           }
+
+         /* Now convert X and Y into device coordinates.  */
+         x = sfnt_mul_f26dot6_fixed (x, interpreter->scale);
+         y = sfnt_mul_f26dot6_fixed (y, interpreter->scale);
+
+         /* If there is some kind of scale and component offsets are
+            scaled, then apply the transform to the offset.  */
+         if (component->flags & 04000) /* SCALED_COMPONENT_OFFSET */
+           sfnt_transform_f26dot6 (component, &x, &y, 1,
+                                   0, 0);
+
+         if (component->flags & 04) /* ROUND_XY_TO_GRID */
+           {
+             x = sfnt_round_f26dot6 (x);
+             y = sfnt_round_f26dot6 (y);
+           }
+       }
+      else
+       {
+         /* The offset is determined by matching a point location in
+            a preceeding component with a point location in the
+            current component.  The index of the point in the
+            previous component can be determined by adding
+            component->argument1.a or component->argument1.c to
+            point.  argument2 contains the index of the point in the
+            current component.  */
+
+         if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */
+           {
+             point = base_index + component->argument1.a;
+             point2 = component->argument2.a;
+           }
+         else
+           {
+             point = base_index + component->argument1.c;
+             point2 = component->argument2.c;
+           }
+
+         /* Now, check that the anchor point specified lies inside
+            the glyph.  */
+
+         if (point >= contour_start)
+           {
+             if (need_free)
+               free_glyph (subglyph, dcontext);
+
+             return "Invalid anchor point";
+           }
+
+         if (!subglyph->compound)
+           {
+             if (point2 >= subglyph->simple->number_of_points)
+               {
+                 if (need_free)
+                   free_glyph (subglyph, dcontext);
+
+                 return "Invalid anchored point";
+               }
+
+             /* Get the points and use them to compute the offsets.  */
+             xtemp = context->x_coordinates[point];
+             ytemp = context->y_coordinates[point];
+             x = (xtemp - subglyph->simple->x_coordinates[point2] * 64);
+             y = (ytemp - subglyph->simple->y_coordinates[point2] * 64);
+           }
+         else
+           {
+             /* First, set offsets to 0, because it is not yet
+                possible to determine the position of the anchor
+                point in the child.  */
+             x = 0;
+             y = 0;
+
+             /* Set a flag which indicates that offsets must be
+                resolved from the child glyph after it is loaded, but
+                before it is incorporated into the parent glyph.  */
+             defer_offsets = true;
+           }
+       }
+
+      /* Obtain the glyph metrics.  If doing so fails, then cancel
+        decomposition.  */
+
+      if (sfnt_lookup_glyph_metrics (component->glyph_index,
+                                    -1, &sub_metrics,
+                                    hmtx, hhea, NULL, maxp))
+       {
+         if (need_free)
+           free_glyph (subglyph, dcontext);
+
+         return "Failed to obtain component metrics";
+       }
+
+      if (subglyph->simple)
+       {
+         /* Simple subglyph.  Copy over the points and contours,
+            then transform and instruct them.
+
+            Skip this step for glyphs without contours.  */
+
+         if (subglyph->number_of_contours)
+           {
+             /* Now instruct the simple glyph, and copy it over,
+                including the two phantom points at the end.  */
+             interpreter->state = *state;
+             error = sfnt_interpret_simple_glyph (subglyph, interpreter,
+                                                  &sub_metrics, &value);
+
+             /* Cancel instructing if an error occurs.  */
+
+             if (error)
+               {
+                 if (need_free)
+                   free_glyph (subglyph, dcontext);
+
+                 return error;
+               }
+
+             /* Figure out how many more points and contours are
+                needed.  Here, last_point is not the end of the
+                glyph's contours, as two phantom points are
+                included.  */
+             last_point = value->num_points;
+             number_of_contours = value->num_contours;
+
+             /* Grow various arrays.  */
+             rc = sfnt_expand_compound_glyph_context (context,
+                                                      /* Number of
+                                                         new contours
+                                                         required.  */
+                                                      number_of_contours,
+                                                      /* Number of new
+                                                         points
+                                                         required.  */
+                                                      last_point,
+                                                      &x_base,
+                                                      &y_base,
+                                                      &flags_base,
+                                                      &contour_base);
+             if (rc)
+               {
+                 xfree (value);
+
+                 if (need_free)
+                   free_glyph (subglyph, dcontext);
+
+                 return "Failed to grow arrays";
+               }
+
+             /* Copy the values in VALUE into the context and free
+                VALUE, including phantom points.  */
+
+             for (i = 0; i < last_point; ++i)
+               {
+                 x_base[i] = value->x_points[i] + off_x + x;
+                 y_base[i] = value->y_points[i] + off_y + y;
+                 flags_base[i] = value->flags[i];
+               }
+
+             /* Copy over the contours.  */
+             for (i = 0; i < number_of_contours; ++i)
+               contour_base[i] = (contour_start
+                                  + value->contour_end_points[i]);
+
+             xfree (value);
+
+             /* Apply the transform to the points.  */
+             sfnt_transform_f26dot6 (component, x_base, y_base,
+                                     last_point, 0, 0);
+           }
+       }
+      else
+       {
+         /* Compound subglyph.  Decompose and instruct the glyph
+            recursively, and then apply the transform.  */
+
+         error = sfnt_interpret_compound_glyph_1 (subglyph, interpreter,
+                                                  state,
+                                                  context, get_glyph,
+                                                  free_glyph, hmtx, hhea,
+                                                  maxp, &sub_metrics,
+                                                  off_x + x, off_y + y,
+                                                  recursion_count + 1,
+                                                  dcontext);
+
+         if (error)
+           {
+             if (need_free)
+               free_glyph (subglyph, dcontext);
+
+             return error;
+           }
+
+         /* When an anchor point is being used to translate the
+            glyph, and the subglyph in question is actually a
+            compound glyph, it is impossible to know which offset to
+            use until the compound subglyph has actually been
+            loaded.
+
+            As a result, the offset is calculated here, using the
+            points in the loaded child compound glyph.  But first, X
+            and Y must be reset to 0, as otherwise the translation
+            might be applied twice if defer_offsets is not set.  */
+
+         x = 0;
+         y = 0;
+
+         if (defer_offsets)
+           {
+             /* Renumber the non renumbered point2 to point into the
+                decomposed component.  */
+             point2 += contour_start;
+
+             /* Next, check that the non-renumbered point being
+                anchored lies inside the glyph data that was
+                decomposed.  */
+
+             if (point2 >= context->num_points)
+               {
+                 if (need_free)
+                   free_glyph (subglyph, dcontext);
+
+                 return "Invalid point2";
+               }
+
+             /* Get the points and use them to compute the
+                offsets.  */
+
+             xtemp = context->x_coordinates[point];
+             ytemp = context->y_coordinates[point];
+             x = (xtemp - context->x_coordinates[point2]);
+             y = (ytemp - context->y_coordinates[point2]);
+           }
+
+         sfnt_transform_f26dot6 (component,
+                                 context->x_coordinates + contour_start,
+                                 context->y_coordinates + contour_start,
+                                 contour_start - context->num_points,
+                                 x, y);
+       }
+
+      /* Finally, free the subglyph.  */
+      if (need_free)
+       free_glyph (subglyph, dcontext);
+    }
+
+  /* Run the program for the entire compound glyph, if any.  */
+
+  if (glyph->compound->instruction_length)
+    {
+      interpreter->state = *state;
+      error = sfnt_interpret_compound_glyph_2 (glyph, interpreter,
+                                              context, base_index,
+                                              base_contour,
+                                              metrics);
+    }
+
+  return error;
+}
+
+/* Interpret the compound glyph GLYPH using the specified INTERPRETER.
+   Load each component of the compound glyph GLYPH.  CONTEXT should be
+   a reference to a `struct sfnt_compound_glyph_context' that is on
+   the stack.
+
+   Use glyph metrics specified in the HMTX, HHEA and MAXP tables, and
+   the unscaled metrics for GLYPH specified in METRICS.
+
+   If the component is a simple glyph, scale and instruct it
+   immediately.
+
+   If the component is a compound glyph, then load it recursively
+   until a predetermined recursion level is exceeded.
+
+   Set INTERPRETER's state to STATE prior to instructing each
+   component.
+
+   Load component glyphs using GET_GLYPH, which should return whether
+   or not FREE_GLYPH should be called with the glyph that was loaded.
+   Call both functions with DCONTEXT as an argument.
+
+   Finally, append the resulting contours, run any compound glyph
+   program, and return the instructed outline in *VALUE.
+
+   Value is NULL upon success, and the type of error upon failure.  */
+
+TEST_STATIC const char *
+sfnt_interpret_compound_glyph (struct sfnt_glyph *glyph,
+                              struct sfnt_interpreter *interpreter,
+                              struct sfnt_graphics_state *state,
+                              sfnt_get_glyph_proc get_glyph,
+                              sfnt_free_glyph_proc free_glyph,
+                              struct sfnt_hmtx_table *hmtx,
+                              struct sfnt_hhea_table *hhea,
+                              struct sfnt_maxp_table *maxp,
+                              struct sfnt_glyph_metrics *metrics,
+                              void *dcontext,
+                              struct sfnt_instructed_outline **value)
+{
+  struct sfnt_compound_glyph_context context;
+  const char *error;
+  struct sfnt_instructed_outline *outline;
+  size_t outline_size, temp;
+
+  /* Set up the glyph decomposition context.  */
+  memset (&context, 0, sizeof context);
+
+  /* Now start decomposing the glyph.  */
+  error = sfnt_interpret_compound_glyph_1 (glyph, interpreter,
+                                          state, &context,
+                                          get_glyph, free_glyph,
+                                          hmtx, hhea, maxp,
+                                          metrics, 0, 0, 0,
+                                          dcontext);
+
+  /* If an error occurs, free the data in the context and return.  */
+
+  if (error)
+    {
+      xfree (context.x_coordinates);
+      xfree (context.y_coordinates);
+      xfree (context.flags);
+      xfree (context.contour_end_points);
+      return error;
+    }
+
+  /* Copy the compound glyph data into an instructed outline.  */
+  outline_size = sizeof (*outline);
+
+  if (INT_MULTIPLY_WRAPV (context.num_end_points,
+                         sizeof *outline->contour_end_points,
+                         &temp)
+      || INT_ADD_WRAPV (outline_size, temp, &outline_size)
+      || INT_MULTIPLY_WRAPV (context.num_points,
+                            sizeof *outline->x_points * 2,
+                            &temp)
+      || INT_ADD_WRAPV (outline_size, temp, &outline_size)
+      || INT_ADD_WRAPV (context.num_points, outline_size,
+                       &outline_size))
+    {
+      xfree (context.x_coordinates);
+      xfree (context.y_coordinates);
+      xfree (context.flags);
+      xfree (context.contour_end_points);
+      return "Glyph exceeds maximum permissible size";
+    }
+
+  /* Allocate the outline.  */
+  outline = xmalloc (outline_size);
+  outline->num_points = context.num_points;
+  outline->num_contours = context.num_end_points;
+  outline->contour_end_points = (size_t *) (outline + 1);
+  outline->x_points = (sfnt_f26dot6 *) (outline->contour_end_points
+                                       + outline->num_contours);
+  outline->y_points = outline->x_points + outline->num_points;
+  outline->flags = (unsigned char *) (outline->y_points
+                                     + outline->num_points);
+
+  /* Copy over the contour endpoints, points, and flags.  Note that
+     all arrays in `context' are NULL unless num_contours is
+     non-zero.  */
+
+  if (context.num_end_points)
+    memcpy (outline->contour_end_points, context.contour_end_points,
+           outline->num_contours * sizeof *outline->contour_end_points);
+
+  if (context.num_points)
+    {
+      memcpy (outline->x_points, context.x_coordinates,
+             outline->num_points * sizeof *outline->x_points);
+      memcpy (outline->y_points, context.y_coordinates,
+             outline->num_points * sizeof *outline->y_points);
+      memcpy (outline->flags, context.flags, context.num_points);
+    }
+
+  /* Free the context data.  */
+  xfree (context.x_coordinates);
+  xfree (context.y_coordinates);
+  xfree (context.flags);
+  xfree (context.contour_end_points);
+
+  *value = outline;
+  return NULL;
+}
+
+
+
+
+/* Unicode Variation Sequence (UVS) support.
+
+   Unicode defines a mechanism by which a two-codepoint sequence
+   consisting of a ``base character'' and ``variation selector'' is
+   able to produce a glyph that is a variant of the glyph that would
+   conventionally have been mapped to the ``base character''.
+
+   TrueType describes variation selector sequences through a type of
+   character mapping table that is given the format 14.  The character
+   mapping table consists of an array of variation selectors, each of
+   which have a corresponding ``default UVS table'', which describes
+   ranges of ``base characters'' having no special variant glyphs, and
+   a ``non-default UVS table'', which is a map of ``base characters''
+   to their corresponding variant glyphs.  */
+
+/* Read a default UVS table from the font file FD, at the specified
+   OFFSET.  Value is the default UVS table upon success, else
+   NULL.  */
+
+static struct sfnt_default_uvs_table *
+sfnt_read_default_uvs_table (int fd, off_t offset)
+{
+  struct sfnt_default_uvs_table *uvs;
+  uint32_t num_ranges, i, j;
+  size_t size, temp;
+  char data[512];
+
+  /* First, seek to the given offset.  */
+
+  if (lseek (fd, offset, SEEK_SET) != offset)
+    return NULL;
+
+  /* Next, read the number of ranges present.  */
+
+  if (read (fd, &num_ranges, sizeof num_ranges) != sizeof num_ranges)
+    return NULL;
+
+  /* Swap the number of ranges present.  */
+  sfnt_swap32 (&num_ranges);
+
+  /* Now, allocate enough to hold the UVS table.  */
+
+  size = sizeof *uvs;
+  if (INT_MULTIPLY_WRAPV (sizeof *uvs->ranges, num_ranges,
+                         &temp)
+      || INT_ADD_WRAPV (temp, size, &size))
+    return NULL;
+
+  uvs = xmalloc (size);
+
+  /* Fill in the data which was already read.  */
+  uvs->num_unicode_value_ranges = num_ranges;
+
+  /* Fill in the pointer to the ranges.  */
+  uvs->ranges = (struct sfnt_unicode_value_range *) (uvs + 1);
+  i = 0;
+
+  /* Read each default UVS range in multiples of 512 bytes.  Then,
+     fill in uvs->ranges.  */
+
+  while (num_ranges)
+    {
+      size = (num_ranges > 128 ? 512 : num_ranges * 4);
+
+      if (read (fd, data, size) != size)
+       {
+         xfree (uvs);
+         return NULL;
+       }
+
+      for (j = 0; j < size / 4; ++j)
+       {
+         uvs->ranges[i + j].start_unicode_value
+           = sfnt_read_24 ((unsigned char *) data + j * 4);
+         uvs->ranges[i + j].additional_count = data[j * 4 + 1];
+       }
+
+      i += j;
+      num_ranges -= size / 4;
+    }
+
+  /* Return the resulting default UVS table.  */
+  return uvs;
+}
+
+/* Read a non-default UVS table from the font file FD, at the
+   specified OFFSET.  Value is the non-default UVS table upon success,
+   else NULL.  */
+
+static struct sfnt_nondefault_uvs_table *
+sfnt_read_nondefault_uvs_table (int fd, off_t offset)
+{
+  struct sfnt_nondefault_uvs_table *uvs;
+  uint32_t num_mappings, i, j;
+  size_t size, temp;
+  char data[500];
+
+  /* First, seek to the given offset.  */
+
+  if (lseek (fd, offset, SEEK_SET) != offset)
+    return NULL;
+
+  /* Next, read the number of mappings present.  */
+
+  if (read (fd, &num_mappings, sizeof num_mappings)
+      != sizeof num_mappings)
+    return NULL;
+
+  /* Swap the number of mappings present.  */
+  sfnt_swap32 (&num_mappings);
+
+  /* Now, allocate enough to hold the UVS table.  */
+
+  size = sizeof *uvs;
+  if (INT_MULTIPLY_WRAPV (sizeof *uvs->mappings, num_mappings,
+                         &temp)
+      || INT_ADD_WRAPV (temp, size, &size))
+    return NULL;
+
+  uvs = xmalloc (size);
+
+  /* Fill in the data which was already read.  */
+  uvs->num_uvs_mappings = num_mappings;
+
+  /* Fill in the pointer to the mappings.  */
+  uvs->mappings = (struct sfnt_uvs_mapping *) (uvs + 1);
+
+  i = 0;
+
+  /* Read each nondefault UVS mapping in multiples of 500 bytes.
+     Then, fill in uvs->ranges.  */
+
+  while (num_mappings)
+    {
+      size = (num_mappings > 100 ? 500 : num_mappings * 5);
+
+      if (read (fd, data, size) != size)
+       {
+         xfree (uvs);
+         return NULL;
+       }
+
+      for (j = 0; j < size / 5; ++j)
+       {
+         uvs->mappings[i + j].unicode_value
+           = sfnt_read_24 ((unsigned char *) data + j * 5);
+         memcpy (&uvs->mappings[i + j].base_character_value,
+                 data + j * 5 + 3,
+                 sizeof uvs->mappings[i + j].base_character_value);
+         sfnt_swap16 (&uvs->mappings[i + j].base_character_value);
+       }
+
+      i += j;
+      num_mappings -= size / 5;
+    }
+
+  /* Return the nondefault UVS table.  */
+  return uvs;
+}
+
+/* Perform comparison of A and B, two table offsets.  */
+
+static int
+sfnt_compare_table_offsets (const void *a, const void *b)
+{
+  const struct sfnt_table_offset_rec *rec_a, *rec_b;
+
+  rec_a = a;
+  rec_b = b;
+
+  if (rec_a->offset < rec_b->offset)
+    return -1;
+  else if (rec_a->offset > rec_b->offset)
+    return 1;
+
+  return 0;
+}
+
+/* Create a variation selection context based on the format 14 cmap
+   subtable CMAP.
+
+   FD is the font file to which the table belongs.
+
+   Value is the variation selection context upon success, else NULL.
+   The context contains each variation selector record and their
+   associated default and nondefault UVS tables.  Free the context
+   with `sfnt_free_uvs_context'.  */
+
+TEST_STATIC struct sfnt_uvs_context *
+sfnt_create_uvs_context (struct sfnt_cmap_format_14 *cmap, int fd)
+{
+  struct sfnt_table_offset_rec *table_offsets, *rec, template;
+  size_t size, i, nmemb, j;
+  off_t offset;
+  struct sfnt_uvs_context *context;
+
+  if (INT_MULTIPLY_WRAPV (cmap->num_var_selector_records,
+                         sizeof *table_offsets, &size)
+      || INT_MULTIPLY_WRAPV (size, 2, &size))
+    return NULL;
+
+  context = NULL;
+
+  /* First, record and sort the UVS and nondefault UVS table offsets
+     in ascending order.  */
+
+  table_offsets = xmalloc (size);
+  memset (table_offsets, 0, size);
+  nmemb = cmap->num_var_selector_records * 2;
+  j = 0;
+
+  for (i = 0; i < cmap->num_var_selector_records; ++i)
+    {
+      /* Note that either offset may be 0, meaning there is no such
+        table.  */
+
+      if (cmap->records[i].default_uvs_offset)
+       {
+         if (INT_ADD_WRAPV (cmap->offset,
+                            cmap->records[i].default_uvs_offset,
+                            &table_offsets[j].offset))
+           goto bail;
+
+         table_offsets[j++].is_nondefault_table = false;
+       }
+
+      if (cmap->records[i].nondefault_uvs_offset)
+       {
+         if (INT_ADD_WRAPV (cmap->offset,
+                            cmap->records[i].nondefault_uvs_offset,
+                            &table_offsets[j].offset))
+           goto bail;
+
+         table_offsets[j++].is_nondefault_table = true;
+       }
+    }
+
+  /* Make nmemb the number of offsets actually looked up.  */
+  nmemb = j;
+
+  qsort (table_offsets, nmemb, sizeof *table_offsets,
+        sfnt_compare_table_offsets);
+
+  /* Now go through table_offsets, and read everything.  nmemb is the
+     number of elements in table_offsets[i]; it is kept up to date
+     when duplicate members are removed.  */
+  offset = -1;
+
+  for (i = 0; i < nmemb; ++i)
+    {
+      /* Skip past duplicate tables.  */
+
+      while (table_offsets[i].offset == offset && i < nmemb)
+       {
+         nmemb--;
+         table_offsets[i] = table_offsets[i + 1];
+       }
+
+      /* If the last element of the array is a duplicate, break out of
+        the loop.  */
+
+      if (i == nmemb)
+       break;
+
+      /* Read the correct type of table depending on
+        table_offsets[i].is_nondefault_table.  Then skip past
+        duplicate tables.  Don't handle the case where two different
+        kind of tables share the same offset, because that is not
+        possible in a valid variation selector record.  */
+
+      offset = table_offsets[i].offset;
+
+      if (table_offsets[i].is_nondefault_table)
+       table_offsets[i].table
+         = sfnt_read_nondefault_uvs_table (fd, offset);
+      else
+       table_offsets[i].table
+         = sfnt_read_default_uvs_table (fd, offset);
+    }
+
+  /* Now make the context.  */
+  context = xmalloc (sizeof *context);
+  context->num_records = cmap->num_var_selector_records;
+  context->nmemb = nmemb;
+  context->records = xmalloc (sizeof *context->records
+                             * cmap->num_var_selector_records);
+
+  for (i = 0; i < cmap->num_var_selector_records; ++i)
+    {
+      context->records[i].selector = cmap->records[i].var_selector;
+
+      /* Either offset may be 0, meaning no such table exists.  Also,
+         the code below will lose if more than one kind of table
+         shares the same offset, because that is impossible.  */
+
+      if (cmap->records[i].default_uvs_offset)
+       {
+         /* Resolve the default table.  */
+         template.offset = (cmap->records[i].default_uvs_offset
+                            + cmap->offset);
+         rec = bsearch (&template, table_offsets,
+                        nmemb, sizeof *table_offsets,
+                        sfnt_compare_table_offsets);
+
+         /* Make sure this record is the right type.  */
+         if (!rec || rec->is_nondefault_table || !rec->table)
+           goto bail;
+
+         context->records[i].default_uvs = rec->table;
+       }
+      else
+       context->records[i].default_uvs = NULL;
+
+      if (cmap->records[i].nondefault_uvs_offset)
+       {
+         /* Resolve the nondefault table.  */
+         template.offset = (cmap->records[i].nondefault_uvs_offset
+                            + cmap->offset);
+         rec = bsearch (&template, table_offsets,
+                        nmemb, sizeof *table_offsets,
+                        sfnt_compare_table_offsets);
+
+         if (!rec)
+           goto bail;
+
+         /* Make sure this record is the right type.  */
+         if (!rec || !rec->is_nondefault_table || !rec->table)
+           goto bail;
+
+         context->records[i].nondefault_uvs = rec->table;
+       }
+      else
+       context->records[i].nondefault_uvs = NULL;
+    }
+
+  context->tables = table_offsets;
+  return context;
+
+ bail:
+
+  if (context)
+    {
+      xfree (context->records);
+      xfree (context);
+    }
+
+  /* Loop through and free any tables that might have been read
+     already.  */
+
+  for (i = 0; i < nmemb; ++i)
+    xfree (table_offsets[i].table);
+
+  xfree (table_offsets);
+  return NULL;
+}
+
+/* Free the specified variation selection context C.  */
+
+TEST_STATIC void
+sfnt_free_uvs_context (struct sfnt_uvs_context *c)
+{
+  size_t i;
+
+  xfree (c->records);
+
+  for (i = 0; i < c->nmemb; ++i)
+    xfree (c->tables[i].table);
+
+  xfree (c->tables);
+  xfree (c);
+}
+
+/* Compare *(sfnt_char *) K to ((struct sfnt_uvs_mapping *)
+   V)->unicode_value appropriately for bsearch.  */
+
+static int
+sfnt_compare_uvs_mapping (const void *k, const void *v)
+{
+  const sfnt_char *key;
+  const struct sfnt_uvs_mapping *value;
+
+  key = k;
+  value = v;
+
+  if (*key < value->unicode_value)
+    return -1;
+  else if (*key == value->unicode_value)
+    return 0;
+
+  return 1;
+}
+
+/* Return the ID of a variation glyph for the character C in the
+   nondefault UVS mapping table UVS.
+
+   Value is the glyph ID upon success, or 0 if there is no variation
+   glyph for the base character C.  */
+
+TEST_STATIC sfnt_glyph
+sfnt_variation_glyph_for_char (struct sfnt_nondefault_uvs_table *uvs,
+                              sfnt_char c)
+{
+  struct sfnt_uvs_mapping *mapping;
+
+  mapping = bsearch (&c, uvs->mappings, uvs->num_uvs_mappings,
+                    sizeof *uvs->mappings,
+                    sfnt_compare_uvs_mapping);
+
+  return mapping ? mapping->base_character_value : 0;
+}
+
+
+
+#if defined HAVE_MMAP && !defined TEST
+
+/* Memory mapping support.
+   It useful to map OpenType layout tables prior to using them in
+   an external shaping engine such as HarfBuzz.  */
+
+/* Map a table identified by TAG into the structure *TABLE.
+   TAG is swapped into host byte order.
+
+   Use the table directory SUBTABLE, which corresponds to the font
+   file FD.
+
+   Return 0 upon success, and set TABLE->data to the table data,
+   TABLE->mapping to the start of the mapped area, TABLE->length to
+   the length of the table contents, and TABLE->size to the size of
+   the mapping.
+
+   Return 1 upon failure.  */
+
+int
+sfnt_map_table (int fd, struct sfnt_offset_subtable *subtable,
+               uint32_t tag, struct sfnt_mapped_table *table)
+{
+  struct sfnt_table_directory *directory;
+  size_t offset, page, map_offset;
+  void *data;
+  int i;
+
+  /* Find the table in the directory.  */
+
+  for (i = 0; i < subtable->num_tables; ++i)
+    {
+      if (subtable->subtables[i].tag == tag)
+       {
+         directory = &subtable->subtables[i];
+         break;
+       }
+    }
+
+  if (i == subtable->num_tables)
+    return 1;
+
+  /* Now try to map the glyph data.  Make sure offset is a multiple of
+     the page size.  */
+
+  page = getpagesize ();
+  offset = directory->offset & ~(page - 1);
+
+  /* Figure out how much larger the mapping should be.  */
+  map_offset = directory->offset - offset;
+
+  /* Do the mmap.  */
+  data = mmap (NULL, directory->length + map_offset,
+              PROT_READ, MAP_PRIVATE, fd, offset);
+
+  if (data == MAP_FAILED)
+    return 1;
+
+  /* Fill in *TABLE.  */
+  table->data = (unsigned char *) data + map_offset;
+  table->mapping = data;
+  table->length = directory->length;
+  table->size = directory->length + map_offset;
+  return 0;
+}
+
+/* Unmap the table inside *TABLE.
+   Value is 0 upon success, 1 otherwise.  */
+
+int
+sfnt_unmap_table (struct sfnt_mapped_table *table)
+{
+  return munmap (table->mapping, table->size) != 0;
+}
+
+#endif /* HAVE_MMAP && !TEST */
+
+
+
+#ifndef TEST
+
+/* Reading table contents.  */
+
+/* Read the table with the specified TAG from the font file FD.
+   Return its length in *LENGTH, and its data upon success, else
+   NULL.  */
+
+void *
+sfnt_read_table (int fd, struct sfnt_offset_subtable *subtable,
+                uint32_t tag, size_t *length)
+{
+  struct sfnt_table_directory *directory;
+  void *data;
+  int i;
+
+  /* Find the table in the directory.  */
+
+  for (i = 0; i < subtable->num_tables; ++i)
+    {
+      if (subtable->subtables[i].tag == tag)
+       {
+         directory = &subtable->subtables[i];
+         break;
+       }
+    }
+
+  if (i == subtable->num_tables)
+    return NULL;
+
+  /* Seek to the table.  */
+
+  if (lseek (fd, directory->offset, SEEK_SET) != directory->offset)
+    return NULL;
+
+  /* Now allocate enough to hold the data and read into it.  */
+
+  data = xmalloc (directory->length);
+  if (read (fd, data, directory->length) != directory->length)
+    {
+      xfree (data);
+      return NULL;
+    }
+
+  /* Return the length and table data.  */
+  *length = directory->length;
+  return data;
+}
+
+#endif /* !TEST */
+
+
+
+/* Glyph variations.  Instead of defining separate fonts for each
+   combination of weight, width and slant (bold, condensed, italic,
+   etc), some fonts specify a list of ``variation axes'', each of
+   which determines one delta to apply to each point in every
+   glyph.
+
+   This optional information is specified in the `fvar' (font
+   variation), `gvar' (glyph variation) and `cvar' (CVT variation)
+   tables in a font file.  */
+
+/* Read an fvar table from the given font FD.  Use the table directory
+   specified in SUBTABLE.
+
+   Return the fvar table upon success, else NULL.  */
+
+TEST_STATIC struct sfnt_fvar_table *
+sfnt_read_fvar_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+  struct sfnt_table_directory *directory;
+  struct sfnt_fvar_table *fvar;
+  ssize_t rc;
+  size_t min_bytes, ps_size, non_ps_size, temp, pad;
+  off_t offset;
+  int i, j;
+  char *buffer;
+  sfnt_fixed *coords;
+
+  /* Find the table in the directory.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_FVAR);
+
+  if (!directory)
+    return NULL;
+
+  min_bytes = SFNT_ENDOF (struct sfnt_fvar_table,
+                         instance_size, uint16_t);
+
+  /* Check that the length is at least min_bytes.  */
+  if (directory->length < min_bytes)
+    return NULL;
+
+  /* Seek to the location given in the directory.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  /* Allocate enough to hold the fvar table header.  */
+  fvar = xmalloc (sizeof *fvar);
+
+  /* Read the fvar table header.  */
+  buffer = NULL;
+  rc = read (fd, fvar, min_bytes);
+  if (rc != min_bytes)
+    goto bail;
+
+  /* Swap what was read.  */
+  sfnt_swap16 (&fvar->major_version);
+  sfnt_swap16 (&fvar->minor_version);
+  sfnt_swap16 (&fvar->offset_to_data);
+  sfnt_swap16 (&fvar->count_size_pairs);
+  sfnt_swap16 (&fvar->axis_count);
+  sfnt_swap16 (&fvar->axis_size);
+  sfnt_swap16 (&fvar->instance_count);
+  sfnt_swap16 (&fvar->instance_size);
+
+  /* major_version should be 1, and minor_version 0.  */
+
+  if (fvar->major_version != 1 || fvar->minor_version)
+    goto bail;
+
+  /* count_size_pairs should be more than 2.  */
+
+  if (fvar->count_size_pairs < 2)
+    goto bail;
+
+  /* Don't try to read tables where the axis format differs.  */
+
+  if (fvar->axis_size != 20)
+    goto bail;
+
+  /* The instance size must either be 2 * sizeof (uint16_t) +
+     axisCount * sizeof (sfnt_fixed), meaning there is no PostScript
+     name identifier, or 3 * sizeof (uint16_t) + axisCount * sizeof
+     (sfnt_fixed), meaning there is.  */
+
+  if (INT_MULTIPLY_WRAPV (fvar->axis_count, sizeof (sfnt_fixed),
+                         &temp)
+      || INT_ADD_WRAPV (2 * sizeof (uint16_t), temp, &non_ps_size))
+    goto bail;
+
+  if (INT_MULTIPLY_WRAPV (fvar->axis_count, sizeof (sfnt_fixed),
+                         &temp)
+      || INT_ADD_WRAPV (3 * sizeof (uint16_t), temp, &ps_size))
+    goto bail;
+
+  if (fvar->instance_size != non_ps_size
+      && fvar->instance_size != ps_size)
+    goto bail;
+
+  /* Now compute the offset of the axis data from the start of the
+     font file.  */
+
+  if (INT_ADD_WRAPV (fvar->offset_to_data, directory->offset,
+                    &offset))
+    goto bail;
+
+  /* Seek there.  */
+
+  if (lseek (fd, offset, SEEK_SET) != offset)
+    goto bail;
+
+  min_bytes = sizeof *fvar;
+
+  /* Now, read each axis and instance.  Calculate how much extra data
+     needs to be allocated for the axes and instances: this is
+     fvar->axis_count * sizeof (struct sfnt_variation_axis), some
+     padding, and finally fvar->instance_count * sizeof (struct
+     sfnt_instance) + sizeof (sfnt_fixed) * fvar->instance_count *
+     fvar->axis_count.  */
+
+  if (INT_MULTIPLY_WRAPV (fvar->axis_count, sizeof *fvar->axis,
+                         &temp)
+      || INT_ADD_WRAPV (min_bytes, temp, &min_bytes))
+    goto bail;
+
+  pad = alignof (struct sfnt_variation_axis);
+  pad -= min_bytes & (pad - 1);
+
+  if (INT_ADD_WRAPV (min_bytes, pad, &min_bytes))
+    goto bail;
+
+  if (INT_MULTIPLY_WRAPV (fvar->instance_count,
+                         sizeof *fvar->instance,
+                         &temp)
+      || INT_ADD_WRAPV (min_bytes, temp, &min_bytes))
+    goto bail;
+
+  if (INT_MULTIPLY_WRAPV (fvar->instance_count,
+                         sizeof *fvar->instance->coords,
+                         &temp)
+      || INT_MULTIPLY_WRAPV (temp, fvar->axis_count, &temp)
+      || INT_ADD_WRAPV (min_bytes, temp, &min_bytes))
+    goto bail;
+
+  /* Reallocate fvar.  */
+  fvar = xrealloc (fvar, min_bytes);
+
+  /* Fill in offsets.  */
+  fvar->axis = (struct sfnt_variation_axis *) (fvar + 1);
+  fvar->instance
+    = (struct sfnt_instance *) (((char *) (fvar->axis
+                                          + fvar->axis_count))
+                               + pad);
+
+  /* Read axes.  */
+
+  if (directory->length - SFNT_ENDOF (struct sfnt_fvar_table,
+                                     instance_size, uint16_t)
+      < sizeof *fvar->axis * fvar->axis_count)
+    goto bail;
+
+  rc = read (fd, fvar->axis, sizeof *fvar->axis * fvar->axis_count);
+  if (rc != sizeof *fvar->axis * fvar->axis_count)
+    goto bail;
+
+  /* Swap each axis.  */
+
+  for (i = 0; i < fvar->axis_count; ++i)
+    {
+      sfnt_swap32 (&fvar->axis[i].axis_tag);
+      sfnt_swap32 (&fvar->axis[i].min_value);
+      sfnt_swap32 (&fvar->axis[i].default_value);
+      sfnt_swap32 (&fvar->axis[i].max_value);
+      sfnt_swap16 (&fvar->axis[i].flags);
+      sfnt_swap16 (&fvar->axis[i].name_id);
+    }
+
+  /* Read each instance.  */
+
+  if (fvar->instance_size < 1024 * 16)
+    buffer = alloca (fvar->instance_size);
+  else
+    buffer = xmalloc (fvar->instance_size);
+
+  coords = (sfnt_fixed *) (fvar->instance + fvar->instance_count);
+
+  for (i = 0; i < fvar->instance_count; ++i)
+    {
+      rc = read (fd, buffer, fvar->instance_size);
+      if (rc != fvar->instance_size)
+       goto bail;
+
+      /* Fill in various fields.  */
+
+      fvar->instance[i].name_id = *((uint16_t *) buffer);
+      fvar->instance[i].flags = *((uint16_t *) buffer + 1);
+      fvar->instance[i].ps_name_id = 0;
+
+      sfnt_swap16 (&fvar->instance[i].name_id);
+      sfnt_swap16 (&fvar->instance[i].flags);
+
+      /* Read coordinates.  */
+
+      fvar->instance[i].coords = coords;
+      coords += fvar->axis_count;
+
+      memcpy (fvar->instance[i].coords, buffer + 4,
+             sizeof *fvar->instance[i].coords * fvar->axis_count);
+
+      /* Swap coordinates.  */
+
+      for (j = 0; j < fvar->axis_count; ++j)
+       sfnt_swap32 (&fvar->instance[i].coords[j]);
+
+      /* Read the PostScript name ID if necessary.  If not, set it to
+        nil.  */
+
+      if (fvar->instance_size == ps_size)
+       {
+         fvar->instance[i].ps_name_id
+           = *(uint16_t *) (buffer + 4 + (sizeof *fvar->instance[i].coords
+                                          * fvar->axis_count));
+         sfnt_swap16 (&fvar->instance[i].ps_name_id);
+       }
+    }
+
+  /* Free the temporary buffer.  */
+  if (buffer && fvar->instance_size >= 1024 * 16)
+    xfree (buffer);
+
+  /* Return the fvar table.  */
+  return fvar;
+
+ bail:
+  if (buffer && fvar->instance_size >= 1024 * 16)
+    xfree (buffer);
+
+  xfree (fvar);
+  return NULL;
+}
+
+
+
+/* Read a gvar table from the given font FD.  Use the table directory
+   specified in SUBTABLE.
+
+   Return the gvar table upon success, else NULL.  */
+
+TEST_STATIC struct sfnt_gvar_table *
+sfnt_read_gvar_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+  struct sfnt_table_directory *directory;
+  struct sfnt_gvar_table *gvar;
+  ssize_t rc;
+  size_t min_bytes, off_size, coordinate_size, data_size;
+  int i;
+  off_t offset;
+
+  /* Find the table in the directory.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_GVAR);
+
+  if (!directory)
+    return NULL;
+
+  min_bytes = SFNT_ENDOF (struct sfnt_gvar_table,
+                         offset_to_data, uint32_t);
+
+  /* Check that the length is at least min_bytes.  */
+  if (directory->length < min_bytes)
+    return NULL;
+
+  /* Seek to the location given in the directory.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  /* Allocate enough to hold the gvar table header.  */
+  gvar = xmalloc (sizeof *gvar);
+
+  /* Read the gvar table header.  */
+  rc = read (fd, gvar, min_bytes);
+  if (rc != min_bytes)
+    goto bail;
+
+  /* Swap what was read.  */
+  sfnt_swap16 (&gvar->version);
+  sfnt_swap16 (&gvar->reserved);
+  sfnt_swap16 (&gvar->axis_count);
+  sfnt_swap16 (&gvar->shared_coord_count);
+  sfnt_swap32 (&gvar->offset_to_coord);
+  sfnt_swap16 (&gvar->glyph_count);
+  sfnt_swap16 (&gvar->flags);
+  sfnt_swap32 (&gvar->offset_to_data);
+
+  if (gvar->version != 1)
+    goto bail;
+
+  if (gvar->offset_to_data > directory->length)
+    goto bail;
+
+  /* Figure out the size required for the offset array.  Note that
+     there is one extra offset at the end of the array to mark the
+     size of the last glyph.  */
+
+  if (gvar->flags & 1)
+    /* Offsets are long words.  */
+    off_size = sizeof (uint32_t) * (gvar->glyph_count + 1);
+  else
+    /* Offsets are words.  */
+    off_size = sizeof (uint16_t) * (gvar->glyph_count + 1);
+
+  /* Now figure out the size of the shared coordinates.  */
+  coordinate_size = (gvar->shared_coord_count * gvar->axis_count
+                    * sizeof (uint16_t));
+
+  /* And the size of the glyph variation data.  */
+  data_size = directory->length - gvar->offset_to_data;
+
+  /* Wraparound.  */
+  if (data_size > directory->length)
+    goto bail;
+
+  /* Figure out how big gvar needs to be.  */
+  if (INT_ADD_WRAPV (sizeof *gvar, coordinate_size, &min_bytes)
+      || INT_ADD_WRAPV (min_bytes, off_size, &min_bytes)
+      || INT_ADD_WRAPV (min_bytes, data_size, &min_bytes))
+    goto bail;
+
+  /* Now allocate enough for all of this extra data.  */
+  gvar = xrealloc (gvar, min_bytes);
+
+  /* Start reading offsets.  */
+
+  if (!(gvar->flags & 1))
+    {
+      gvar->u.offset_word = (uint16_t *) (gvar + 1);
+      rc = read (fd, gvar->u.offset_word, off_size);
+      if (rc != off_size)
+       goto bail;
+
+      for (i = 0; i <= gvar->glyph_count; ++i)
+       sfnt_swap16 (&gvar->u.offset_word[i]);
+    }
+  else
+    {
+      gvar->u.offset_long = (uint32_t *) (gvar + 1);
+      rc = read (fd, gvar->u.offset_long, off_size);
+      if (rc != off_size)
+       goto bail;
+
+      for (i = 0; i <= gvar->glyph_count; ++i)
+       sfnt_swap32 (&gvar->u.offset_long[i]);
+    }
+
+  /* Start reading shared coordinates.  */
+
+  gvar->global_coords = ((sfnt_f2dot14 *) ((char *) gvar + off_size));
+
+  if (gvar->shared_coord_count)
+    {
+      if (INT_ADD_WRAPV (gvar->offset_to_coord, directory->offset,
+                        &offset))
+       goto bail;
+
+      if (lseek (fd, offset, SEEK_SET) != offset)
+       goto bail;
+
+      if (read (fd, gvar->global_coords, coordinate_size)
+         != coordinate_size)
+       goto bail;
+
+      for (i = 0; i <= coordinate_size / sizeof *gvar->global_coords; ++i)
+       sfnt_swap16 (&gvar->global_coords[i]);
+    }
+
+  /* Finally, read the rest of the glyph variation data.  */
+  gvar->data_size = data_size;
+  gvar->glyph_variation_data
+    = (unsigned char *) (gvar->global_coords
+                        + (coordinate_size
+                           / sizeof *gvar->global_coords));
+
+  if (gvar->data_size)
+    {
+      if (INT_ADD_WRAPV (gvar->offset_to_data, directory->offset,
+                        &offset))
+       goto bail;
+
+      if (lseek (fd, offset, SEEK_SET) != offset)
+       goto bail;
+
+      if (read (fd, gvar->glyph_variation_data,
+               gvar->data_size) != gvar->data_size)
+       goto bail;
+    }
+
+  /* Return the read gvar table.  */
+  return gvar;
+
+ bail:
+  xfree (gvar);
+  return NULL;
+}
+
+
+
+/* Read an avar table from the given font FD.  Use the table directory
+   specified in SUBTABLE.
+
+   Return the avar table upon success, else NULL.  */
+
+TEST_STATIC struct sfnt_avar_table *
+sfnt_read_avar_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+  struct sfnt_table_directory *directory;
+  struct sfnt_avar_table *avar;
+  ssize_t rc;
+  size_t min_size, size, i, k, j;
+  uint16_t *buffer;
+  struct sfnt_short_frac_correspondence *correspondences;
+
+  /* Find the table in the directory.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_AVAR);
+
+  if (!directory)
+    return NULL;
+
+  min_size = SFNT_ENDOF (struct sfnt_avar_table, axis_count, uint32_t);
+
+  /* Check that the length is at least min_size.  */
+  if (directory->length < min_size)
+    return NULL;
+
+  /* Seek to the location given in the directory.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  /* Allocate enough to hold the avar table header.  */
+  avar = xmalloc (sizeof *avar);
+
+  /* Read the avar table header.  */
+  rc = read (fd, avar, min_size);
+  if (rc != min_size)
+    goto bail;
+
+  /* Swap what was read.  */
+  sfnt_swap32 (&avar->version);
+  sfnt_swap32 (&avar->axis_count);
+
+  if (avar->version != 0x00010000)
+    goto bail;
+
+  if (avar->axis_count < 0)
+    goto bail;
+
+  /* Allocate a buffer that holds the rest of the data.  */
+  size = directory->length - min_size;
+  buffer = xmalloc (size);
+  rc = read (fd, buffer, size);
+  if (rc != size)
+    goto bail1;
+
+  /* Swap each word.  */
+  for (i = 0; i < size / sizeof *buffer; ++i)
+    sfnt_swap16 (&buffer[i]);
+
+  /* Now, determine how big the resulting data needs to be.  Each
+     struct has a pointer field, and that should be its alignment.  */
+
+  k = 0;
+  min_size = sizeof *avar;
+  for (i = 0; i < avar->axis_count; ++i)
+    {
+      /* Check that k remains within bounds.  */
+      if (k >= size / sizeof *buffer)
+       goto bail1;
+
+      /* Now add one struct sfnt_short_frac_segment for each axis and
+         each of its correspondences.  */
+      if (INT_ADD_WRAPV (sizeof (struct sfnt_short_frac_segment),
+                        min_size, &min_size)
+         || INT_ADD_WRAPV (sizeof (struct sfnt_short_frac_correspondence)
+                           * buffer[k], min_size, &min_size))
+       goto bail1;
+
+      /* Verify that words from here to buffer[1 + buffer[k] * 2], the
+        next pairCount field, are within bounds.  */
+      j = k + 1 + buffer[k] * 2;
+      if (j > size / sizeof *buffer)
+       goto bail1;
+
+      /* Move to the next pairCount field.  */
+      k = j;
+    }
+
+  /* Resize avar to min_size and start filling in various
+     pointers.  */
+  avar = xrealloc (avar, min_size);
+  avar->segments = (struct sfnt_short_frac_segment *) (avar + 1);
+  correspondences
+    = ((struct sfnt_short_frac_correspondence *) (avar->segments
+                                                 + avar->axis_count));
+
+  k = 0;
+  for (i = 0; i < avar->axis_count; ++i)
+    {
+      avar->segments[i].pair_count = buffer[k++];
+      avar->segments[i].correspondence = correspondences;
+
+      for (j = 0; j < avar->segments[i].pair_count; ++j)
+       {
+         correspondences->from_coord = buffer[k++];
+         correspondences->to_coord = buffer[k++];
+         correspondences++;
+       }
+    }
+
+  /* Return the read avar table.  Free buffer.  */
+  xfree (buffer);
+  return avar;
+
+ bail1:
+  xfree (buffer);
+ bail:
+  xfree (avar);
+  return NULL;
+}
+
+
+
+/* Read a sequence of packed points starting from DATA.  Return the
+   number of points read in *NPOINTS_RETURN and the array of unpacked
+   points, or NULL upon failure.
+
+   If non-NULL, set LOCATION to DATA plus the number of bytes read
+   upon success.
+
+   Return (uint16_t *) -1 if there are no points at all.
+   In this case, deltas will apply to all points in the glyph,
+   and *NPOINTS_RETURN will be UINT16_MAX.
+
+   END is one byte past the last byte in DATA.  */
+
+static uint16_t *
+sfnt_read_packed_points (unsigned char *restrict data,
+                        uint16_t *npoints_return,
+                        unsigned char *restrict end,
+                        unsigned char *restrict *location)
+{
+  int npoints;
+  uint16_t *points;
+  int i, first, control;
+
+  points = NULL;
+  npoints = 0;
+
+  if (data >= end)
+    return NULL;
+
+  /* Load the control byte.  */
+  control = *data++;
+
+  if (!control)
+    {
+      *npoints_return = UINT16_MAX;
+      *location = data;
+      return (uint16_t *) -1;
+    }
+
+  /* Now figure out the number of points within.  */
+
+  if (control & 0x80)
+    {
+      npoints = control & 0x7f;
+      npoints <<= 8;
+
+      if (data >= end)
+       return NULL;
+
+      npoints |= *data++;
+    }
+  else
+    npoints = control;
+
+  /* Start reading points.  */
+  first = 0;
+  i = 0;
+  points = xmalloc (sizeof *points * npoints);
+
+  while (i < npoints)
+    {
+      if (data >= end)
+       goto bail;
+
+      control = *data++;
+
+      if (control & 0x80)
+       {
+         /* Next control & 0x7f words are points.  */
+
+         control &= 0x7f;
+
+         while (control != -1 && i < npoints)
+           {
+             if (data >= end || data + 1 >= end)
+               goto bail;
+
+             first += *data++ << 8u;
+             first += *data++;
+             points[i] = first;
+             control -= 1, ++i;
+           }
+       }
+      else
+       {
+         /* Next control bytes are points.  */
+
+         while (control != -1 && i < npoints)
+           {
+             if (data >= end)
+               goto bail;
+
+             first += *data++;
+             points[i] = first;
+             control -= 1, ++i;
+           }
+       }
+    }
+
+  /* Return the points read.  */
+  *npoints_return = npoints;
+  *location = data;
+  return points;
+
+ bail:
+  xfree (points);
+  return NULL;
+}
+
+/* Read and return N packed deltas from DATA.  Set *DATA_RETURN to
+   DATA plus the number of bytes read.
+
+   END is the end of the glyph variation data.  Value is an array of N
+   deltas upon success, and NULL upon failure.  */
+
+static sfnt_fword *
+sfnt_read_packed_deltas (unsigned char *restrict data,
+                        unsigned char *restrict end,
+                        int n,
+                        unsigned char *restrict *data_return)
+{
+  sfnt_fword *deltas;
+  int i, count;
+  unsigned char control;
+  uint16_t value;
+
+  if (data >= end)
+    return NULL;
+
+  deltas = xmalloc (sizeof *deltas * n);
+  i = 0;
+
+  while (i < n)
+    {
+      if (data >= end)
+       goto fail;
+
+      control = *data++;
+      count = control & 0x3f;
+
+      while (count != -1 && i < n)
+       {
+         if (control & 0x80)
+           deltas[i++] = 0;
+         else if (control & 0x40)
+           {
+             if (data + 1 >= end)
+               goto fail;
+
+             value = *data++ << 8;
+             value |= *data++;
+             deltas[i++] = value;
+           }
+         else
+           {
+             if (data >= end)
+               goto fail;
+
+             deltas[i++] = (signed char) *data++;
+           }
+
+         --count;
+       }
+    }
+
+  *data_return = data;
+  return deltas;
+
+ fail:
+  xfree (deltas);
+  return NULL;
+}
+
+/* Read a cvar table from the given font FD.  Use the table directory
+   specified in SUBTABLE, axis information provided in the fvar table
+   FVAR, and CVT information provided in the cvt table CVT.
+
+   Return the cvar table upon success, else NULL.  */
+
+TEST_STATIC struct sfnt_cvar_table *
+sfnt_read_cvar_table (int fd, struct sfnt_offset_subtable *subtable,
+                     struct sfnt_fvar_table *fvar,
+                     struct sfnt_cvt_table *cvt)
+{
+  struct sfnt_table_directory *directory;
+  struct sfnt_cvar_table *cvar;
+  ssize_t rc;
+  size_t ntuples, size;
+  int i, j;
+  sfnt_f2dot14 *coords;
+  uint16_t *local, *points, npoints, data_size, min_size, index;
+  unsigned char *buffer, *data, *end, *tuple;
+  ptrdiff_t data_offset;
+  sfnt_fword *deltas;
+
+  /* Find the table in the directory.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_CVAR);
+
+  if (!directory)
+    return NULL;
+
+  min_size = SFNT_ENDOF (struct sfnt_cvar_table, data_offset,
+                        uint16_t);
+
+  /* Check that the length is at least min_size.  */
+  if (directory->length < min_size)
+    return NULL;
+
+  /* Seek to the location given in the directory.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  /* Allocate enough to hold the cvar table header.  */
+  cvar = xmalloc (sizeof *cvar);
+
+  /* Read the cvar table header.  */
+  rc = read (fd, cvar, min_size);
+  if (rc != min_size)
+    goto bail;
+
+  /* Swap what was read.  */
+  sfnt_swap32 (&cvar->version);
+  sfnt_swap16 (&cvar->tuple_count);
+  sfnt_swap16 (&cvar->data_offset);
+
+  /* Read the rest of the table.  */
+  size = directory->length - min_size;
+  buffer = xmalloc (size);
+  rc = read (fd, buffer, size);
+  if (rc != size)
+    goto bail;
+
+  /* Now figure out how large cvar must be by reading the tuples.  */
+
+  ntuples = cvar->tuple_count & 0x0fff;
+  data_offset = ((ptrdiff_t) cvar->data_offset
+                - (ptrdiff_t) min_size);
+  end = buffer + size;
+
+  if (data_offset < 0)
+    goto bail1;
+
+  /* See if there are shared points, and read them if there are.  */
+
+  data = buffer + data_offset;
+  tuple = buffer;
+  points = NULL;
+
+  /* Initialize `npoints' to zero.  The specification doesn't say what
+     should happen with tuples using shared point numbers if it is not
+     set later on; simply assume no points at all apply to such a
+     tuple.  */
+
+  npoints = 0;
+
+  /* Initialize `size' to 0.  */
+  size = 0;
+
+  if (cvar->tuple_count & 0x8000)
+    {
+      points = sfnt_read_packed_points (data, &npoints, end,
+                                       &tuple);
+      if (!points)
+       goto bail1;
+
+      /* Add npoints words to the size.  If npoints is UINT16_MAX, no
+        coordinates will actually be allocated.  */
+
+      if (npoints != UINT16_MAX)
+       size = npoints * sizeof *points;
+    }
+
+  while (ntuples--)
+    {
+      data = buffer + data_offset;
+
+      /* Read the tuple.  */
+      if (tuple + 3 >= end)
+       goto bail2;
+
+      memcpy (&data_size, tuple, sizeof data_size);
+      tuple += sizeof data_size;
+      memcpy (&index, tuple, sizeof index);
+      tuple += sizeof index;
+      sfnt_swap16 (&data_size);
+      sfnt_swap16 (&index);
+
+      /* Increment the offset to the data by the data size specified
+        here.  */
+      data_offset += data_size;
+
+      if (index & 0x8000)
+       {
+         /* Embedded coordinates are present.  Read each coordinate
+            and add it to the size.  */
+
+         if (tuple + fvar->axis_count * sizeof *coords - 1 >= end)
+           goto bail2;
+
+         tuple += sizeof *coords * fvar->axis_count;
+         if (INT_ADD_WRAPV (size, sizeof *coords * fvar->axis_count,
+                            &size))
+           goto bail2;
+       }
+      else
+       /* This table is invalid, as cvar tables don't have global
+          coordinates.  */
+       goto bail2;
+
+      /* Now read indeterminate tuples if required.  */
+      if (index & 0x4000)
+       {
+         tuple += fvar->axis_count * 4;
+         if (INT_ADD_WRAPV (size, fvar->axis_count * 4, &size))
+           goto bail2;
+       }
+
+      /* Add one point and one delta for each CVT element.  */
+      if (INT_ADD_WRAPV (size, cvt->num_elements * 4, &size))
+       goto bail2;
+
+      /* Now add the size of the tuple.  */
+      if (INT_ADD_WRAPV (size, sizeof *cvar->variation, &size))
+       goto bail2;
+    }
+
+  if (INT_ADD_WRAPV (sizeof *cvar, size, &size))
+    goto bail2;
+
+  /* Reallocate cvar.  */
+  cvar = xrealloc (cvar, size);
+  ntuples = cvar->tuple_count & 0x0fff;
+  cvar->variation = (struct sfnt_tuple_variation *) (cvar + 1);
+  coords = (sfnt_f2dot14 *) (cvar->variation + ntuples);
+  tuple = buffer;
+
+  data_offset = ((ptrdiff_t) cvar->data_offset
+                - (ptrdiff_t) min_size);
+
+  /* Start reading the tuples into cvar.  */
+  for (i = 0; i < ntuples; ++i)
+    {
+      data = buffer + data_offset;
+
+      /* Read the tuple.  */
+      if (tuple + 3 >= end)
+       goto bail2;
+
+      memcpy (&data_size, tuple, sizeof data_size);
+      tuple += sizeof data_size;
+      memcpy (&index, tuple, sizeof index);
+      tuple += sizeof index;
+      sfnt_swap16 (&data_size);
+      sfnt_swap16 (&index);
+
+      /* Increment the offset to the data by the data size specified
+        here.  */
+      data_offset += data_size;
+
+      cvar->variation[i].intermediate_start = NULL;
+      cvar->variation[i].intermediate_end = NULL;
+
+      if (index & 0x8000)
+       {
+         /* Embedded coordinates are present.  Read each
+            coordinate.  */
+         cvar->variation[i].coordinates = coords;
+
+         for (j = 0; j < fvar->axis_count; ++j)
+           {
+             if (tuple + 1 >= end)
+               goto bail2;
+
+             memcpy (coords++, tuple, sizeof *coords);
+             tuple += sizeof *coords;
+             sfnt_swap16 (coords);
+           }
+       }
+      else
+       goto bail2;
+
+      /* Now read indeterminate tuples if required.  */
+      if (index & 0x4000)
+       {
+         cvar->variation[i].intermediate_start = coords;
+
+         for (j = 0; j < fvar->axis_count; ++j)
+           {
+             if (tuple + 1 >= end)
+               goto bail2;
+
+             memcpy (coords++, tuple, sizeof *coords);
+             tuple += sizeof *coords;
+             sfnt_swap16 (coords);
+           }
+
+         cvar->variation[i].intermediate_end = coords;
+
+         for (j = 0; j < fvar->axis_count; ++j)
+           {
+             if (tuple + 1 >= end)
+               goto bail2;
+
+             memcpy (coords++, tuple, sizeof *coords);
+             tuple += sizeof *coords;
+             sfnt_swap16 (coords);
+           }
+       }
+
+      /* Finally, read private ``point'' numbers.  If this flag is not
+         set, use shared point numbers previously read.
+
+         Read at most CVT->num_elements points, as that is all the
+         storage allocated.  */
+
+      if (index & 0x2000)
+       {
+         local = sfnt_read_packed_points (data, &cvar->variation[i].num_points,
+                                          end, &data);
+         if (!local)
+           goto bail2;
+
+         /* If points apply to all CVT indices, skip this part.  */
+
+         if (cvar->variation[i].num_points != UINT16_MAX)
+           {
+             if (cvar->variation[i].num_points > cvt->num_elements)
+               cvar->variation[i].num_points = cvt->num_elements;
+
+             cvar->variation[i].points = (uint16_t *) coords;
+             for (j = 0; j < cvar->variation[i].num_points; ++j)
+               *coords++ = local[j];
+             xfree (local);
+           }
+         else
+           cvar->variation[i].points = NULL;
+       }
+      else
+       {
+         /* Copy in the shared point numbers instead.  */
+         cvar->variation[i].num_points = npoints;
+
+         if (npoints != UINT16_MAX)
+           {
+             if (cvar->variation[i].num_points > cvt->num_elements)
+               cvar->variation[i].num_points = cvt->num_elements;
+
+             cvar->variation[i].points = (uint16_t *) coords;
+             for (j = 0; j < cvar->variation[i].num_points; ++j)
+               *coords++ = points[j];
+           }
+         else
+           cvar->variation[i].points = NULL;
+       }
+
+      /* And read packed deltas.  If cvar->variation[i].num_points is
+        UINT16_MAX, then there is one delta for each CVT entry.
+        Otherwise, there are that many deltas.  */
+
+      if (cvar->variation[i].num_points == UINT16_MAX)
+       {
+         deltas = sfnt_read_packed_deltas (data, end, cvt->num_elements,
+                                           &data);
+
+         if (!deltas)
+           goto bail2;
+
+         cvar->variation[i].deltas = coords;
+
+         for (j = 0; j < cvt->num_elements; ++j)
+           *coords++ = deltas[j];
+         xfree (deltas);
+       }
+      else
+       {
+         deltas = sfnt_read_packed_deltas (data, end,
+                                           cvar->variation[i].num_points,
+                                           &data);
+         if (!deltas)
+           goto bail2;
+
+         cvar->variation[i].deltas = coords;
+
+         for (j = 0; j < cvar->variation[i].num_points; ++j)
+           *coords++ = deltas[j];
+         xfree (deltas);
+       }
+    }
+
+  /* Free data and return the read cvar table.  */
+  if (points != (void *) -1)
+    xfree (points);
+  xfree (buffer);
+  return cvar;
+
+ bail2:
+  if (points != (void *) -1)
+    xfree (points);
+ bail1:
+  xfree (buffer);
+ bail:
+  xfree (cvar);
+  return NULL;
+}
+
+
+
+/* Initialize the specified BLEND with the given FVAR and GVAR tables.
+   If non-NULL, adjust normalized coordinates using the axis variation
+   table AVAR; similarly, adjust interpreter CVT values using CVAR, if
+   specified.  */
+
+TEST_STATIC void
+sfnt_init_blend (struct sfnt_blend *blend, struct sfnt_fvar_table *fvar,
+                struct sfnt_gvar_table *gvar, struct sfnt_avar_table *avar,
+                struct sfnt_cvar_table *cvar)
+{
+  size_t size;
+
+  blend->fvar = fvar;
+  blend->gvar = gvar;
+  blend->avar = avar;
+  blend->cvar = cvar;
+
+  /* Allocate a single array to hold both coords and norm_coords.  */
+  size = (fvar->axis_count * sizeof *blend->coords * 2);
+  blend->coords = xmalloc (size);
+  blend->norm_coords = blend->coords + fvar->axis_count;
+}
+
+/* Free what was initialized in the specified BLEND.  */
+
+TEST_STATIC void
+sfnt_free_blend (struct sfnt_blend *blend)
+{
+  xfree (blend->coords);
+}
+
+/* Normalize BLEND->fvar->axis_count coordinates in BLEND->coords and
+   place the result in BLEND->norm_coords.  */
+
+TEST_STATIC void
+sfnt_normalize_blend (struct sfnt_blend *blend)
+{
+  struct sfnt_variation_axis *axis;
+  int i, j;
+  sfnt_fixed from, coord, j0, j1, j2;
+  sfnt_fixed from_last, coord_last;
+  struct sfnt_short_frac_segment *segment;
+
+  /* For each axis... */
+  for (i = 0; i < blend->fvar->axis_count; ++i)
+    {
+      /* Normalize based on [min, default, max], into [-1, 0, 1].  */
+      axis = &blend->fvar->axis[i];
+
+      /* Load the current design coordinate.  */
+      coord = blend->coords[i];
+
+      /* Keep it within bounds.  */
+
+      if (coord > axis->max_value)
+       coord = axis->max_value;
+      else if (coord < axis->min_value)
+       coord = axis->min_value;
+
+      if (coord > axis->default_value)
+       {
+         /* Avoid division by 0.  */
+         if (axis->max_value != axis->default_value)
+           blend->norm_coords[i]
+             = sfnt_div_fixed (sfnt_sub (coord, axis->default_value),
+                               sfnt_sub (axis->max_value,
+                                         axis->default_value));
+         else
+           blend->norm_coords[i] = 0;
+       }
+      else if (coord < axis->default_value)
+       {
+         if (axis->default_value != axis->min_value)
+           blend->norm_coords[i]
+             = sfnt_div_fixed (sfnt_sub (coord, axis->default_value),
+                               sfnt_sub (axis->default_value,
+                                         axis->min_value));
+         else
+           blend->norm_coords[i] = 0;
+       }
+      else
+       blend->norm_coords[i] = 0;
+    }
+
+  /* Now, apply axis variations, but only if the avar table has the
+     right number of axes.  */
+
+  if (blend->avar && (blend->fvar->axis_count
+                     == blend->avar->axis_count))
+    {
+      for (i = 0; i < blend->fvar->axis_count; ++i)
+       {
+         segment = &blend->avar->segments[i];
+
+         /* Search for a correspondence record above the normalized
+            coordinate of this axis.  */
+
+         for (j = 1; j < segment->pair_count; ++j)
+           {
+             from = segment->correspondence[j].from_coord * 4;
+             coord = segment->correspondence[j].to_coord * 4;
+
+             if (blend->norm_coords[i] < from)
+               {
+                 from_last
+                   = segment->correspondence[j - 1].from_coord * 4;
+                 coord_last
+                   = segment->correspondence[j - 1].to_coord * 4;
+
+                 j0 = blend->norm_coords[i] - from_last;
+                 j1 = coord - coord_last;
+                 j2 = from - from_last;
+
+                 blend->norm_coords[i]
+                   = (sfnt_multiply_divide_signed (j0, j1, j2) + coord_last);
+                 break;
+               }
+           }
+       }
+    }
+}
+
+
+
+struct sfnt_gvar_glyph_header
+{
+  /* A packed field. The high 4 bits are flags and the low 12 bits are
+     the number of tuples for this glyph.  The number of tuples can be
+     any number between 1 and 4095.  */
+  uint16_t tuple_count;
+
+  /* Offset from the start of the GlyphVariationData table to the
+     serialized data.  */
+  uint16_t data_offset;
+};
+
+/* Given a BLEND containing normalized coordinates, an array of
+   BLEND->gvar->axis_count tuple coordinates, and, if INTERMEDIATE_P,
+   a range of tuple coordinates from INTERMEDIATE_START to
+   INTERMEDIATE_END, return the scaling factor to apply to deltas for
+   each corresponding point.  */
+
+static sfnt_fixed
+sfnt_compute_tuple_scale (struct sfnt_blend *blend, bool intermediate_p,
+                         sfnt_f2dot14 *coords,
+                         sfnt_f2dot14 *intermediate_start,
+                         sfnt_f2dot14 *intermediate_end)
+{
+  int i;
+  sfnt_fixed coord, start UNINIT, end UNINIT;
+  sfnt_fixed scale;
+
+  /* scale is initially 1.0.  */
+  scale = 0200000;
+
+  for (i = 0; i < blend->gvar->axis_count; ++i)
+    {
+      /* Load values for this axis, scaled up to sfnt_fixed.  */
+      coord = coords[i] * 4;
+
+      /* GCC warns about start and end being used when uninitialized,
+        but they are used only if intermediate_p.  */
+
+      if (intermediate_p)
+       {
+         start = intermediate_start[i] * 4;
+         end = intermediate_start[i] * 4;
+       }
+
+      /* Ignore tuples that can be skipped.  */
+
+      if (!coord)
+       continue;
+
+      /* If the coordinate is set to 0, then deltas should not be
+        applied.  Return 0.  */
+
+      if (!blend->norm_coords[i])
+       return 0;
+
+      /* If no scaling need take place, continue.  */
+
+      if (blend->norm_coords[i] == coord)
+       continue;
+
+      if (!intermediate_p)
+       {
+         /* Not an intermediate tuple; if coord is less than 0 and
+            blend->norm_coords[i] < coord, or coord is more than 0
+            and blend->norm_coords[i] > coord, then it doesn't fit,
+            so return.  */
+
+         if (blend->norm_coords[i] < MIN (0, coord)
+             || blend->norm_coords[i] > MAX (0, coord))
+           return 0;
+
+         scale = sfnt_multiply_divide_signed (scale,
+                                              blend->norm_coords[i],
+                                              coord);
+       }
+      else
+       {
+         /* Otherwise, renormalize between start and end.  */
+
+         if (blend->norm_coords[i] < start
+             || blend->norm_coords[i] > end)
+           return 0;
+
+         if (blend->norm_coords[i] < coord)
+           scale = sfnt_multiply_divide (scale,
+                                         blend->norm_coords[i] - start,
+                                         coord - start);
+         else
+           scale = sfnt_multiply_divide (scale,
+                                         end - blend->norm_coords[i],
+                                         end - coord);
+       }
+    }
+
+  return scale;
+}
+
+/* Infer point positions for points that have been partially moved
+   within the contour in GLYPH denoted by START and END.  */
+
+static void
+sfnt_infer_deltas_1 (struct sfnt_glyph *glyph, size_t start,
+                    size_t end, bool *touched, sfnt_fword *x,
+                    sfnt_fword *y)
+{
+  size_t i, pair_start, pair_end, pair_first, j;
+  sfnt_fword min_pos, max_pos, position;
+  sfnt_fixed ratio, delta;
+
+  pair_start = pair_first = -1;
+
+  /* Look for pairs of touched points.  */
+
+  for (i = start; i <= end; ++i)
+    {
+      if (!touched[i])
+       continue;
+
+      if (pair_start == -1)
+       {
+         pair_first = i;
+         goto next;
+       }
+
+      pair_end = i;
+
+      /* pair_start to pair_end are now a pair of points, where points
+        in between should be interpolated.  */
+
+      for (j = pair_start + 1; j < pair_end; ++j)
+       {
+         /* Consider the X axis.  Set min_pos and max_pos to the
+            smallest and greatest values along that axis.  */
+         min_pos = MIN (x[pair_start], x[pair_end]);
+         max_pos = MAX (x[pair_start], x[pair_end]);
+
+         /* Now see if the current point lies between min and
+            max... */
+         if (x[j] >= min_pos && x[j] <= max_pos)
+           {
+             /* If min_pos and max_pos are the same, apply
+                pair_start's delta if it is identical to that of
+                pair_end, or apply nothing at all otherwise.  */
+
+             if (min_pos == max_pos)
+               {
+                 if ((glyph->simple->x_coordinates[pair_start]
+                      - x[pair_start])
+                     == (glyph->simple->x_coordinates[pair_end]
+                         - x[pair_end]))
+                   glyph->simple->x_coordinates[j]
+                     += (glyph->simple->x_coordinates[pair_start]
+                         - x[pair_start]);
+
+                 continue;
+               }
+
+             /* Interpolate between min_pos and max_pos.  */
+             ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos)
+                                      * 65536),
+                                     (sfnt_sub (max_pos, min_pos)
+                                      * 65536));
+
+             /* Load the current positions of pair_start and pair_end
+                along this axis.  */
+             min_pos = MIN (glyph->simple->x_coordinates[pair_start],
+                            glyph->simple->x_coordinates[pair_end]);
+             max_pos = MAX (glyph->simple->x_coordinates[pair_start],
+                            glyph->simple->x_coordinates[pair_end]);
+
+             /* Lerp in between.  */
+             delta = sfnt_sub (max_pos, min_pos);
+             delta = sfnt_mul_fixed (ratio, delta);
+             glyph->simple->x_coordinates[j] = min_pos + delta;
+           }
+         else
+           {
+             /* ... otheriwse, move point j by the delta of the
+                nearest touched point.  */
+
+             if (x[j] >= max_pos)
+               {
+                 position = MAX (glyph->simple->x_coordinates[pair_start],
+                                 glyph->simple->x_coordinates[pair_end]);
+                 delta = position - max_pos;
+               }
+             else
+               {
+                 position = MIN (glyph->simple->x_coordinates[pair_start],
+                                 glyph->simple->x_coordinates[pair_end]);
+                 delta = position - min_pos;
+               }
+
+             glyph->simple->x_coordinates[j] = x[j] + delta;
+           }
+
+         /* Now, consider the Y axis.  */
+         min_pos = MIN (y[pair_start], y[pair_end]);
+         max_pos = MAX (y[pair_start], y[pair_end]);
+
+         /* Now see if the current point lies between min and
+            max... */
+         if (y[j] >= min_pos && y[j] <= max_pos)
+           {
+             /* If min_pos and max_pos are the same, apply
+                pair_start's delta if it is identical to that of
+                pair_end, or apply nothing at all otherwise.  */
+
+             if (min_pos == max_pos)
+               {
+                 if ((glyph->simple->y_coordinates[pair_start]
+                      - y[pair_start])
+                     == (glyph->simple->y_coordinates[pair_end]
+                         - y[pair_end]))
+                   glyph->simple->y_coordinates[j]
+                     += (glyph->simple->y_coordinates[pair_start]
+                         - y[pair_start]);
+
+                 continue;
+               }
+
+             /* Interpolate between min_pos and max_pos.  */
+             ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos)
+                                      * 65536),
+                                     (sfnt_sub (max_pos, min_pos)
+                                      * 65536));
+
+             /* Load the current positions of pair_start and pair_end
+                along this axis.  */
+             min_pos = MIN (glyph->simple->y_coordinates[pair_start],
+                            glyph->simple->y_coordinates[pair_end]);
+             max_pos = MAX (glyph->simple->y_coordinates[pair_start],
+                            glyph->simple->y_coordinates[pair_end]);
+
+             /* Lerp in between.  */
+             delta = sfnt_sub (max_pos, min_pos);
+             delta = sfnt_mul_fixed (ratio, delta);
+             glyph->simple->y_coordinates[j] = min_pos + delta;
+           }
+         else
+           {
+             /* ... otheriwse, move point j by the delta of the
+                nearest touched point.  */
+
+             if (y[j] >= max_pos)
+               {
+                 position = MAX (glyph->simple->y_coordinates[pair_start],
+                                 glyph->simple->y_coordinates[pair_end]);
+                 delta = position - max_pos;
+               }
+             else
+               {
+                 position = MIN (glyph->simple->y_coordinates[pair_start],
+                                 glyph->simple->y_coordinates[pair_end]);
+                 delta = position - min_pos;
+               }
+
+             glyph->simple->y_coordinates[j] = y[j] + delta;
+           }
+       }
+
+    next:
+      pair_start = i;
+    }
+
+  /* If pair_start is set, then lerp points between it and
+     pair_first.  */
+
+  if (pair_start != (size_t) -1)
+    {
+      j = pair_start + 1;
+
+      if (j > end)
+       j = start;
+
+      pair_end = pair_first;
+
+      while (j != pair_first)
+       {
+         /* Consider the X axis.  Set min_pos and max_pos to the
+            smallest and greatest values along that axis.  */
+         min_pos = MIN (x[pair_start], x[pair_end]);
+         max_pos = MAX (x[pair_start], x[pair_end]);
+
+         /* Now see if the current point lies between min and
+            max... */
+         if (x[j] >= min_pos && x[j] <= max_pos)
+           {
+             /* If min_pos and max_pos are the same, apply
+                pair_start's delta if it is identical to that of
+                pair_end, or apply nothing at all otherwise.  */
+
+             if (min_pos == max_pos)
+               {
+                 if ((glyph->simple->x_coordinates[pair_start]
+                      - x[pair_start])
+                     == (glyph->simple->x_coordinates[pair_end]
+                         - x[pair_end]))
+                   glyph->simple->x_coordinates[j]
+                     += (glyph->simple->x_coordinates[pair_start]
+                         - x[pair_start]);
+
+                 goto next_1;
+               }
+
+             /* Interpolate between min_pos and max_pos.  */
+             ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos)
+                                      * 65536),
+                                     (sfnt_sub (max_pos, min_pos)
+                                      * 65536));
+
+             /* Load the current positions of pair_start and pair_end
+                along this axis.  */
+             min_pos = MIN (glyph->simple->x_coordinates[pair_start],
+                            glyph->simple->x_coordinates[pair_end]);
+             max_pos = MAX (glyph->simple->x_coordinates[pair_start],
+                            glyph->simple->x_coordinates[pair_end]);
+
+             /* Lerp in between.  */
+             delta = sfnt_sub (max_pos, min_pos);
+             delta = sfnt_mul_fixed (ratio, delta);
+             glyph->simple->x_coordinates[j] = min_pos + delta;
+           }
+         else
+           {
+             /* ... otheriwse, move point j by the delta of the
+                nearest touched point.  */
+
+             if (x[j] >= max_pos)
+               {
+                 position = MAX (glyph->simple->x_coordinates[pair_start],
+                                 glyph->simple->x_coordinates[pair_end]);
+                 delta = position - max_pos;
+               }
+             else
+               {
+                 position = MIN (glyph->simple->x_coordinates[pair_start],
+                                 glyph->simple->x_coordinates[pair_end]);
+                 delta = position - min_pos;
+               }
+
+             glyph->simple->x_coordinates[j] = x[j] + delta;
+           }
+
+         /* Now, consider the Y axis.  */
+         min_pos = MIN (y[pair_start], y[pair_end]);
+         max_pos = MAX (y[pair_start], y[pair_end]);
+
+         /* Now see if the current point lies between min and
+            max... */
+         if (y[j] >= min_pos && y[j] <= max_pos)
+           {
+             /* If min_pos and max_pos are the same, apply
+                pair_start's delta if it is identical to that of
+                pair_end, or apply nothing at all otherwise.  */
+
+             if (min_pos == max_pos)
+               {
+                 if ((glyph->simple->y_coordinates[pair_start]
+                      - y[pair_start])
+                     == (glyph->simple->y_coordinates[pair_end]
+                         - y[pair_end]))
+                   glyph->simple->y_coordinates[j]
+                     += (glyph->simple->y_coordinates[pair_start]
+                         - y[pair_start]);
+
+                 goto next_1;
+               }
+
+             /* Interpolate between min_pos and max_pos.  */
+             ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos)
+                                      * 65536),
+                                     (sfnt_sub (max_pos, min_pos)
+                                      * 65536));
+
+             /* Load the current positions of pair_start and pair_end
+                along this axis.  */
+             min_pos = MIN (glyph->simple->y_coordinates[pair_start],
+                            glyph->simple->y_coordinates[pair_end]);
+             max_pos = MAX (glyph->simple->y_coordinates[pair_start],
+                            glyph->simple->y_coordinates[pair_end]);
+
+             /* Lerp in between.  */
+             delta = sfnt_sub (max_pos, min_pos);
+             delta = sfnt_mul_fixed (ratio, delta);
+             glyph->simple->y_coordinates[j] = min_pos + delta;
+           }
+         else
+           {
+             /* ... otheriwse, move point j by the delta of the
+                nearest touched point.  */
+
+             if (y[j] >= max_pos)
+               {
+                 position = MAX (glyph->simple->y_coordinates[pair_start],
+                                 glyph->simple->y_coordinates[pair_end]);
+                 delta = position - max_pos;
+               }
+             else
+               {
+                 position = MIN (glyph->simple->y_coordinates[pair_start],
+                                 glyph->simple->y_coordinates[pair_end]);
+                 delta = position - min_pos;
+               }
+
+             glyph->simple->y_coordinates[j] = y[j] + delta;
+           }
+
+       next_1:
+         j++;
+         if (j > end)
+           j = start;
+       }
+    }
+}
+
+/* Infer point positions for contours that have been partially moved
+   by variation.  For each contour in GLYPH, find pairs of points
+   which have had deltas applied.  For each such pair, interpolate
+   points between the first point in the pair and the second by
+   considering each point along every one of the two axes (X and Y)
+   like so:
+
+     - For each point that lies between the first point and the last
+       on the axis currently being considered, interpolate its
+       position in that axis so that the ratio formed by its position
+       relative to the first and last points of the pair in the
+       original outline still holds.
+
+     - For each point that lies to the left or top of the first point
+       on the axis being considered, use the delta of the first point.
+
+     - And finally, for each point that lies to the right or bottom of
+       the last point on that axis, use the delta of the last
+       point.
+
+   X and Y contain the original positions of each point.
+   TOUCHED contains whether or not each point within GLYPH has been
+   changed through variation.
+
+   Apply the inferred deltas back to GLYPH.  */
+
+static void
+sfnt_infer_deltas (struct sfnt_glyph *glyph, bool *touched,
+                  sfnt_fword *x, sfnt_fword *y)
+{
+  size_t i;
+  int point, first, end;
+
+  point = 0;
+  for (i = 0; i < glyph->number_of_contours; ++i)
+    {
+      first = point;
+      end = glyph->simple->end_pts_of_contours[i];
+
+      /* Return if the glyph is invalid.  */
+
+      if (first >= glyph->simple->number_of_points
+         || end >= glyph->simple->number_of_points
+         || first > end)
+       return;
+
+      sfnt_infer_deltas_1 (glyph, first, end, touched, x, y);
+      point = end + 1;
+    }
+}
+
+/* Read the glyph variation data for the specified glyph ID from
+   BLEND's gvar table.  Apply the offsets to each point in the
+   specified simple GLYPH, based on the specified BLEND.
+
+   Value is 0 upon success, else 1.
+
+   The glyph variation data consists of a number of elements, each of
+   which has its own associated point numbers and deltas, and a list
+   of one or two coordinates for each axis.  Each such list is
+   referred to as a ``tuple''.
+
+   The deltas, one for each point, are multipled by the normalized
+   value of each axis and applied to those points for each tuple that
+   is found to be applicable.
+
+   Each element of the glyph variation data is applicable to an axis
+   if its list of coordinates:
+
+     - contains one element for each axis, and its axis has a value
+       between 0 and that element.
+
+     - contains two elements for each axis, and its axis has a value
+       between the first element and the second.
+
+   Return the deltas that would normally be applied to the two phantom
+   points describing horizontal bounds in *DISTORTION.  Do not
+   transform the outline to reflect adjustments to the origin
+   point.  */
+
+TEST_STATIC int
+sfnt_vary_simple_glyph (struct sfnt_blend *blend, sfnt_glyph id,
+                       struct sfnt_glyph *glyph,
+                       struct sfnt_metrics_distortion *distortion)
+{
+  uint32_t offset;
+  struct sfnt_gvar_glyph_header header;
+  uint16_t *points, npoints;
+  int i, ntuples, j, point_count;
+  unsigned char *tuple, *end, *data;
+  uint16_t data_size, index, *glyph_points;
+  sfnt_f2dot14 *restrict coords;
+  sfnt_f2dot14 *restrict intermediate_start;
+  sfnt_f2dot14 *restrict intermediate_end;
+  sfnt_fword *restrict dx, *restrict dy, fword;
+  struct sfnt_gvar_table *gvar;
+  uint16_t *local_points, n_local_points;
+  sfnt_fixed scale;
+  ptrdiff_t data_offset;
+  bool *touched;
+  sfnt_fword *restrict original_x, *restrict original_y;
+
+  gvar = blend->gvar;
+
+  if (gvar->axis_count != blend->fvar->axis_count)
+    return 1;
+
+  if (gvar->glyph_count <= id)
+    return 1;
+
+  if (gvar->flags & 1)
+    offset = gvar->u.offset_long[id];
+  else
+    offset = gvar->u.offset_word[id] * 2u;
+
+  if (offset >= gvar->data_size)
+    return 1;
+
+  end = gvar->glyph_variation_data + gvar->data_size;
+
+  /* Start reading the header.  */
+
+  if (offset + sizeof header > gvar->data_size)
+    return 1;
+
+  /* Clear the distortion.  */
+  distortion->origin = 0;
+  distortion->advance = 0;
+
+  memcpy (&header, gvar->glyph_variation_data + offset,
+         sizeof header);
+
+  /* Swap the header.  */
+  sfnt_swap16 (&header.tuple_count);
+  sfnt_swap16 (&header.data_offset);
+
+  /* Prepare to read each tuple.  */
+  ntuples = header.tuple_count & 0x0fff;
+
+  /* Initialize the data offset.  This is incremented with each tuple
+     read.  */
+  data_offset = header.data_offset;
+
+  /* If gvar->flags & tuples_share_point_numbers, read the shared
+     point numbers.  Initialize `npoints' to zero.  The specification
+     doesn't say what should happen with tuples using shared point
+     numbers if it is not set later on; simply assume no points at all
+     apply to such a tuple.  */
+
+  npoints = 0;
+
+  if (header.tuple_count & 0x8000)
+    {
+      data = gvar->glyph_variation_data + offset + data_offset;
+      points = sfnt_read_packed_points (data, &npoints, end,
+                                       &tuple);
+
+      if (!points)
+       return 1;
+
+      /* Shared point numbers are part of the data after the tuple
+         array.  Thus, increment data_offset by tuple - data.  `tuple'
+         here holds no relation to a pointer to the current part of
+         the tuple array that is being read later on.  */
+      data_offset += tuple - data;
+    }
+  else
+    points = NULL;
+
+  /* Start reading each tuple.  */
+  tuple = gvar->glyph_variation_data + offset + sizeof header;
+
+  if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+    coords = xmalloc (gvar->axis_count * sizeof *coords * 3);
+  else
+    coords = alloca (gvar->axis_count * sizeof *coords * 3);
+
+  intermediate_start = coords + gvar->axis_count;
+  intermediate_end = coords + gvar->axis_count;
+
+  /* Allocate arrays of booleans and fwords to keep track of which
+     points have been touched.  */
+  touched = NULL;
+  original_x = NULL;
+  original_y = NULL;
+
+  while (ntuples--)
+    {
+      data = gvar->glyph_variation_data + offset + data_offset;
+
+      if (tuple + 3 >= end)
+       goto fail1;
+
+      memcpy (&data_size, tuple, sizeof data_size);
+      tuple += sizeof data_size;
+      memcpy (&index, tuple, sizeof index);
+      tuple += sizeof index;
+      sfnt_swap16 (&data_size);
+      sfnt_swap16 (&index);
+
+      /* Increment the offset to the data by the data size specified
+        here.  */
+      data_offset += data_size;
+
+      if (index & 0x8000)
+       {
+         /* Embedded coordinates are present.  Read each
+            coordinate and add it to the tuple.  */
+         for (j = 0; j < gvar->axis_count; ++j)
+           {
+             if (tuple + 1 >= end)
+               goto fail1;
+
+             memcpy (&coords[j], tuple, sizeof *coords);
+             tuple += sizeof *coords;
+             sfnt_swap16 (&coords[j]);
+           }
+       }
+      else if ((index & 0xfff) > gvar->shared_coord_count)
+       /* index exceeds the number of shared tuples present.  */
+       goto fail1;
+      else
+       /* index points into gvar->axis_count coordinates making up
+          the tuple.  */
+       memcpy (coords, (gvar->global_coords
+                        + ((index & 0xfff) * gvar->axis_count)),
+               gvar->axis_count * sizeof *coords);
+
+      /* Now read indeterminate tuples if required.  */
+      if (index & 0x4000)
+       {
+         for (j = 0; j < gvar->axis_count; ++j)
+           {
+             if (tuple + 1 >= end)
+               goto fail1;
+
+             memcpy (&intermediate_start[j], tuple,
+                     sizeof *intermediate_start);
+             tuple += sizeof *intermediate_start;
+             sfnt_swap16 (&intermediate_start[j]);
+           }
+
+         for (j = 0; j < gvar->axis_count; ++j)
+           {
+             if (tuple + 1 >= end)
+               goto fail1;
+
+             memcpy (&intermediate_end[j], tuple,
+                     sizeof *intermediate_end);
+             tuple += sizeof *intermediate_end;
+             sfnt_swap16 (&intermediate_end[j]);
+           }
+       }
+
+      /* See whether or not the tuple applies to the current variation
+        configuration, and how much to scale them by.  */
+
+      scale = sfnt_compute_tuple_scale (blend, index & 0x4000,
+                                       coords, intermediate_start,
+                                       intermediate_end);
+
+      if (!scale)
+       continue;
+
+      local_points = NULL;
+
+      /* Finally, read private point numbers.
+        Set local_points to those numbers; it will be freed
+        once the loop ends.  */
+
+      if (index & 0x2000)
+       {
+         local_points = sfnt_read_packed_points (data, &n_local_points,
+                                                 end, &data);
+         if (!local_points)
+           goto fail1;
+
+         point_count = n_local_points;
+         glyph_points = local_points;
+       }
+      else
+       {
+         /* If there are no private point numbers, use global
+            points.  */
+         point_count = npoints;
+         glyph_points = points;
+       }
+
+      /* Now, read packed deltas.  */
+
+      dx = NULL;
+      dy = NULL;
+
+      switch (point_count)
+       {
+       case UINT16_MAX:
+         /* Deltas are provided for all points in the glyph.
+            No glyph should have more than 65535 points.  */
+
+         /* Add 4 phantom points to each end.  */
+         dx = sfnt_read_packed_deltas (data, end,
+                                       glyph->simple->number_of_points + 4,
+                                       &data);
+         dy = sfnt_read_packed_deltas (data, end,
+                                       glyph->simple->number_of_points + 4,
+                                       &data);
+
+         if (!dx || !dy)
+           goto fail3;
+
+         /* Apply each delta to the simple glyph.  */
+
+         for (i = 0; i < glyph->simple->number_of_points; ++i)
+           {
+             fword = sfnt_mul_fixed_round (dx[i], scale);
+             glyph->simple->x_coordinates[i] += fword;
+             fword = sfnt_mul_fixed_round (dy[i], scale);
+             glyph->simple->y_coordinates[i] += fword;
+           }
+
+         /* Apply the deltas for the two phantom points.  */
+         distortion->origin += sfnt_mul_fixed_round (dx[i++], scale);
+         distortion->advance += sfnt_mul_fixed_round (dx[i], scale);
+         break;
+
+       default:
+         dx = sfnt_read_packed_deltas (data, end, point_count, &data);
+         dy = sfnt_read_packed_deltas (data, end, point_count, &data);
+
+         if (!dx || !dy)
+           goto fail3;
+
+         /* Deltas are only applied for each point number read.  */
+
+         if (!original_x)
+           {
+             if ((glyph->simple->number_of_points
+                  * sizeof *touched) >= 1024 * 16)
+               touched = xmalloc (sizeof *touched
+                                  * glyph->simple->number_of_points);
+             else
+               touched = alloca (sizeof *touched
+                                 * glyph->simple->number_of_points);
+
+             if ((sizeof *original_x * 2
+                  * glyph->simple->number_of_points) >= 1024 * 16)
+               original_x = xmalloc (sizeof *original_x * 2
+                                     * glyph->simple->number_of_points);
+             else
+               original_x = alloca (sizeof *original_x * 2
+                                    * glyph->simple->number_of_points);
+
+             original_y = original_x + glyph->simple->number_of_points;
+             memcpy (original_x, glyph->simple->x_coordinates,
+                     (sizeof *original_x
+                      * glyph->simple->number_of_points));
+             memcpy (original_y, glyph->simple->y_coordinates,
+                     (sizeof *original_y
+                      * glyph->simple->number_of_points));
+           }
+
+         memset (touched, 0, (sizeof *touched
+                              * glyph->simple->number_of_points));
+
+         for (i = 0; i < point_count; ++i)
+           {
+             /* Apply deltas to phantom points.  */
+
+             if (glyph_points[i] == glyph->simple->number_of_points)
+               {
+                 distortion->origin += sfnt_mul_fixed_round (dx[i], scale);
+                 continue;
+               }
+
+             if (glyph_points[i] == glyph->simple->number_of_points + 1)
+               {
+                 distortion->advance += sfnt_mul_fixed_round (dx[i], scale);
+                 continue;
+               }
+
+             /* Make sure the point doesn't end up out of bounds.  */
+             if (glyph_points[i] >= glyph->simple->number_of_points)
+               continue;
+
+             fword = sfnt_mul_fixed_round (dx[i], scale);
+             glyph->simple->x_coordinates[glyph_points[i]] += fword;
+             fword = sfnt_mul_fixed_round (dy[i], scale);
+             glyph->simple->y_coordinates[glyph_points[i]] += fword;
+             touched[glyph_points[i]] = true;
+           }
+
+         sfnt_infer_deltas (glyph, touched, original_x,
+                            original_y);
+         break;
+       }
+
+      xfree (dx);
+      xfree (dy);
+
+      if (local_points != (uint16_t *) -1)
+       xfree (local_points);
+    }
+
+  /* Return success.  */
+
+  if ((glyph->simple->number_of_points
+       * sizeof *touched) >= 1024 * 16)
+    xfree (touched);
+
+  if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+    xfree (coords);
+
+  if ((sizeof *original_x * 2
+       * glyph->simple->number_of_points) >= 1024 * 16)
+    xfree (original_x);
+
+  if (points != (uint16_t *) -1)
+    xfree (points);
+
+  /* Set the glyph metrics distortion as well.  */
+  glyph->advance_distortion = distortion->advance;
+  glyph->origin_distortion = distortion->origin;
+
+  return 0;
+
+ fail3:
+  xfree (dx);
+  xfree (dy);
+  xfree (local_points);
+ fail1:
+
+  if ((glyph->simple->number_of_points
+       * sizeof *touched) >= 1024 * 16)
+    xfree (touched);
+
+  if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+    xfree (coords);
+
+  if ((sizeof *original_x * 2
+       * glyph->simple->number_of_points) >= 1024 * 16)
+    xfree (original_x);
+
+  if (points != (uint16_t *) -1)
+    xfree (points);
+
+  return 1;
+}
+
+/* Read the glyph variation data for the specified glyph ID from
+   BLEND's gvar table.  Apply the deltas specified within to each
+   component with offsets in the specified compound GLYPH, based on
+   the specified BLEND.  Return distortions to phantom points in
+   *DISTORTION.
+
+   Value is 0 upon success, 1 otherwise.  */
+
+TEST_STATIC int
+sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id,
+                         struct sfnt_glyph *glyph,
+                         struct sfnt_metrics_distortion *distortion)
+{
+  uint32_t offset;
+  struct sfnt_gvar_glyph_header header;
+  uint16_t *points, npoints;
+  int i, ntuples, j, point_count;
+  unsigned char *tuple, *end, *data;
+  uint16_t data_size, index, *glyph_points;
+  sfnt_f2dot14 *restrict coords;
+  sfnt_f2dot14 *restrict intermediate_start;
+  sfnt_f2dot14 *restrict intermediate_end;
+  sfnt_fword *restrict dx, *restrict dy, fword, word;
+  struct sfnt_gvar_table *gvar;
+  uint16_t *local_points, n_local_points;
+  sfnt_fixed scale;
+  ptrdiff_t data_offset;
+  struct sfnt_compound_glyph_component *component;
+
+  gvar = blend->gvar;
+
+  if (gvar->axis_count != blend->fvar->axis_count)
+    return 1;
+
+  if (gvar->glyph_count <= id)
+    return 1;
+
+  if (gvar->flags & 1)
+    offset = gvar->u.offset_long[id];
+  else
+    offset = gvar->u.offset_word[id] * 2u;
+
+  if (offset >= gvar->data_size)
+    return 1;
+
+  end = gvar->glyph_variation_data + gvar->data_size;
+
+  /* Start reading the header.  */
+
+  if (offset + sizeof header > gvar->data_size)
+    return 1;
+
+  /* Clear the distortion.  */
+  distortion->origin = 0;
+  distortion->advance = 0;
+
+  memcpy (&header, gvar->glyph_variation_data + offset,
+         sizeof header);
+
+  /* Swap the header.  */
+  sfnt_swap16 (&header.tuple_count);
+  sfnt_swap16 (&header.data_offset);
+
+  /* Prepare to read each tuple.  */
+  ntuples = header.tuple_count & 0x0fff;
+
+  /* Initialize the data offset.  This is incremented with each tuple
+     read.  */
+  data_offset = header.data_offset;
+
+  /* If gvar->flags & tuples_share_point_numbers, read the shared
+     point numbers.  */
+
+  npoints = 0;
+
+  if (header.tuple_count & 0x8000)
+    {
+      data = gvar->glyph_variation_data + offset + data_offset;
+      points = sfnt_read_packed_points (data, &npoints, end,
+                                       &tuple);
+
+      if (!points)
+       return 1;
+
+      /* Shared point numbers are part of the data after the tuple
+         array.  Thus, increment data_offset by tuple - data.  `tuple'
+         here holds no relation to a pointer to the current part of
+         the tuple array that is being read later on.  */
+      data_offset += tuple - data;
+    }
+  else
+    points = NULL;
+
+  /* Start reading each tuple.  */
+  tuple = gvar->glyph_variation_data + offset + sizeof header;
+
+  if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+    coords = xmalloc (gvar->axis_count * sizeof *coords * 3);
+  else
+    coords = alloca (gvar->axis_count * sizeof *coords * 3);
+
+  intermediate_start = coords + gvar->axis_count;
+  intermediate_end = coords + gvar->axis_count;
+
+  while (ntuples--)
+    {
+      data = gvar->glyph_variation_data + offset + data_offset;
+
+      if (tuple + 3 >= end)
+       goto fail1;
+
+      memcpy (&data_size, tuple, sizeof data_size);
+      tuple += sizeof data_size;
+      memcpy (&index, tuple, sizeof index);
+      tuple += sizeof index;
+      sfnt_swap16 (&data_size);
+      sfnt_swap16 (&index);
+
+      /* Increment the offset to the data by the data size specified
+        here.  */
+      data_offset += data_size;
+
+      if (index & 0x8000)
+       {
+         /* Embedded coordinates are present.  Read each
+            coordinate and add it to the tuple.  */
+         for (j = 0; j < gvar->axis_count; ++j)
+           {
+             if (tuple + 1 >= end)
+               goto fail1;
+
+             memcpy (&coords[j], tuple, sizeof *coords);
+             tuple += sizeof *coords;
+             sfnt_swap16 (&coords[j]);
+           }
+       }
+      else if ((index & 0xfff) > gvar->shared_coord_count)
+       /* index exceeds the number of shared tuples present.  */
+       goto fail1;
+      else
+       /* index points into gvar->axis_count coordinates making up
+          the tuple.  */
+       memcpy (coords, (gvar->global_coords
+                        + ((index & 0xfff) * gvar->axis_count)),
+               gvar->axis_count * sizeof *coords);
+
+      /* Now read indeterminate tuples if required.  */
+      if (index & 0x4000)
+       {
+         for (j = 0; j < gvar->axis_count; ++j)
+           {
+             if (tuple + 1 >= end)
+               goto fail1;
+
+             memcpy (&intermediate_start[j], tuple,
+                     sizeof *intermediate_start);
+             tuple += sizeof *intermediate_start;
+             sfnt_swap16 (&intermediate_start[j]);
+           }
+
+         for (j = 0; j < gvar->axis_count; ++j)
+           {
+             if (tuple + 1 >= end)
+               goto fail1;
+
+             memcpy (&intermediate_end[j], tuple,
+                     sizeof *intermediate_end);
+             tuple += sizeof *intermediate_end;
+             sfnt_swap16 (&intermediate_end[j]);
+           }
+       }
+
+      /* See whether or not the tuple applies to the current variation
+        configuration, and how much to scale them by.  */
+
+      scale = sfnt_compute_tuple_scale (blend, index & 0x4000,
+                                       coords, intermediate_start,
+                                       intermediate_end);
+
+      if (!scale)
+       continue;
+
+      local_points = NULL;
+
+      /* Finally, read private point numbers.
+        Set local_points to those numbers; it will be freed
+        once the loop ends.  */
+
+      if (index & 0x2000)
+       {
+         local_points = sfnt_read_packed_points (data, &n_local_points,
+                                                 end, &data);
+         if (!local_points)
+           goto fail1;
+
+         point_count = n_local_points;
+         glyph_points = local_points;
+       }
+      else
+       {
+         /* If there are no private point numbers, use global
+            points.  */
+         point_count = npoints;
+         glyph_points = points;
+       }
+
+      /* Now, read packed deltas.  */
+
+      dx = NULL;
+      dy = NULL;
+
+      switch (point_count)
+       {
+       case UINT16_MAX:
+         /* Deltas are provided for all components in the glyph.  */
+
+         /* Add 4 phantom points to each end.  */
+         dx = sfnt_read_packed_deltas (data, end,
+                                       glyph->compound->num_components + 4,
+                                       &data);
+         dy = sfnt_read_packed_deltas (data, end,
+                                       glyph->compound->num_components + 4,
+                                       &data);
+
+         if (!dx || !dy)
+           goto fail3;
+
+         /* Apply each delta to the compound glyph.  */
+
+         for (i = 0; i < glyph->compound->num_components; ++i)
+           {
+             component = &glyph->compound->components[i];
+
+             /* Check if the component uses deltas at all.  */
+             if (!(component->flags & 02))
+               continue;
+
+             /* Vary the X offset.  */
+
+             if (!(component->flags & 01))
+               word = component->argument1.b;
+             else
+               word = component->argument1.d;
+
+             fword = sfnt_mul_fixed_round (dx[i], scale);
+             component->argument1.d = word + fword;
+
+             /* Vary the Y offset.  */
+
+             if (!(component->flags & 01))
+               word = component->argument2.b;
+             else
+               word = component->argument2.d;
+
+             fword = sfnt_mul_fixed_round (dy[i], scale);
+
+             /* Set the flag that says offsets are words.  */
+             component->flags |= 01;
+             component->argument2.d = word + fword;
+           }
+
+         /* Apply the deltas for the two phantom points.  */
+         distortion->origin += sfnt_mul_fixed_round (dx[i++], scale);
+         distortion->advance += sfnt_mul_fixed_round (dx[i], scale);
+         break;
+
+       default:
+         dx = sfnt_read_packed_deltas (data, end, point_count, &data);
+         dy = sfnt_read_packed_deltas (data, end, point_count, &data);
+
+         if (!dx || !dy)
+           goto fail3;
+
+         /* Deltas are only applied for each point number read.  */
+
+         for (i = 0; i < point_count; ++i)
+           {
+             /* Apply deltas to phantom points.  */
+
+             if (glyph_points[i] == glyph->compound->num_components)
+               {
+                 distortion->origin += sfnt_mul_fixed_round (dx[i], scale);
+                 continue;
+               }
+
+             if (glyph_points[i] == glyph->compound->num_components + 1)
+               {
+                 distortion->advance += sfnt_mul_fixed_round (dx[i], scale);
+                 continue;
+               }
+
+             /* Make sure the point doesn't end up out of bounds.  */
+             if (glyph_points[i] >= glyph->compound->num_components)
+               continue;
+
+             component = &glyph->compound->components[glyph_points[i]];
+
+             /* Check if the component uses deltas at all.  */
+             if (!(component->flags & 02))
+               continue;
+
+             /* Vary the X offset.  */
+
+             if (!(component->flags & 01))
+               word = component->argument1.b;
+             else
+               word = component->argument1.d;
+
+             fword = sfnt_mul_fixed_round (dx[i], scale);
+             component->argument1.d = word + fword;
+
+             /* Vary the Y offset.  */
+
+             if (!(component->flags & 01))
+               word = component->argument2.b;
+             else
+               word = component->argument2.d;
+
+             fword = sfnt_mul_fixed_round (dy[i], scale);
+
+             /* Set the flag that says offsets are words.  */
+             component->flags |= 01;
+             component->argument2.d = word + fword;
+           }
+
+         break;
+       }
+
+      xfree (dx);
+      xfree (dy);
+
+      if (local_points != (uint16_t *) -1)
+       xfree (local_points);
+    }
+
+  /* Return success.  */
+
+  if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+    xfree (coords);
+
+  if (points != (uint16_t *) -1)
+    xfree (points);
+
+  /* Set the glyph metrics distortion as well.  */
+  glyph->advance_distortion = distortion->advance;
+  glyph->origin_distortion = distortion->origin;
+
+  return 0;
+
+ fail3:
+  xfree (dx);
+  xfree (dy);
+  xfree (local_points);
+ fail1:
+
+  if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+    xfree (coords);
+
+  if (points != (uint16_t *) -1)
+    xfree (points);
+
+  return 1;
+}
+
+/* Vary the specified INTERPRETER's control value table using the
+   variations in BLEND's CVT variations table, then record the blend's
+   normalized coordinates and axis count in the interpreter.
+
+   The CVT table used to create INTERPRETER must be the same used
+   to read BLEND->cvar.  If not, behavior is undefined.  */
+
+TEST_STATIC void
+sfnt_vary_interpreter (struct sfnt_interpreter *interpreter,
+                      struct sfnt_blend *blend)
+{
+  sfnt_fixed scale;
+  int i;
+  struct sfnt_tuple_variation *variation;
+  size_t ndeltas, j, index;
+  sfnt_f26dot6 delta;
+
+  /* Return if there's no cvar table.  */
+  if (!blend->cvar)
+    return;
+
+  /* For each tuple in the cvar table... */
+  for (i = 0; i < (blend->cvar->tuple_count & 0x0fff); ++i)
+    {
+      /* See if the tuple applies.  */
+      variation = &blend->cvar->variation[i];
+      scale = sfnt_compute_tuple_scale (blend,
+                                       variation->intermediate_start != NULL,
+                                       variation->coordinates,
+                                       variation->intermediate_start,
+                                       variation->intermediate_end);
+      if (!scale)
+       continue;
+
+      /* Figure out how many deltas there are.  If variation->points,
+         there are num_points deltas.  Otherwise, there are
+         interpreter->cvt->num_elements deltas.  */
+
+      ndeltas = (variation->points
+                ? variation->num_points
+                : interpreter->cvt_size);
+
+      for (j = 0; j < ndeltas; ++j)
+       {
+         /* Figure out which CVT entry this applies to.  */
+         index = variation->points ? variation->points[j] : j;
+
+         if (index > interpreter->cvt_size)
+           continue;
+
+         /* Multiply the delta by the interpreter scale factor and
+            then the tuple scale factor.  */
+         delta = sfnt_mul_f26dot6_fixed (variation->deltas[j] * 64,
+                                         interpreter->scale);
+         delta = sfnt_mul_fixed_round (delta, scale);
+
+         /* Apply the delta to the control value table.  */
+         interpreter->cvt[i] += delta;
+       }
+    }
+
+  interpreter->n_axis = blend->fvar->axis_count;
+  interpreter->norm_coords = blend->norm_coords;
+}
+
+
+
+#ifdef TEST
+
+struct sfnt_test_dcontext
+{
+  /* Context for sfnt_test_get_glyph.  */
+  struct sfnt_glyf_table *glyf;
+  struct sfnt_loca_table_short *loca_short;
+  struct sfnt_loca_table_long *loca_long;
+  struct sfnt_blend *blend;
+};
+
+/* Global context for test functions.  Height of glyph.  */
+static sfnt_fixed sfnt_test_max;
+
+static void
+sfnt_test_move_to (struct sfnt_point point, void *dcontext)
+{
+  printf ("move_to: %g, %g\n", sfnt_coerce_fixed (point.x),
+         sfnt_coerce_fixed (point.y));
+}
+
+static void
+sfnt_test_line_to (struct sfnt_point point, void *dcontext)
+{
+  printf ("line_to: %g, %g\n", sfnt_coerce_fixed (point.x),
+         sfnt_coerce_fixed (point.y));
+}
+
+static void
+sfnt_test_curve_to (struct sfnt_point control,
+                   struct sfnt_point endpoint,
+                   void *dcontext)
+{
+  printf ("curve_to: %g, %g - %g, %g\n",
+         sfnt_coerce_fixed (control.x),
+         sfnt_coerce_fixed (control.y),
+         sfnt_coerce_fixed (endpoint.x),
+         sfnt_coerce_fixed (endpoint.y));
+}
+
+static struct sfnt_glyph *
+sfnt_test_get_glyph (sfnt_glyph id, void *dcontext,
+                    bool *need_free)
+{
+  struct sfnt_test_dcontext *tables;
+  struct sfnt_glyph *glyph;
+  struct sfnt_metrics_distortion distortion;
+
+  tables = dcontext;
+  *need_free = true;
+
+  glyph = sfnt_read_glyph (id, tables->glyf,
+                          tables->loca_short,
+                          tables->loca_long);
+
+  if (tables->blend && glyph)
+    {
+      if (glyph->simple)
+       sfnt_vary_simple_glyph (tables->blend, id, glyph,
+                               &distortion);
+      else
+       sfnt_vary_compound_glyph (tables->blend, id, glyph,
+                                 &distortion);
+    }
+
+  return glyph;
+}
+
+static void
+sfnt_test_free_glyph (struct sfnt_glyph *glyph, void *dcontext)
+{
+  sfnt_free_glyph (glyph);
+}
+
+static void
+sfnt_test_span (struct sfnt_edge *edge, sfnt_fixed y,
+               void *dcontext)
+{
+#if 1
+  printf ("/* span at %g */\n", sfnt_coerce_fixed (y));
+  for (; edge; edge = edge->next)
+    {
+      if (y >= edge->bottom && y < edge->top)
+       printf ("ctx.fillRect (%g, %g, 1, 1); "
+               "/* %g top: %g bot: %g stepx: %g winding: %d */\n",
+               sfnt_coerce_fixed (edge->x),
+               sfnt_coerce_fixed (sfnt_test_max - y),
+               sfnt_coerce_fixed (y),
+               sfnt_coerce_fixed (edge->top),
+               sfnt_coerce_fixed (edge->bottom),
+               sfnt_coerce_fixed (edge->step_x),
+               edge->winding);
+      else
+       printf ("STRIPPED BAD SPAN!!! %g %g %"PRIi32
+               " %"PRIi32" (winding: %d)\n",
+               sfnt_coerce_fixed (edge->top),
+               sfnt_coerce_fixed (edge->bottom),
+               edge->top, y, edge->winding);
+    }
+#elif 0
+  int winding;
+  short x, dx;
+
+  winding = 0;
+  x = 0;
+
+  for (; edge; edge = edge->next)
+    {
+      dx = (edge->x >> 16) - x;
+      x = edge->x >> 16;
+
+      for (; dx > 0; --dx)
+       putc (winding ? '.' : ' ', stdout);
+
+      winding = !winding;
+    }
+
+  putc ('\n', stdout);
+#elif 0
+  for (; edge; edge = edge->next)
+    printf ("%g-", sfnt_coerce_fixed (edge->x));
+  puts ("");
+#endif
+}
+
+static void
+sfnt_test_edge_ignore (struct sfnt_edge *edges, size_t num_edges,
+                      void *dcontext)
+{
+
+}
+
+/* The same debugger stuff is used here.  */
+static void sfnt_setup_debugger (void);
+
+/* The debugger's X display.  */
+static Display *display;
+
+/* The debugger window.  */
+static Window window;
+
+/* The GC.  */
+static GC point_gc, background_gc;
+
+static void
+sfnt_test_edges (struct sfnt_edge *edges, size_t num_edges)
+{
+  static sfnt_fixed y;
+  size_t i;
+
+  for (i = 0; i < num_edges; ++i)
+    {
+      if (y >= edges[i].bottom && y < edges[i].top)
+       {
+         XDrawPoint (display, window, point_gc,
+                     edges[i].x / 65536, 100 - (y / 65536));
+         printf ("sfnt_test_edges: %d %d\n",
+                 edges[i].x / 65536, 100 - (y / 65536));
+       }
+    }
+
+  y += SFNT_POLY_STEP;
+
+  for (i = 0; i < num_edges; ++i)
+    sfnt_step_edge (&edges[i]);
+}
+
+static void
+sfnt_debug_edges (struct sfnt_edge *edges, size_t num_edges)
+{
+  XEvent event;
+
+  sfnt_setup_debugger ();
+
+  while (true)
+    {
+      XNextEvent (display, &event);
+
+      switch (event.type)
+       {
+       case KeyPress:
+         XDestroyWindow (display, window);
+         XCloseDisplay (display);
+         exit (0);
+         break;
+
+       case Expose:
+
+         while (true)
+           {
+             sfnt_test_edges (edges, num_edges);
+             XFlush (display);
+             usleep (50000);
+           }
+
+         break;
+       }
+    }
+}
+
+static void
+sfnt_test_edge (struct sfnt_edge *edges, size_t num_edges,
+               void *dcontext)
+{
+  size_t i;
+
+  printf ("built %zu edges\n", num_edges);
+
+  for (i = 0; i < num_edges; ++i)
+    {
+      printf ("/* edge x, top, bot: %g, %g - %g.  winding: %d */\n"
+             "/* edge step_x: %g */\n",
+             sfnt_coerce_fixed (edges[i].x),
+             sfnt_coerce_fixed (edges[i].top),
+             sfnt_coerce_fixed (edges[i].bottom),
+             edges[i].winding,
+             sfnt_coerce_fixed (edges[i].step_x));
+#ifdef TEST_VERTEX
+      printf ("ctx.fillRect (%g, %g, 1, 1);\n",
+             sfnt_coerce_fixed (edges[i].x),
+             sfnt_coerce_fixed (sfnt_test_max
+                                - edges[i].y));
+#else
+      printf ("ctx.fillRect (%g, %g, 1, 1);\n",
+             sfnt_coerce_fixed (edges[i].x),
+             sfnt_coerce_fixed (sfnt_test_max
+                                - edges[i].bottom));
+#endif
+    }
+
+  if (getenv ("SFNT_DEBUG_STEP"))
+    {
+      if (!fork ())
+       sfnt_debug_edges (edges, num_edges);
+    }
+
+  printf ("==end of edges==\n");
+
+  sfnt_poly_edges (edges, num_edges, sfnt_test_span, NULL);
+}
+
+static void
+sfnt_x_raster (struct sfnt_raster **rasters,
+              int *advances,
+              int nrasters,
+              struct sfnt_hhea_table *hhea,
+              sfnt_fixed scale)
+{
+  Display *display;
+  Window window;
+  Pixmap *pixmaps;
+  Picture *glyphs, drawable, solid;
+  int event_base, error_base;
+  int major, minor, *depths, count;
+  XRenderPictFormat *format, *glyph_format;
+  Visual *visual;
+  XImage image;
+  GC gc;
+  XGCValues gcvalues;
+  XEvent event;
+  XRenderColor white, black;
+  int i, ascent, origin, x, y;
+  Font font;
+
+  if (!nrasters)
+    exit (0);
+
+  display = XOpenDisplay (NULL);
+
+  if (!display)
+    exit (0);
+
+  if (!XRenderQueryExtension (display, &event_base, &error_base)
+      || !XRenderQueryVersion (display, &major, &minor))
+    exit (0);
+
+  if (major == 0 && minor < 10)
+    exit (0);
+
+  window = XCreateSimpleWindow (display, DefaultRootWindow (display),
+                               0, 0, 100, 150, 0, 0,
+                               WhitePixel (display,
+                                           DefaultScreen (display)));
+  XSelectInput (display, window, ExposureMask);
+  XMapWindow (display, window);
+
+  visual = DefaultVisual (display, DefaultScreen (display));
+  format = XRenderFindVisualFormat (display, visual);
+
+  if (!format)
+    exit (0);
+
+  glyph_format = XRenderFindStandardFormat (display, PictStandardA8);
+  depths = XListDepths (display, DefaultScreen (display), &count);
+
+  for (i = 0; i < count; ++i)
+    {
+      if (depths[i] == 8)
+       goto depth_found;
+    }
+
+  exit (0);
+
+ depth_found:
+
+  XFree (depths);
+  pixmaps = alloca (sizeof *pixmaps * nrasters);
+  glyphs = alloca (sizeof *glyphs * nrasters);
+  gc = None;
+
+  for (i = 0; i < nrasters; ++i)
+    {
+      pixmaps[i] = XCreatePixmap (display, DefaultRootWindow (display),
+                                 rasters[i]->width, rasters[i]->height, 8);
+      if (!gc)
+       gc = XCreateGC (display, pixmaps[i], 0, &gcvalues);
+
+      /* Upload the raster.  */
+      image.width = rasters[i]->width;
+      image.height = rasters[i]->height;
+      image.xoffset = 0;
+      image.format = ZPixmap;
+      image.data = (char *) rasters[i]->cells;
+      image.byte_order = MSBFirst;
+      image.bitmap_unit = 8;
+      image.bitmap_bit_order = LSBFirst;
+      image.bitmap_pad = SFNT_POLY_ALIGNMENT * 8;
+      image.depth = 8;
+      image.bytes_per_line = rasters[i]->stride;
+      image.bits_per_pixel = 8;
+      image.red_mask = 0;
+      image.green_mask = 0;
+      image.blue_mask = 0;
+
+      if (!XInitImage (&image))
+       abort ();
+
+      XPutImage (display, pixmaps[i], gc, &image,
+                0, 0, 0, 0, image.width, image.height);
+
+      glyphs[i] = XRenderCreatePicture (display, pixmaps[i],
+                                       glyph_format, 0, NULL);
+    }
+
+  XFreeGC (display, gc);
+
+  font = XLoadFont (display, "6x13");
+
+  if (!font)
+    exit (1);
+
+  gcvalues.font = font;
+  gcvalues.foreground = BlackPixel (display, DefaultScreen (display));
+  gc = XCreateGC (display, window, GCForeground | GCFont, &gcvalues);
+
+  drawable = XRenderCreatePicture (display, window, format,
+                                  0, NULL);
+  memset (&black, 0, sizeof black);
+  black.alpha = 65535;
+
+  solid = XRenderCreateSolidFill (display, &black);
+
+  while (true)
+    {
+      XNextEvent (display, &event);
+
+      if (event.type == Expose)
+       {
+         white.red = 65535;
+         white.green = 65535;
+         white.blue = 65535;
+         white.alpha = 65535;
+
+         /* Clear the background.  */
+         XRenderFillRectangle (display, PictOpSrc, drawable,
+                               &white, 0, 0, 65535, 65535);
+
+         /* Compute ascent line.  */
+         ascent = sfnt_mul_fixed (hhea->ascent * 65536,
+                                  scale) / 65536;
+
+         origin = 0;
+
+         for (i = 0; i < nrasters; ++i)
+           {
+             /* Compute the base position.  */
+             x = origin + rasters[i]->offx;
+             y = ascent - rasters[i]->height - rasters[i]->offy;
+
+             /* Draw the solid fill with the glyph as clip mask.  */
+             XRenderComposite (display, PictOpOver, solid, glyphs[i],
+                               drawable, 0, 0, 0, 0, x, y,
+                               rasters[i]->width, rasters[i]->height);
+
+             origin += advances[i];
+           }
+       }
+    }
+}
+
+static void
+sfnt_test_raster (struct sfnt_raster *raster,
+                 struct sfnt_hhea_table *hhea,
+                 sfnt_fixed scale)
+{
+  int x, y, i;
+
+  for (y = 0; y < raster->height; ++y)
+    {
+      for (x = 0; x < raster->width; ++x)
+       printf ("%3d ", (int) raster->cells[y * raster->stride + x]);
+      puts ("");
+    }
+
+  if (hhea && getenv ("SFNT_X"))
+    {
+      i = 0;
+
+      if (!fork ())
+       sfnt_x_raster (&raster, &i, 1, hhea, scale);
+    }
+}
+
+
+
+/* Instruction execution tests.  */
+
+static struct sfnt_maxp_table test_interpreter_profile =
+  {
+    0x00010000,
+    650,
+    100,
+    100,
+    100,
+    100,
+    2,
+    100,
+    255,
+    12,
+    12,
+    100,
+    5000,
+    100,
+    1,
+  };
+
+static sfnt_fword test_cvt_values[] =
+  {
+    100, 100, -100, -100, 50, 50, 50, 50, 0, 0,
+  };
+
+static struct sfnt_cvt_table test_interpreter_cvt =
+  {
+    10,
+    test_cvt_values,
+  };
+
+static struct sfnt_head_table test_interpreter_head =
+  {
+    0x00010000,
+    0x00010000,
+    0,
+    0x5f0f3cf5,
+    0,
+    800,
+    0,
+    0,
+    0,
+    0,
+    -312,
+    -555,
+    1315,
+    2163,
+    0,
+    12,
+    0,
+    0,
+    0,
+  };
+
+static struct sfnt_interpreter *
+sfnt_make_test_interpreter (void)
+{
+  return sfnt_make_interpreter (&test_interpreter_profile,
+                               &test_interpreter_cvt,
+                               &test_interpreter_head,
+                               NULL, 17, 17);
+}
+
+struct sfnt_interpreter_test
+{
+  const char *name;
+  unsigned char *instructions;
+  int num_instructions;
+  void *arg;
+  void (*check) (struct sfnt_interpreter *, void *, bool);
+};
+
+static void
+sfnt_run_interpreter_test (struct sfnt_interpreter_test *test,
+                          struct sfnt_interpreter *interpreter)
+{
+  fprintf (stderr, "Testing %s: ", test->name);
+
+  if (setjmp (interpreter->trap))
+    test->check (interpreter, test->arg, true);
+  else
+    {
+      interpreter->IP = 0;
+      interpreter->SP = interpreter->stack;
+      interpreter->instructions = test->instructions;
+      interpreter->num_instructions = test->num_instructions;
+
+      sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_TEST);
+      test->check (interpreter, test->arg, false);
+    }
+}
+
+struct sfnt_generic_test_args
+{
+  uint32_t *expected_stack;
+  int expected_stack_elements;
+  bool expected_trap;
+  int expected_IP;
+};
+
+static void
+sfnt_generic_check (struct sfnt_interpreter *interpreter,
+                   void *arg, bool trap)
+{
+  struct sfnt_generic_test_args *args;
+  int i;
+
+  args = arg;
+
+  if (((interpreter->SP - interpreter->stack)
+       != args->expected_stack_elements))
+    {
+      fprintf (stderr,
+              "failed at IP %d:%d (expected %d stack elements,"
+              " got %td); last trap string: %s\n",
+              interpreter->call_depth, interpreter->IP,
+              args->expected_stack_elements,
+              interpreter->SP - interpreter->stack,
+              ((trap && interpreter->trap_reason)
+               ? interpreter->trap_reason
+               : "NULL"));
+
+      for (i = 0; i < interpreter->SP - interpreter->stack; ++i)
+       fprintf (stderr, "%8d ", (int) interpreter->stack[i]);
+      fprintf (stderr, "\n");
+      return;
+    }
+
+  if (memcmp (interpreter->stack, args->expected_stack,
+             ((char *) interpreter->SP
+              - (char *) interpreter->stack)))
+    {
+      fprintf (stderr, "failed (inconsistent stack elements)\n"
+              "machine stack ------------------------->\n");
+
+      for (i = 0; i < args->expected_stack_elements; ++i)
+       fprintf (stderr, "%8d ", (int) interpreter->stack[i]);
+
+      fprintf (stderr,
+              "\nexpected stack ------------------------>\n");
+
+      for (i = 0; i < args->expected_stack_elements; ++i)
+       fprintf (stderr, "%8d ", (int) args->expected_stack[i]);
+
+      fprintf (stderr, "\n");
+      return;
+    }
+
+  if (args->expected_IP != -1
+      && interpreter->IP != args->expected_IP)
+    {
+      fprintf (stderr, "failed (IP is %d, not %d)\n",
+              interpreter->IP, args->expected_IP);
+      return;
+    }
+
+  if (trap)
+    {
+      if (args->expected_trap)
+       fprintf (stderr, "passed (with trap %s)\n",
+                interpreter->trap_reason);
+      else
+       fprintf (stderr, "failed (unexpected trap %s)\n",
+                interpreter->trap_reason);
+
+      return;
+    }
+
+  if (args->expected_trap)
+    fprintf (stderr, "failed, trap not encountered\n");
+  else
+    fprintf (stderr, "passed\n");
+
+  return;
+}
+
+static void
+sfnt_check_srp0 (struct sfnt_interpreter *interpreter,
+                void *arg, bool trap)
+{
+  if (trap)
+    {
+      fprintf (stderr, "failed (unexpected trap %s)\n",
+              interpreter->trap_reason);
+      return;
+    }
+
+  if (interpreter->state.rp0 != 0)
+    {
+      fprintf (stderr, "failed, rp0 is not 0, but %d\n",
+              interpreter->state.rp0);
+      return;
+    }
+
+  if (interpreter->state.rp1 != 1)
+    {
+      fprintf (stderr, "failed, rp1 is not 1, but %d\n",
+              interpreter->state.rp1);
+      return;
+    }
+
+  if (interpreter->state.rp2 != 2)
+    {
+      fprintf (stderr, "failed, rp2 is not 2, but %d\n",
+              interpreter->state.rp2);
+      return;
+    }
+
+  if (interpreter->SP != interpreter->stack)
+    {
+      fprintf (stderr, "failed, stack not empty\n");
+      return;
+    }
+
+  fprintf (stderr, "passed\n");
+  return;
+}
+
+static void
+sfnt_check_szp0 (struct sfnt_interpreter *interpreter,
+                void *arg, bool trap)
+{
+  if (!trap)
+    {
+      fprintf (stderr, "failed, expected trap\n");
+      return;
+    }
+
+  if (interpreter->state.zp0 != 1
+      || interpreter->state.zp1 != 1
+      || interpreter->state.zp2 != 0)
+    {
+      fprintf (stderr,
+              "failed, unexpected values of zone pointers: %d %d %d\n",
+              interpreter->state.zp0, interpreter->state.zp1,
+              interpreter->state.zp2);
+      return;
+    }
+
+  if (interpreter->SP != interpreter->stack)
+    {
+      fprintf (stderr, "failed, stack not empty\n");
+      return;
+    }
+
+  fprintf (stderr, "passed with expected trap %s\n",
+          interpreter->trap_reason);
+  return;
+}
+
+static void
+sfnt_check_sloop (struct sfnt_interpreter *interpreter,
+                 void *arg, bool trap)
+{
+  if (interpreter->state.loop != 1)
+    {
+      /* The trap should've restored GS->loop to 1.  */
+      fprintf (stderr, "failed, GS->loop should be 1, not %d\n",
+              interpreter->state.loop);
+      return;
+    }
+
+  if (!trap)
+    {
+      fprintf (stderr, "failed, expected trap\n");
+      return;
+    }
+
+  if (interpreter->SP != interpreter->stack)
+    {
+      fprintf (stderr, "failed, stack not empty\n");
+      return;
+    }
+
+  fprintf (stderr, "passed with expected trap %s\n",
+          interpreter->trap_reason);
+  return;
+}
+
+struct sfnt_rounding_test_args
+{
+  sfnt_f26dot6 value;
+};
+
+static void
+sfnt_check_rounding (struct sfnt_interpreter *interpreter,
+                    void *arg, bool trap)
+{
+  sfnt_f26dot6 value;
+  struct sfnt_rounding_test_args *args;
+
+  if (trap)
+    {
+      fprintf (stderr, "failed, unexpected trap: %s\n",
+              interpreter->trap_reason);
+      return;
+    }
+
+  if (interpreter->SP == interpreter->stack)
+    {
+      fprintf (stderr, "failed, empty stack\n");
+      return;
+    }
+
+  value = *(interpreter->SP - 1);
+  args = arg;
+
+  if (value != args->value)
+    {
+      fprintf (stderr, "failed.  value is: %d %d, but wanted: %d %d\n",
+              value >> 6, value & 63, args->value >> 6,
+              args->value & 63);
+      return;
+    }
+
+  fprintf (stderr, "passed, expected value %d\n", value);
+  return;
+}
+
+static void
+sfnt_check_smd (struct sfnt_interpreter *interpreter,
+               void *arg, bool trap)
+{
+  if (trap)
+    {
+      fprintf (stderr, "failed, unexpected trap\n");
+      return;
+    }
+
+  if (interpreter->state.minimum_distance != 32)
+    {
+      fprintf (stderr, "failed, expected minimum distance"
+              " of 32, got %d\n",
+              interpreter->state.minimum_distance);
+      return;
+    }
+
+  fprintf (stderr, "passed\n");
+  return;
+}
+
+static void
+sfnt_check_scvtci (struct sfnt_interpreter *interpreter,
+                  void *arg, bool trap)
+{
+  if (trap)
+    {
+      fprintf (stderr, "failed, unexpected trap\n");
+      return;
+    }
+
+  if (interpreter->state.cvt_cut_in != 128)
+    {
+      fprintf (stderr, "failed, expected 128, got %d\n",
+              interpreter->state.cvt_cut_in);
+      return;
+    }
+
+  fprintf (stderr, "passed\n");
+  return;
+}
+
+static void
+sfnt_check_sswci (struct sfnt_interpreter *interpreter,
+                 void *arg, bool trap)
+{
+  if (trap)
+    {
+      fprintf (stderr, "failed, unexpected trap\n");
+      return;
+    }
+
+  if (interpreter->state.sw_cut_in != 512)
+    {
+      fprintf (stderr, "failed, expected 512, got %d\n",
+              interpreter->state.sw_cut_in);
+      return;
+    }
+
+  fprintf (stderr, "passed\n");
+  return;
+}
+
+static void
+sfnt_check_ssw (struct sfnt_interpreter *interpreter,
+               void *arg, bool trap)
+{
+  if (trap)
+    {
+      fprintf (stderr, "failed, unexpected trap\n");
+      return;
+    }
+
+  if (interpreter->state.single_width_value
+      != sfnt_mul_f26dot6_fixed (-64, interpreter->scale))
+    {
+      fprintf (stderr, "failed, got %d at scale %d,"
+              " expected %d\n",
+              interpreter->state.single_width_value,
+              interpreter->scale,
+              sfnt_mul_f26dot6_fixed (-64, interpreter->scale));
+      return;
+    }
+
+  fprintf (stderr, "passed\n");
+  return;
+}
+
+static void
+sfnt_check_flipon (struct sfnt_interpreter *interpreter,
+                  void *arg, bool trap)
+{
+  if (trap)
+    {
+      fprintf (stderr, "failed, unexpected trap\n");
+      return;
+    }
+
+  if (!interpreter->state.auto_flip)
+    fprintf (stderr, "failed, auto flip not enabled\n");
+  else
+    fprintf (stderr, "pass\n");
+
+  return;
+}
+
+static void
+sfnt_check_flipoff (struct sfnt_interpreter *interpreter,
+                   void *arg, bool trap)
+{
+  if (trap)
+    {
+      fprintf (stderr, "failed, unexpected trap\n");
+      return;
+    }
+
+  if (interpreter->state.auto_flip)
+    fprintf (stderr, "failed, auto flip not disabled\n");
+  else
+    fprintf (stderr, "pass\n");
+
+  return;
+}
+
+static void
+sfnt_check_sdb (struct sfnt_interpreter *interpreter,
+               void *arg, bool trap)
+{
+  if (trap)
+    {
+      fprintf (stderr, "failed, unexpected trap %s\n",
+              interpreter->trap_reason);
+      return;
+    }
+
+  if (interpreter->state.delta_base != 8)
+    fprintf (stderr, "failed, delta base is %d, not 8\n",
+            interpreter->state.delta_base);
+  else
+    fprintf (stderr, "pass\n");
+
+  return;
+}
+
+static void
+sfnt_check_sds (struct sfnt_interpreter *interpreter,
+               void *arg, bool trap)
+{
+  if (trap)
+    {
+      fprintf (stderr, "failed, unexpected trap %s\n",
+              interpreter->trap_reason);
+      return;
+    }
+
+  if (interpreter->state.delta_shift != 1)
+    fprintf (stderr, "failed, delta shift is %d, not 1\n",
+            interpreter->state.delta_shift);
+  else
+    fprintf (stderr, "pass\n");
+
+  return;
+}
+
+static void
+sfnt_check_scanctrl (struct sfnt_interpreter *interpreter,
+                    void *arg, bool trap)
+{
+  if (trap)
+    {
+      fprintf (stderr, "failed, unexpected trap %s\n",
+              interpreter->trap_reason);
+      return;
+    }
+
+  if (interpreter->SP != interpreter->stack)
+    {
+      fprintf (stderr, "failed, expected empty stack\n");
+      return;
+    }
+
+  if (interpreter->state.scan_control != 1)
+    fprintf (stderr, "failed, scan control is %d, not 1\n",
+            interpreter->state.scan_control);
+  else
+    fprintf (stderr, "pass\n");
+
+  return;
+}
+
+static void
+sfnt_check_instctrl (struct sfnt_interpreter *interpreter,
+                    void *arg, bool trap)
+{
+  if (trap)
+    {
+      fprintf (stderr, "failed, unexpected trap %s\n",
+              interpreter->trap_reason);
+      return;
+    }
+
+  if (interpreter->SP != interpreter->stack)
+    {
+      fprintf (stderr, "failed, expected empty stack\n");
+      return;
+    }
+
+  if (interpreter->state.instruct_control != 2)
+    fprintf (stderr, "failed, inst control is %d, not 2\n",
+            interpreter->state.instruct_control);
+  else
+    fprintf (stderr, "pass\n");
+
+  return;
+}
+
+static struct sfnt_generic_test_args npushb_test_args =
+  {
+    (uint32_t []) { 1U, 2U, 3U, 4U, },
+    4,
+    true,
+    6,
+  };
+
+static struct sfnt_generic_test_args npushw_test_args =
+  {
+    (uint32_t []) { 0x101U, 0x202U, 0x303U, 0x404U, },
+    4,
+    true,
+    10,
+  };
+
+static struct sfnt_generic_test_args pushb_test_args =
+  {
+    (uint32_t []) { 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U,
+                   1U, },
+    9,
+    true,
+    11,
+  };
+
+static struct sfnt_generic_test_args pushw_test_args =
+  {
+    (uint32_t []) { 0x203U, 0x204U, 0x205U, 0x206U, 0x207U, 0x208U,
+                   0x909U, 0x909U, (uint32_t) -1, },
+    9,
+    true,
+    20,
+  };
+
+static struct sfnt_generic_test_args stack_overflow_test_args =
+  {
+    (uint32_t[]) { },
+    0,
+    true,
+    0,
+  };
+
+static struct sfnt_generic_test_args stack_underflow_test_args =
+  {
+    /* GCC BUG, this should be []! */
+    (uint32_t []) { },
+    0,
+    true,
+    4,
+  };
+
+static struct sfnt_rounding_test_args rtg_test_args =
+  {
+    64,
+  };
+
+static struct sfnt_rounding_test_args rtg_symmetric_test_args =
+  {
+    -64,
+  };
+
+static struct sfnt_rounding_test_args rtg_1_test_args =
+  {
+    0,
+  };
+
+static struct sfnt_rounding_test_args rtg_1_symmetric_test_args =
+  {
+    0,
+  };
+
+static struct sfnt_rounding_test_args rthg_test_args =
+  {
+    32,
+  };
+
+static struct sfnt_rounding_test_args rthg_1_test_args =
+  {
+    96,
+  };
+
+static struct sfnt_rounding_test_args rtdg_test_args =
+  {
+    32,
+  };
+
+static struct sfnt_rounding_test_args rtdg_1_test_args =
+  {
+    0,
+  };
+
+static struct sfnt_rounding_test_args rtdg_2_test_args =
+  {
+    32,
+  };
+
+static struct sfnt_rounding_test_args rtdg_3_test_args =
+  {
+    64,
+  };
+
+static struct sfnt_generic_test_args else_test_args =
+  {
+    (uint32_t []) { 77U, 90U, 83U, },
+    3,
+    false,
+    40,
+  };
+
+static struct sfnt_generic_test_args jmpr_test_args =
+  {
+    /* What ends up on the stack?
+
+       First, there are the three words that the first PUSHW[2]
+       instruction has pushed:
+
+        0, 0xb2, -3
+
+       After those three words are pushed, JMPR[] is called, and pops an
+       offset:
+
+        -3
+
+       so now the stack is:
+
+        0, 0xb2
+
+       as a result of the relative jump, IP is now at the least
+       significant byte of the word inside what was originally a
+       PUSHW[2] instruction, 0xb2, which itself is PUSHB[2]!
+
+       As a result of that instruction, three more bytes, including
+       JMPR[] itself are pushed onto the stack, making it:
+
+        0, 0xb2, 255, 253, 0x1c
+
+       Then, execution continues as usual.  4 is pushed on to the
+       stack, making it:
+
+        0, 0xb2, 255, 253, 0x1c, 4
+
+       Another JMPR[] pops:
+
+        4
+
+       making the stack:
+
+        0, 0xb2, 255, 253, 0x1c
+
+       And skips the next three padding bytes, finally reaching a
+       PUSHW[0] instruction which pushes -30 onto the stack:
+
+        0, 0xb2, 255, 253, 0x1c, -30
+
+       and a JMPR[] instruction, which pops:
+
+        -30
+
+       making:
+
+        0, 0xb2, 255, 253,
+
+       and subsequently traps, as -30 would underflow the instruction
+       stream.  */
+    (uint32_t []) { 0, 0xb2, 255, 253, 0x1c, },
+    5,
+    true,
+    17,
+  };
+
+static struct sfnt_generic_test_args dup_test_args =
+  {
+    (uint32_t []) { },
+    0,
+    true,
+    5,
+  };
+
+static struct sfnt_generic_test_args pop_test_args =
+  {
+    (uint32_t []) { 70, 70, },
+    2,
+    false,
+    5,
+  };
+
+static struct sfnt_generic_test_args clear_test_args =
+  {
+    (uint32_t []) { },
+    0,
+    false,
+    10,
+  };
+
+static struct sfnt_generic_test_args swap_test_args =
+  {
+    (uint32_t []) { 2, 1, },
+    2,
+    false,
+    4,
+  };
+
+static struct sfnt_generic_test_args depth_test_args =
+  {
+    (uint32_t []) { 3, 3, 3, 3, },
+    4,
+    false,
+    5,
+  };
+
+static struct sfnt_generic_test_args cindex_test_args =
+  {
+    (uint32_t []) { 0, 3, 3, 4, 0, },
+    5,
+    true,
+    10,
+  };
+
+static struct sfnt_generic_test_args mindex_test_args =
+  {
+    (uint32_t []) { 0, 3, 7, 4, 4, },
+    5,
+    false,
+    10,
+  };
+
+static struct sfnt_generic_test_args raw_test_args =
+  {
+    (uint32_t []) { },
+    0,
+    true,
+    0,
+  };
+
+static struct sfnt_generic_test_args loopcall_test_args =
+  {
+    (uint32_t []) { 10, },
+    1,
+    false,
+    12,
+  };
+
+static struct sfnt_generic_test_args call_test_args =
+  {
+    (uint32_t []) { 11, },
+    1,
+    true,
+    2,
+  };
+
+static struct sfnt_generic_test_args fdef_test_args =
+  {
+    (uint32_t []) { },
+    0,
+    true,
+    4,
+  };
+
+static struct sfnt_generic_test_args fdef_1_test_args =
+  {
+    (uint32_t []) { },
+    0,
+    true,
+    9,
+  };
+
+static struct sfnt_generic_test_args endf_test_args =
+  {
+    (uint32_t []) {  },
+    0,
+    true,
+    0,
+  };
+
+static struct sfnt_generic_test_args ws_test_args =
+  {
+    (uint32_t []) { 40, },
+    1,
+    true,
+    10,
+  };
+
+static struct sfnt_generic_test_args rs_test_args =
+  {
+    (uint32_t []) { },
+    0,
+    true,
+    2,
+  };
+
+static struct sfnt_generic_test_args wcvtp_test_args =
+  {
+    (uint32_t []) { 32, },
+    1,
+    true,
+    10,
+  };
+
+static struct sfnt_generic_test_args rcvt_test_args =
+  {
+    (uint32_t []) { 136, },
+    1,
+    true,
+    5,
+  };
+
+static struct sfnt_generic_test_args mppem_test_args =
+  {
+    (uint32_t []) { 17, },
+    1,
+    false,
+    1,
+  };
+
+static struct sfnt_generic_test_args mps_test_args =
+  {
+    (uint32_t []) { 17, },
+    1,
+    false,
+    1,
+  };
+
+static struct sfnt_generic_test_args debug_test_args =
+  {
+    (uint32_t []) { },
+    0,
+    false,
+    3,
+  };
+
+static struct sfnt_generic_test_args lt_test_args =
+  {
+    (uint32_t []) { 1, 0, 0, },
+    3,
+    false,
+    12,
+  };
+
+static struct sfnt_generic_test_args lteq_test_args =
+  {
+    (uint32_t []) { 1, 0, 1, },
+    3,
+    false,
+    12,
+  };
+
+static struct sfnt_generic_test_args gt_test_args =
+  {
+    (uint32_t []) { 0, 1, 0, },
+    3,
+    false,
+    12,
+  };
+
+static struct sfnt_generic_test_args gteq_test_args =
+  {
+    (uint32_t []) { 0, 1, 1, },
+    3,
+    false,
+    12,
+  };
+
+static struct sfnt_generic_test_args eq_test_args =
+  {
+    (uint32_t []) { 0, 1, 0, },
+    3,
+    false,
+    18,
+  };
+
+static struct sfnt_generic_test_args neq_test_args =
+  {
+    (uint32_t []) { 1, 0, 1, },
+    3,
+    false,
+    18,
+  };
+
+static struct sfnt_generic_test_args odd_test_args =
+  {
+    (uint32_t []) { 1, 0, },
+    2,
+    false,
+    9,
+  };
+
+static struct sfnt_generic_test_args even_test_args =
+  {
+    (uint32_t []) { 0, 1, },
+    2,
+    false,
+    9,
+  };
+
+static struct sfnt_generic_test_args if_test_args =
+  {
+    (uint32_t []) { 17, 24, 1, 2, 3, 4, 5, -1, -1,
+                   88, 1, 3, },
+    12,
+    false,
+    185,
+  };
+
+static struct sfnt_generic_test_args eif_test_args =
+  {
+    (uint32_t []) { },
+    0,
+    false,
+    3,
+  };
+
+static struct sfnt_generic_test_args and_test_args =
+  {
+    (uint32_t []) { 0, 0, 1, 0, },
+    4,
+    false,
+    16,
+  };
+
+static struct sfnt_generic_test_args or_test_args =
+  {
+    (uint32_t []) { 1, 1, 1, 0, },
+    4,
+    false,
+    16,
+  };
+
+static struct sfnt_generic_test_args not_test_args =
+  {
+    (uint32_t []) { 0, 1, },
+    2,
+    false,
+    6,
+  };
+
+static struct sfnt_generic_test_args sds_test_args =
+  {
+    (uint32_t []) { },
+    0,
+    true,
+    5,
+  };
+
+static struct sfnt_generic_test_args add_test_args =
+  {
+    (uint32_t []) { 96, -1, },
+    2,
+    false,
+    10,
+  };
+
+static struct sfnt_generic_test_args sub_test_args =
+  {
+    (uint32_t []) { 64, -64, 431, },
+    3,
+    false,
+    14,
+  };
+
+static struct sfnt_generic_test_args div_test_args =
+  {
+    (uint32_t []) { 32, -64, },
+    2,
+    true,
+    15,
+  };
+
+static struct sfnt_generic_test_args mul_test_args =
+  {
+    (uint32_t []) { 255, -255, 255, },
+    3,
+    false,
+    16,
+  };
+
+static struct sfnt_generic_test_args abs_test_args =
+  {
+    (uint32_t []) { 1, 1, },
+    2,
+    false,
+    7,
+  };
+
+static struct sfnt_generic_test_args neg_test_args =
+  {
+    (uint32_t []) { 1, -1, },
+    2,
+    false,
+    7,
+  };
+
+static struct sfnt_generic_test_args floor_test_args =
+  {
+    (uint32_t []) { -128, -64, 0, 64, 128, },
+    5,
+    false,
+    17,
+  };
+
+static struct sfnt_generic_test_args ceiling_test_args =
+  {
+    (uint32_t []) { -128, -128, -64, 0, 64, 128, 128, },
+    7,
+    false,
+    25,
+  };
+
+static struct sfnt_generic_test_args round_test_args =
+  {
+    (uint32_t []) { },
+    0,
+    true,
+    0,
+  };
+
+static struct sfnt_generic_test_args nround_test_args =
+  {
+    (uint32_t []) { 63, },
+    1,
+    false,
+    3,
+  };
+
+static struct sfnt_generic_test_args wcvtf_test_args =
+  {
+    (uint32_t []) { (63 * 17 * 65535 / 800) >> 10, },
+    1,
+    false,
+    7,
+  };
+
+static struct sfnt_generic_test_args jrot_test_args =
+  {
+    (uint32_t []) { 40, 40, },
+    2,
+    false,
+    13,
+  };
+
+static struct sfnt_generic_test_args jrof_test_args =
+  {
+    (uint32_t []) { 4, },
+    1,
+    false,
+    13,
+  };
+
+static struct sfnt_generic_test_args deltac1_test_args =
+  {
+    (uint32_t []) { ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8,
+                   ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8, },
+    2,
+    false,
+    22,
+  };
+
+static struct sfnt_generic_test_args deltac2_test_args =
+  {
+    (uint32_t []) { ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8,
+                   ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8, },
+    2,
+    false,
+    22,
+  };
+
+static struct sfnt_generic_test_args deltac3_test_args =
+  {
+    (uint32_t []) { ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8,
+                   ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8, },
+    2,
+    false,
+    22,
+  };
+
+/* Macros and instructions for detailed rounding tests.  */
+
+/* PUSHB[0] period:phase:threshold
+   SROUND[] */
+#define SFNT_ROUNDING_OPERAND(period, phase, threshold)        \
+  0xb0, (((unsigned char) period << 6)                 \
+        | ((unsigned char) phase & 3) << 4             \
+        | ((unsigned char) threshold & 15)), 0x76
+
+/* PUSHB[0] period:phase:threshold
+   S45ROUND[] */
+#define SFNT_ROUNDING_OPERAND_45(period, phase, threshold)     \
+  0xb0, (((unsigned char) period << 6)                         \
+        | ((unsigned char) phase & 3) << 4                     \
+        | ((unsigned char) threshold & 15)), 0x77
+
+/* PUSHB[0] value
+   ROUND[] */
+#define SFNT_ROUND_VALUE(value) 0xb0, value, 0x68
+
+static unsigned char sfnt_sround_instructions[] =
+  {
+    SFNT_ROUNDING_OPERAND (0, 0, 8),
+    SFNT_ROUND_VALUE (15),
+    SFNT_ROUND_VALUE (17),
+    SFNT_ROUNDING_OPERAND (1, 0, 8),
+    SFNT_ROUND_VALUE (32),
+    SFNT_ROUND_VALUE (16),
+    SFNT_ROUNDING_OPERAND (2, 0, 8),
+    SFNT_ROUND_VALUE (64),
+    SFNT_ROUND_VALUE (63),
+    SFNT_ROUNDING_OPERAND (0, 1, 8),
+    SFNT_ROUND_VALUE (16),
+    SFNT_ROUND_VALUE (24),
+    SFNT_ROUNDING_OPERAND (0, 2, 8),
+    SFNT_ROUND_VALUE (20),
+    SFNT_ROUND_VALUE (48),
+    SFNT_ROUNDING_OPERAND (0, 3, 8),
+    SFNT_ROUND_VALUE (7),
+    SFNT_ROUND_VALUE (70),
+  };
+
+static uint32_t sfnt_sround_values[] =
+  {
+    /* 0, 0, 8 = RTDG; 15 rounded to the double grid and becomes 0, 17
+       is 32.  */
+    0, 32,
+    /* 1, 0, 8 = RTG; 32 rounded to the grid is 64, 16 is 0.  */
+    64, 0,
+    /* 2, 0, 8 = round to a grid separated by 128s.  64 is 128, 63 is
+       0.  */
+    128, 0,
+    /* 0, 1, 8 = round to a double grid with a phase of 8.  16 rounds
+       down to 8, 24 rounds up to 40.  */
+    8, 40,
+    /* 0, 2, 8 = round to a double grid with a phase of 16.  20 rounds
+       down to 16, 40 rounds up to 48.  */
+    16, 48,
+    /* 0, 3, 8 = round to a double grid with a phase of 48.  7 rounds
+       up to 16, 70 rounds up to 80.  */
+    16, 80,
+  };
+
+static struct sfnt_generic_test_args sround_test_args =
+  {
+    sfnt_sround_values,
+    ARRAYELTS (sfnt_sround_values),
+    false,
+    ARRAYELTS (sfnt_sround_instructions),
+  };
+
+static unsigned char sfnt_s45round_instructions[] =
+  {
+    SFNT_ROUNDING_OPERAND_45 (0, 0, 0),
+    SFNT_ROUND_VALUE (1),
+    SFNT_ROUND_VALUE (45),
+  };
+
+static uint32_t sfnt_s45round_values[] =
+  {
+    /* 0, 0, 0: 1 rounded to the double cubic grid becomes 45, and 46
+       rounded to the double cubic grid becomes 90.  */
+    45, 90,
+  };
+
+static struct sfnt_generic_test_args s45round_test_args =
+  {
+    sfnt_s45round_values,
+    ARRAYELTS (sfnt_s45round_values),
+    false,
+    ARRAYELTS (sfnt_s45round_instructions),
+  };
+
+static struct sfnt_generic_test_args rutg_test_args =
+  {
+    (uint32_t []) { 64, 64, 0, },
+    3,
+    false,
+    10,
+  };
+
+static struct sfnt_generic_test_args rdtg_test_args =
+  {
+    (uint32_t []) { 0, 0, 64, },
+    3,
+    false,
+    10,
+  };
+
+static struct sfnt_generic_test_args sangw_test_args =
+  {
+    (uint32_t []) { },
+    0,
+    false,
+    3,
+  };
+
+static struct sfnt_generic_test_args aa_test_args =
+  {
+    (uint32_t []) { },
+    0,
+    false,
+    3,
+  };
+
+static struct sfnt_generic_test_args getinfo_test_args =
+  {
+    /* Pretend to be the Macintosh System 7 scaler.
+
+       This lets the interpreter get away with only two phantom
+       points, as specified in Apple's TrueType reference manual.  */
+    (uint32_t []) { 2, 0, },
+    2,
+    false,
+    6,
+  };
+
+static struct sfnt_generic_test_args idef_test_args =
+  {
+    (uint32_t []) { 1, 2, 3, },
+    3,
+    false,
+    11,
+  };
+
+static struct sfnt_generic_test_args roll_test_args =
+  {
+    (uint32_t []) { 1, 2, 4, 5, 3, },
+    5,
+    false,
+    7,
+  };
+
+static struct sfnt_generic_test_args roll_1_test_args =
+  {
+    (uint32_t []) { 1, 2, },
+    2,
+    true,
+    3,
+  };
+
+static struct sfnt_generic_test_args max_test_args =
+  {
+    (uint32_t []) { 70, },
+    1,
+    false,
+    6,
+  };
+
+static struct sfnt_generic_test_args min_test_args =
+  {
+    (uint32_t []) { -70, },
+    1,
+    false,
+    6,
+  };
+
+static struct sfnt_generic_test_args scantype_test_args =
+  {
+    (uint32_t []) { },
+    0,
+    false,
+    3,
+  };
+
+static struct sfnt_interpreter_test all_tests[] =
+  {
+    {
+      "NPUSHB",
+      /* NPUSHB[] 4 1 2 3 4
+        NPUSHB[] 5 1 2 3 4 */
+      (unsigned char []) { 0x40, 4, 1, 2, 3, 4,
+                          0x40, 5, 1, 2, 3, 4, },
+      10,
+      &npushb_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "NPUSHW",
+      /* NPUSHW[] 4 0x101 0x202 0x303 0x404
+        NPUSHW[] 4 0x101 0x202 0x303 0x4?? */
+      (unsigned char []) { 0x41, 4, 1, 1, 2, 2, 3, 3, 4, 4,
+                          0x41, 4, 1, 1, 2, 2, 3, 3, 4, },
+      19,
+      &npushw_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "PUSHB",
+      /* PUSHB[7] 1 2 3 4 5 6 7 8
+        PUSHB[0] 1
+        PUSHB[5] 1 2 3 4 5 ? */
+      (unsigned char []) { 0xb7, 1, 2, 3, 4, 5, 6, 7, 8,
+                          0xb0, 1,
+                          0xb5, 1, 2, 3, 4, 5, },
+      17,
+      &pushb_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "PUSHW",
+      /* PUSHW[7] 2 3 2 4 2 5 2 6 2 7 2 8 9 9 9 9
+        PUSHW[0] 255 255 -- this should get sign-extended
+        PUSHW[5] 1 1 2 2 3 3 4 4 5 5 6 ? */
+      (unsigned char []) { 0xbf, 2, 3, 2, 4, 2, 5, 2, 6, 2, 7, 2, 8, 9, 9, 9, 
9,
+                          0xb8, 255, 255,
+                          0xbc, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, },
+      28,
+      &pushw_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "that stack overflow is handled correctly",
+      /* NPUSHB[] 101 0... */
+      (unsigned char [103]) { 0x40, 101, },
+      103,
+      &stack_overflow_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "that stack underflow is handled correctly",
+      /* PUSHW[0] 100 100
+        POP[]
+        POP[] */
+      (unsigned char []) { 0xb8, 100, 100,
+                          0x21,
+                          0x21, },
+      5,
+      &stack_underflow_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "SRP0, SRP1, SRP2",
+      /* PUSHB[0] 0
+        SRP0[]
+        PUSHB[0] 1
+        SRP1[]
+        PUSHB[0] 2
+        SRP2[] */
+      (unsigned char []) { 0xb0, 0,
+                          0x10,
+                          0xb0, 1,
+                          0x11,
+                          0xb0, 2,
+                          0x12, },
+      9,
+      NULL,
+      sfnt_check_srp0,
+    },
+    {
+      "SZP0, SZP1, SZP2, SZPS",
+      /* PUSHB[0] 1
+        SZP0[]
+        PUSHB[0] 1
+        SZP1[]
+        PUSHB[0] 0
+        SZP2[]
+        PUSHB[0] 5
+        SZPS[]  */
+      (unsigned char []) { 0xb0, 1,
+                          0x13,
+                          0xb0, 1,
+                          0x14,
+                          0xb0, 0,
+                          0x15,
+                          0xb0, 5,
+                          0x16, },
+      12,
+      NULL,
+      sfnt_check_szp0,
+    },
+    {
+      "SLOOP",
+      /* PUSHB[0] 2
+        SLOOP[]
+        PUSHB[0] 0
+        SLOOP[] */
+      (unsigned char []) { 0xb0, 2,
+                          0x17,
+                          0xb0, 0,
+                          0x17, },
+      6,
+      NULL,
+      sfnt_check_sloop,
+    },
+    {
+      "RTG",
+      /* RTG[]
+        PUSHB[0] 32
+        ROUND[] */
+      (unsigned char []) { 0x18,
+                          0xb0, 32,
+                          0x68, },
+      4,
+      &rtg_test_args,
+      sfnt_check_rounding,
+    },
+    {
+      "rounding symmetry",
+      /* RTG[]
+        PUSHW[0] 255 -32
+        ROUND[] */
+      (unsigned char []) { 0x18,
+                          0xb8, 255, - (signed char) 32,
+                          0x68, },
+      5,
+      &rtg_symmetric_test_args,
+      sfnt_check_rounding,
+    },
+    {
+      "RTG to 0",
+      /* RTG[]
+        PUSHB[0] 31
+        ROUND[] */
+      (unsigned char []) { 0x18,
+                          0xb0, 31,
+                          0x68, },
+      4,
+      &rtg_1_test_args,
+      sfnt_check_rounding,
+    },
+    {
+      "rounding symmetry to 0",
+      /* RTG[]
+        PUSHB[0] 255 -31
+        ROUND[] */
+      (unsigned char []) { 0x18,
+                          0xb8, 255, - (signed char) 31,
+                          0x68, },
+      5,
+      &rtg_1_symmetric_test_args,
+      sfnt_check_rounding,
+    },
+    {
+      "RTHG",
+      /* RTHG[]
+        PUSHB[0] 0
+        ROUND[] */
+      (unsigned char []) { 0x19,
+                          0xb0, 0,
+                          0x68, },
+      4,
+      &rthg_test_args,
+      sfnt_check_rounding,
+    },
+    {
+      "RTHG to 96",
+      /* RTHG[]
+        PUSHB[0] 64
+        ROUND[] */
+      (unsigned char []) { 0x19,
+                          0xb0, 64,
+                          0x68, },
+      4,
+      &rthg_1_test_args,
+      sfnt_check_rounding,
+    },
+    {
+      "SMD",
+      /* PUSHB[0] 32
+        SMD[] */
+      (unsigned char []) { 0xb0, 32,
+                          0x1a, },
+      3,
+      NULL,
+      sfnt_check_smd,
+    },
+    {
+      "ELSE",
+      /* ELSE[]
+        ;; Lots of variable length instructions
+        ;; which will not be executed, like:
+        NPUSHW[] 3 11 22 33 44 55 66
+        NPUSHB[] 1 3
+        PUSHW[2] 1 1 2 2 3 3
+        PUSHB[2] 1 2 3
+        ;; Also test nested ifs.
+        PUSHW[0] 1 1
+        IF[]
+        PUSHW[0] 1 1
+        ELSE[]
+        PUSHW[0] 1 1
+        EIF[]
+        EIF[]
+        PUSHW[0] 1 1
+        ;; the actual contents of the stack.
+        PUSHB[2] 77 90 83 */
+      (unsigned char []) { 0x1b,
+                          0x41, 3, 11, 22, 33, 44, 55, 66,
+                          0x40, 1, 3,
+                          0xba, 1, 1, 2, 2, 3, 3,
+                          0xb2, 1, 2, 3,
+                          0xb8, 1, 1,
+                          0x58,
+                          0xb8, 1, 1,
+                          0x1b,
+                          0xb8, 1, 1,
+                          0x59,
+                          0x59,
+                          0xb2, 77, 90, 83, },
+      40,
+      &else_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "JMPR",
+      /* PUSHW[2] 00 00 00 PUSHB[2] 255 253 JMPR[]
+        PUSHB[0] 4
+        JMPR[]
+        255 255 255
+        PUSHW[0] 255 -30
+        JMPR[] */
+      (unsigned char []) { 0xba, 00, 00, 00, 0xb2, 255, 253, 0x1c,
+                          0xb0, 4,
+                          0x1c,
+                          255, 255, 255,
+                          0xb8, 255, -30,
+                          0x1c, },
+      18,
+      &jmpr_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "SCVTCI",
+      /* PUSHB[0] 128
+        SCVTCI[] */
+      (unsigned char []) { 0xb0, 128,
+                          0x1d, },
+      3,
+      NULL,
+      sfnt_check_scvtci,
+    },
+    {
+      "SSWCI",
+      /* PUSHW[0] 2 0 ;; 512
+        SSWCI[] */
+      (unsigned char []) { 0xb8, 2, 0,
+                          0x1e, },
+      4,
+      NULL,
+      sfnt_check_sswci,
+    },
+    {
+      "SSW",
+      /* PUSHW[0] 255 255 ; -1
+        SSW[] ; this should be converted to device-space */
+      (unsigned char []) { 0xb8, 255, 255,
+                          0x1f, },
+      4,
+      NULL,
+      sfnt_check_ssw,
+    },
+    {
+      "DUP",
+      /* PUSHB[0] 70
+        DUP[]
+        POP[]
+        POP[]
+        DUP[] */
+      (unsigned char []) { 0xb0, 70,
+                          0x20,
+                          0x21,
+                          0x21,
+                          0x70, },
+      6,
+      &dup_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "POP",
+      /* PUSHB[0] 70
+        DUP[]
+        DUP[]
+        POP[] */
+      (unsigned char []) { 0xb0, 70,
+                          0x20,
+                          0x20,
+                          0x21, },
+      5,
+      &pop_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "CLEAR",
+      /* PUSHB[7] 1 2 3 4 5 6 7 8
+        CLEAR[] */
+      (unsigned char []) { 0xb7, 1, 2, 3, 4, 5, 6, 7, 8,
+                          0x22, },
+      10,
+      &clear_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "SWAP",
+      /* PUSHB[1] 1 2
+        SWAP[] */
+      (unsigned char []) { 0xb1, 1, 2,
+                          0x23, },
+      4,
+      &swap_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "DEPTH",
+      /* PUSHB[2] 3 3 3
+        DEPTH[] */
+      (unsigned char []) { 0xb2, 3, 3, 3,
+                          0x24, },
+      5,
+      &depth_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "CINDEX",
+      /* PUSHB[4] 0 3 3 4 1
+        CINDEX[] ; pops 1, indices 4
+        CINDEX[] ; pops 4, indices 0
+        PUSHB[0] 6
+        CINDEX[] ; pops 6, trap */
+      (unsigned char []) { 0xb4, 0, 3, 3, 4, 1,
+                          0x25,
+                          0x25,
+                          0xb0, 6,
+                          0x25, },
+      11,
+      &cindex_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "MINDEX",
+      /* PUSHB[6] 0 3 4 7 3 4 2
+        MINDEX[] ; pops 2, array becomes 0 3 4 7 4 3
+        MINDEX[] ; pops 3, array becomes 0 3 7 4 4 */
+      (unsigned char []) { 0xb6, 0, 3, 4, 7, 3, 4, 2,
+                          0x26,
+                          0x26, },
+      10,
+      &mindex_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "RAW",
+      /* RAW[] */
+      (unsigned char []) { 0x28, },
+      1,
+      &raw_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "LOOPCALL",
+      /* PUSHB[1] 0 2
+        FDEF[]
+        PUSHB[0] 1
+        ADD[]
+        ENDF[]
+        PUSHB[1] 10 2
+        LOOPCALL[]  */
+      (unsigned char []) { 0xb1, 0, 2,
+                          0x2c,
+                          0xb0, 1,
+                          0x60,
+                          0x2d,
+                          0xb1, 10, 2,
+                          0x2a, },
+      12,
+      &loopcall_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "CALL",
+      /* PUSHB[1] 7 2
+        FDEF[]
+        PUSHB[0] 1
+        ADD[]
+        ENDF[]
+        PUSHB[0] 2
+        CALL[]
+        PUSHB[0] 3
+        ADD[]
+        ;; Test that infinite recursion fails.
+        PUSHB[0] 3
+        FDEF[]
+        PUSHB[0] 3
+        CALL[]
+        ENDF[]
+        PUSHB[0] 3
+        CALL[] */
+      (unsigned char []) { 0xb1, 7, 2,
+                          0x2c,
+                          0xb0, 1,
+                          0x60,
+                          0x2d,
+                          0xb0, 2,
+                          0x2b,
+                          0xb0, 3,
+                          0x60,
+                          0xb0, 3,
+                          0x2c,
+                          0xb0, 3,
+                          0x2b,
+                          0x2d,
+                          0xb0, 3,
+                          0x2b, },
+      24,
+      &call_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "that FDEF traps inside nested definitions",
+      /* PUSHB[0] 1
+        FDEF[]
+        FDEF[]
+        ENDF[]
+        ENDF[] */
+      (unsigned char []) { 0xb0, 1,
+                          0x2c,
+                          0x2c,
+                          0x2d,
+                          0x2d, },
+      6,
+      &fdef_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "that FDEF traps upon missing ENDF",
+      /* PUSHB[0] 1
+        FDEF[]
+        PUSHB[3] 1 2 3 4
+        POP[]  */
+      (unsigned char []) { 0xb0, 1,
+                          0x2c,
+                          0xb3, 1, 2, 3, 4,
+                          0x21, },
+      9,
+      &fdef_1_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "ENDF",
+      /* ENDF[] */
+      (unsigned char []) { 0x2d, },
+      1,
+      &endf_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "RTDG",
+      /* RTDG[]
+        PUSHB[0] 16
+        ROUND[] */
+      (unsigned char []) { 0x3d,
+                          0xb0, 16,
+                          0x68, },
+      4,
+      &rtdg_test_args,
+      sfnt_check_rounding,
+    },
+    {
+      "RTDG down to 0",
+      /* RTDG[]
+        PUSHB[0] 15
+        ROUND[] */
+      (unsigned char []) { 0x3d,
+                          0xb0, 15,
+                          0x68, },
+      4,
+      &rtdg_1_test_args,
+      sfnt_check_rounding,
+    },
+    {
+      "RTDG down to 32",
+      /* RTDG[]
+        PUSHB[0] 47
+        ROUND[] */
+      (unsigned char []) { 0x3d,
+                          0xb0, 47,
+                          0x68, },
+      4,
+      &rtdg_2_test_args,
+      sfnt_check_rounding,
+    },
+    {
+      "RTDG up to 64",
+      /* RTDG[]
+        PUSHB[0] 48
+        ROUND[] */
+      (unsigned char []) { 0x3d,
+                          0xb0, 48,
+                          0x68, },
+      4,
+      &rtdg_3_test_args,
+      sfnt_check_rounding,
+    },
+    {
+      "WS",
+      /* PUSHB[1] 240 40
+        WS[]
+        PUSHB[0] 240
+        RS[]
+        PUSHB[1] 255 40
+        WS[] */
+      (unsigned char []) { 0xb1, 240, 40,
+                          0x42,
+                          0xb0, 240,
+                          0x43,
+                          0xb1, 255, 40,
+                          0x42, },
+      11,
+      &ws_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "RS",
+      /* PUSHB[0] 255
+        RS[] */
+      (unsigned char []) { 0xb0, 255,
+                          0x43, },
+      3,
+      &rs_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "WCVTP",
+      /* PUSHB[1] 9 32
+        WCVTP[]
+        PUSHB[0] 9
+        RCVT[]
+        PUSHB[1] 10 10
+        WCVTP[] */
+      (unsigned char []) { 0xb1, 9, 32,
+                          0x44,
+                          0xb0, 9,
+                          0x45,
+                          0xb1, 10, 10,
+                          0x44, },
+      11,
+      &wcvtp_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "RCVT",
+      /* PUSHB[0] 1
+        RCVT[]
+        PUSHB[0] 10
+        RCVT[] */
+      (unsigned char []) { 0xb0, 1,
+                          0x45,
+                          0xb0, 10,
+                          0x45, },
+      6,
+      &rcvt_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "MPPEM",
+      /* MPPEM[] */
+      (unsigned char []) { 0x4b, },
+      1,
+      &mppem_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "MPS",
+      /* MPS[] */
+      (unsigned char []) { 0x4c, },
+      1,
+      &mps_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "FLIPON",
+      /* FLIPON[] */
+      (unsigned char []) { 0x4d, },
+      1,
+      NULL,
+      sfnt_check_flipon,
+    },
+    {
+      "FLIPOFF",
+      /* FLIPOFF[] */
+      (unsigned char []) { 0x4e, },
+      1,
+      NULL,
+      sfnt_check_flipoff,
+    },
+    {
+      "DEBUG",
+      /* PUSHB[0] 1
+        DEBUG[] */
+      (unsigned char []) { 0xb0, 1,
+                          0x4f, },
+      3,
+      &debug_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "LT",
+      /* PUSHB[1] 47 48
+        LT[]
+        PUSHB[1] 48 47
+        LT[]
+        PUSHB[1] 47 47
+        LT[] */
+      (unsigned char []) { 0xb1, 47, 48,
+                          0x50,
+                          0xb1, 48, 47,
+                          0x50,
+                          0xb1, 47, 47,
+                          0x50, },
+      12,
+      &lt_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "LTEQ",
+      /* PUSHB[1] 47 48
+        LTEQ[]
+        PUSHB[1] 48 47
+        LTEQ[]
+        PUSHB[1] 47 47
+        LTEQ[] */
+      (unsigned char []) { 0xb1, 47, 48,
+                          0x51,
+                          0xb1, 48, 47,
+                          0x51,
+                          0xb1, 47, 47,
+                          0x51, },
+      12,
+      &lteq_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "GT",
+      /* PUSHB[1] 47 48
+        GT[]
+        PUSHB[1] 48 47
+        GT[]
+        GT[1] 47 47
+        LTEQ[] */
+      (unsigned char []) { 0xb1, 47, 48,
+                          0x52,
+                          0xb1, 48, 47,
+                          0x52,
+                          0xb1, 47, 47,
+                          0x52, },
+      12,
+      &gt_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "GTEQ",
+      /* PUSHB[1] 47 48
+        GTEQ[]
+        PUSHB[1] 48 47
+        GTEQ[]
+        GTEQ[1] 47 47
+        LTEQ[] */
+      (unsigned char []) { 0xb1, 47, 48,
+                          0x53,
+                          0xb1, 48, 47,
+                          0x53,
+                          0xb1, 47, 47,
+                          0x53, },
+      12,
+      &gteq_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "EQ",
+      /* PUSHW[1] 255 253 255 255
+        EQ[]
+        PUSHW[1] 27 27 27 27
+        EQ[]
+        PUSHB[0] 3
+        PUSHW[0] 255 254
+        EQ[] */
+      (unsigned char []) { 0xb9, 255, 253, 255, 255,
+                          0x54,
+                          0xb9, 27, 27, 27, 27,
+                          0x54,
+                          0xb0, 3,
+                          0xb8, 255, 254,
+                          0x54, },
+      18,
+      &eq_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "NEQ",
+      /* PUSHW[1] 255 253 255 255
+        NEQ[]
+        PUSHW[1] 27 27 27 27
+        NEQ[]
+        PUSHB[0] 3
+        PUSHW[0] 255 254
+        NEQ[] */
+      (unsigned char []) { 0xb9, 255, 253, 255, 255,
+                          0x55,
+                          0xb9, 27, 27, 27, 27,
+                          0x55,
+                          0xb0, 3,
+                          0xb8, 255, 254,
+                          0x55, },
+      18,
+      &neq_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "ODD",
+      /* RTG[]
+        PUSHW[1] 255 224 ;; -32
+        ODD[] ;; Rounds symmetrically to -64, which is odd.
+        PUSHW[1] 255 159 ;; -96
+        ODD[] ;; Rounds symmetrically to -128, which is even.  */
+      (unsigned char []) { 0x18,
+                          0xb8, 255, 224,
+                          0x56,
+                          0xb8, 255, 159,
+                          0x56, },
+      9,
+      &odd_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "EVEN",
+      /* RTG[]
+        PUSHW[1] 255 224 ;; -32
+        EVEN[] ;; Rounds symmetrically to -64, which is odd.
+        PUSHW[1] 255 159 ;; -96
+        EVEN[] ;; Rounds symmetrically to -128, which is even.  */
+      (unsigned char []) { 0x18,
+                          0xb8, 255, 224,
+                          0x57,
+                          0xb8, 255, 159,
+                          0x57, },
+      9,
+      &even_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "IF",
+      /* NPUSHB[] 1 0
+        IF[]
+        PUSHW[0] 1 1
+        PUSHW[1] 1 1 2 2
+        PUSHW[2] 1 1 2 2 3 3
+        PUSHW[3] 1 1 2 2 3 3 4 4
+        PUSHW[4] 1 1 2 2 3 3 4 4 5 5
+        PUSHW[5] 1 1 2 2 3 3 4 4 5 5 6 6
+        PUSHW[6] 1 1 2 2 3 3 4 4 5 5 6 6 7 7
+        PUSHW[7] 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8
+        PUSHB[0] 1
+        PUSHB[1] 2 1
+        PUSHB[2] 3 2 1
+        PUSHB[3] 4 3 2 1
+        PUSHB[4] 5 4 3 2 1
+        PUSHB[5] 6 5 4 3 2 1
+        PUSHB[6] 7 6 5 4 3 2 1
+        PUSHB[7] 8 7 6 5 4 3 2 1
+        DEBUG[]
+        IF[]
+        PUSHB[7] 12 12 12 12 12 12 12 12
+        ELSE[]
+        EIF[]
+        ELSE[]
+        PUSHB[1] 17 24
+        NPUSHB[] 5 1 2 3 4 5
+        NPUSHW[] 2 255 255 255 255
+        EIF[]
+
+        PUSHB[0] 1
+        IF[]
+        NPUSHB[] 2 43 43
+        IF[]
+        PUSHB[0] 45
+        ELSE[]
+        PUSHB[0] 14
+        EIF[]
+        ADD[]
+        ELSE[]
+        NPUSHB[] 4 3 2 1 0
+        EIF[]
+        PUSHB[1] 1 3 */
+      (unsigned char []) { 0x40, 1, 0,
+                          0x58,
+                          0xb8, 1, 1,
+                          0xb9, 1, 1, 2, 2,
+                          0xba, 1, 1, 2, 2, 3, 3,
+                          0xbb, 1, 1, 2, 2, 3, 3, 4, 4,
+                          0xbc, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5,
+                          0xbd, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+                          0xbe, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+                          0xbf, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+                          0xb0, 1,
+                          0xb1, 2, 1,
+                          0xb2, 3, 2, 1,
+                          0xb3, 4, 3, 2, 1,
+                          0xb4, 5, 4, 3, 2, 1,
+                          0xb5, 6, 5, 4, 3, 2, 1,
+                          0xb6, 7, 6, 5, 4, 3, 2, 1,
+                          0xb7, 8, 7, 6, 5, 4, 3, 2, 1,
+                          0x4f,
+                          0x58,
+                          0xb7, 12, 12, 12, 12, 12, 12, 12, 12,
+                          0x1b,
+                          0x59,
+                          0x1b,
+                          0xb1, 17, 24,
+                          0x40, 5, 1, 2, 3, 4, 5,
+                          0x41, 2, 255, 255, 255, 255,
+                          0x59,
+                          0xb0, 1,
+                          0x58,
+                          0x40, 2, 43, 43,
+                          0x58,
+                          0xb0, 45,
+                          0x1b,
+                          0xb0, 14,
+                          0x59,
+                          0x60,
+                          0x1b,
+                          0x40, 4, 3, 2, 1, 0,
+                          0x59,
+                          0xb1, 1, 3, },
+      185,
+      &if_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "EIF",
+      /* PUSHB[0] 1
+        IF[]
+        EIF[] */
+      (unsigned char []) { 0xb0, 1,
+                          0x58,
+                          0x59, },
+      3,
+      &eif_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "AND",
+      /* PUSHB[1] 0 1
+        AND[]
+        PUSHB[1] 37 0
+        AND[]
+        PUSHB[1] 40 1
+        AND[]
+        PUSHB[1] 0 0
+        AND[] */
+      (unsigned char []) { 0xb1, 0, 1,
+                          0x5a,
+                          0xb1, 37, 0,
+                          0x5a,
+                          0xb1, 40, 1,
+                          0x5a,
+                          0xb1, 0, 0,
+                          0x5a, },
+      16,
+      &and_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "OR",
+      /* PUSHB[1] 0 1
+        OR[]
+        PUSHB[1] 37 0
+        OR[]
+        PUSHB[1] 40 1
+        OR[]
+        PUSHB[1] 0 0
+        OR[] */
+      (unsigned char []) { 0xb1, 0, 1,
+                          0x5b,
+                          0xb1, 37, 0,
+                          0x5b,
+                          0xb1, 40, 1,
+                          0x5b,
+                          0xb1, 0, 0,
+                          0x5b, },
+      16,
+      &or_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "NOT",
+      /* PUSHB[0] 1
+        NOT[]
+        PUSHB[0] 0
+        NOT[] */
+      (unsigned char []) { 0xb0, 1,
+                          0x5c,
+                          0xb0, 0,
+                          0x5c, },
+      6,
+      &not_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "SDB",
+      /* PUSHB[0] 8
+        SDB[] */
+      (unsigned char []) { 0xb0, 8,
+                          0x5e, },
+      3,
+      NULL,
+      sfnt_check_sdb,
+    },
+    {
+      "SDS",
+      /* PUSHB[0] 1
+        SDS[] */
+      (unsigned char []) { 0xb0, 1,
+                          0x5f, },
+      3,
+      NULL,
+      sfnt_check_sds,
+    },
+    {
+      "that SDS rejects invalid values",
+      /* PUSHB[0] 1,
+        SDS[]
+        PUSHB[0] 7
+        SDS[] */
+      (unsigned char []) { 0xb0, 1,
+                          0x5f,
+                          0xb0, 7,
+                          0x5f, },
+      6,
+      &sds_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "ADD",
+      /* PUSHB[1] 64 32
+        ADD[]
+        PUSHW[1] 255 40 0 215 ;; -216 + 215
+        ADD[] */
+      (unsigned char []) { 0xb1, 64, 32,
+                          0x60,
+                          0xb9, 255, 40, 0, 215,
+                          0x60, },
+      10,
+      &add_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "SUB",
+      /* PUSHB[1] 96 32
+        SUB[]
+        PUSHB[1] 32 96
+        SUB[]
+        PUSHW[1] 0 215 255 40 ;; 215 - -216
+        SUB[] */
+      (unsigned char []) { 0xb1, 96, 32,
+                          0x61,
+                          0xb1, 32, 96,
+                          0x61,
+                          0xb9, 0, 215, 255, 40,
+                          0x61, },
+      14,
+      &sub_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "DIV",
+      /* PUSHB[1] 64 128
+        DIV[] ; 1 / 2 = 0.5
+        PUSHW[1] 0 32 255 224
+        DIV[] ; 0.5 / -0.5 = -1.0
+        PUSHW[1] 255 255 0 0
+        DIV[] ; -1 / 0 = trap */
+      (unsigned char []) { 0xb1, 64, 128,
+                          0x62,
+                          0xb9, 0, 32, 255, 224,
+                          0x62,
+                          0xb9, 255, 255, 0, 0,
+                          0x62, },
+      16,
+      &div_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "MUL",
+      /* PUSHB[1] 255 64
+        MUL[] ; 255 * 1 = 255
+        PUSHW[1] 0 255 255 192
+        MUL[] ; 255 * -1 = -255
+        PUSHW[1] 255 1 255 192
+        MUL[] ; -255 * -1 = 255 */
+      (unsigned char []) { 0xb1, 255, 64,
+                          0x63,
+                          0xb9, 0, 255, 255, 192,
+                          0x63,
+                          0xb9, 255, 1, 255, 192,
+                          0x63, },
+      16,
+      &mul_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "ABS",
+      /* PUSHW[0] 255 255
+        ABS[] ;; abs (-1) == 1
+        PUSHB[0] 1
+        ABS[] ;; abs (1) == 1 */
+      (unsigned char []) { 0xb8, 255, 255,
+                          0x64,
+                          0xb0, 1,
+                          0x64, },
+      7,
+      &abs_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "NEG",
+      /* PUSHW[0] 255 255
+        NEG[] ;; neg (-1) == 1
+        PUSHB[0] 1
+        NEG[] ;; neg (1) == -1 */
+      (unsigned char []) { 0xb8, 255, 255,
+                          0x65,
+                          0xb0, 1,
+                          0x65, },
+      7,
+      &neg_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "FLOOR",
+      /* PUSHW[0] 255 129 ; -127
+        FLOOR[] ; floor (-127) == -128
+        PUSHW[0] 255 193 ; -63
+        FLOOR[] ; floor (-63) == -64
+        PUSHB[0] 63
+        FLOOR[] ; floor (63) == 0
+        PUSHB[0] 127
+        FLOOR[] ; floor (127) == 64
+        PUSHB[0] 191
+        FLOOR[] ; floor (191) == 128 */
+      (unsigned char []) { 0xb8, 255, 129,
+                          0x66,
+                          0xb8, 255, 193,
+                          0x66,
+                          0xb0, 63,
+                          0x66,
+                          0xb0, 127,
+                          0x66,
+                          0xb0, 191,
+                          0x66, },
+      17,
+      &floor_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "CEILING",
+      /* PUSHW[0] 255 128 ; -128
+        CEILING[] ; ceiling (-128) == -128
+        PUSHW[0] 255 127 ; -129
+        CEILING[] ; ceiling (-129) == -128
+        PUSHW[0] 255 191 ; -65
+        CEILING[] ; ceiling (-65) == -64
+        PUSHW[0] 255 255 ; -1
+        CEILING[] ; ceiling (-1) == 0
+        PUSHB[0] 63
+        CEILING[] ; ceiling (63) == 64
+        PUSHB[0] 65
+        CEILING[] ; ceiling (65) == 128
+        PUSHB[0] 128
+        CEILING[] ; ceiling (128) == 128 */
+      (unsigned char []) { 0xb8, 255, 128,
+                          0x67,
+                          0xb8, 255, 127,
+                          0x67,
+                          0xb8, 255, 191,
+                          0x67,
+                          0xb8, 255, 255,
+                          0x67,
+                          0xb0, 63,
+                          0x67,
+                          0xb0, 65,
+                          0x67,
+                          0xb0, 128,
+                          0x67, },
+      25,
+      &ceiling_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "ROUND",
+      /* ROUND[] */
+      (unsigned char []) { 0x68, },
+      1,
+      &round_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "NROUND",
+      /* PUSHB[0] 63
+        NROUND[] */
+      (unsigned char []) { 0xb0, 63,
+                          0x6c, },
+      3,
+      &nround_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "WCVTF",
+      /* PUSHB[1] 1 63
+         WCVTF[]
+         PUSHB[0] 1
+         RCVT[] */
+      (unsigned char []) { 0xb1, 1, 63,
+                          0x70,
+                          0xb0, 1,
+                          0x45, },
+      7,
+      &wcvtf_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "JROT",
+      /* PUSHB[1] 4 0
+        JROT[] ; this should not skip past the next instruction
+        PUSHB[1] 40 40
+        PUSHB[1] 3 1
+        JROT[] ; this should skip past the next instruction
+        PUSHB[0] 4 */
+      (unsigned char []) { 0xb1, 4, 0,
+                          0x78,
+                          0xb1, 40, 40,
+                          0xb1, 3, 1,
+                          0x78,
+                          0xb0, 4, },
+      13,
+      &jrot_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "JROF",
+      /* PUSHB[1] 4 0
+        JROF[] ; this should skip past the next instruction
+        PUSHB[1] 40 40
+        PUSHB[1] 3 1
+        JROF[] ; this should not skip past the next instruction
+        PUSHB[0] 4 */
+      (unsigned char []) { 0xb1, 4, 0,
+                          0x79,
+                          0xb1, 40, 40,
+                          0xb1, 3, 1,
+                          0x79,
+                          0xb0, 4, },
+      13,
+      &jrof_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "DELTAC1",
+      /* PUSHB[0] 2
+        SDB[] ; delta base now 2
+        PUSHB[0] 6
+        SDS[] ; delta shift now 6
+        PUSHB[2] 0xff 5 1 ; CVT index 5, ppem 15 + 2, magnitude 15
+         DELTAC1[]
+        PUSHB[0] 1
+         RCVT[] ; CVT index 5 should now be greater by 8 / 64
+
+         PUSHB[2] 0xef 5 1 ; CVT index 5, ppem 14 + 2, magnitude 15
+         DELTAC1[]
+         PUSHB[0] 1
+         RCVT[] ; CVT index 5 should be unchanged */
+      (unsigned char []) { 0xb0, 2,
+                          0x5e,
+                          0xb0, 6,
+                          0x5f,
+                          0xb2, 255, 5, 1,
+                          0x73,
+                          0xb0, 5,
+                          0x45,
+                          0xb2, 239, 5, 1,
+                          0x73,
+                          0xb0, 5,
+                          0x45, },
+      22,
+      &deltac1_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "DELTAC2",
+      /* PUSHB[0] 2
+        SDB[] ; delta base now 2
+        PUSHB[0] 6
+        SDS[] ; delta shift now 6
+        PUSHB[2] 0xff 5 1 ; CVT index 5, ppem 15 + 2 + 16, magnitude 15
+         DELTAC2[]
+        PUSHB[0] 1
+         RCVT[] ; CVT index 5 should be unchanged
+
+         PUSHB[2] 0xef 5 1 ; CVT index 5, ppem 14 + 2 + 16, magnitude 15
+         DELTAC2[]
+         PUSHB[0] 1
+         RCVT[] ; CVT index 5 should be unchanged */
+      (unsigned char []) { 0xb0, 2,
+                          0x5e,
+                          0xb0, 6,
+                          0x5f,
+                          0xb2, 255, 5, 1,
+                          0x74,
+                          0xb0, 5,
+                          0x45,
+                          0xb2, 239, 5, 1,
+                          0x74,
+                          0xb0, 5,
+                          0x45, },
+      22,
+      &deltac2_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "DELTAC3",
+      /* PUSHB[0] 2
+        SDB[] ; delta base now 2
+        PUSHB[0] 6
+        SDS[] ; delta shift now 6
+        PUSHB[2] 0xff 5 1 ; CVT index 5, ppem 15 + 2 + 32, magnitude 15
+         DELTAC3[]
+        PUSHB[0] 1
+         RCVT[] ; CVT index 5 should be unchanged
+
+         PUSHB[2] 0xef 5 1 ; CVT index 5, ppem 14 + 2 + 32, magnitude 15
+         DELTAC3[]
+         PUSHB[0] 1
+         RCVT[] ; CVT index 5 should be unchanged */
+      (unsigned char []) { 0xb0, 2,
+                          0x5e,
+                          0xb0, 6,
+                          0x5f,
+                          0xb2, 255, 5, 1,
+                          0x75,
+                          0xb0, 5,
+                          0x45,
+                          0xb2, 239, 5, 1,
+                          0x75,
+                          0xb0, 5,
+                          0x45, },
+      22,
+      &deltac3_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "SROUND",
+      sfnt_sround_instructions,
+      ARRAYELTS (sfnt_sround_instructions),
+      &sround_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "S45ROUND",
+      sfnt_s45round_instructions,
+      ARRAYELTS (sfnt_s45round_instructions),
+      &s45round_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "RUTG",
+      /* RUTG[]
+        PUSHB[0] 1
+        ROUND[]
+        PUSHB[0] 64
+        ROUND[]
+        PUSHB[0] 0
+        ROUND[] */
+      (unsigned char []) { 0x7c,
+                          0xb0, 1,
+                          0x68,
+                          0xb0, 64,
+                          0x68,
+                          0xb0, 0,
+                          0x68, },
+      10,
+      &rutg_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "RDTG",
+      /* RUTG[]
+        PUSHB[0] 1
+        ROUND[]
+        PUSHB[0] 63
+        ROUND[]
+        PUSHB[0] 64
+        ROUND[] */
+      (unsigned char []) { 0x7d,
+                          0xb0, 1,
+                          0x68,
+                          0xb0, 63,
+                          0x68,
+                          0xb0, 64,
+                          0x68, },
+      10,
+      &rdtg_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "SANGW",
+      /* PUSHB[0] 3
+        SANGW[] */
+      (unsigned char []) { 0xb0, 3,
+                          0x7e, },
+      3,
+      &sangw_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "AA",
+      /* PUSHB[0] 3
+        AA[] */
+      (unsigned char []) { 0xb0, 3,
+                          0x7f, },
+      3,
+      &aa_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "SCANCTRL",
+      /* PUSHB[0] 1
+        SCANCTRL[] */
+      (unsigned char []) { 0xb0, 1,
+                          0x85, },
+      3,
+      NULL,
+      sfnt_check_scanctrl,
+    },
+    {
+      "GETINFO",
+      /* PUSHB[0] 1
+         GETINFO[]
+         PUSHB[0] 6
+         GETINFO[] */
+      (unsigned char []) { 0xb0, 1,
+                          0x88,
+                          0xb0, 6,
+                          0x88, },
+      6,
+      &getinfo_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "IDEF",
+      /* PUSHB[0] 0x83
+        IDEF[]
+        PUSHB[3] 1 2 3 4
+        POP[]
+        ENDF[]
+        0x83 */
+      (unsigned char []) { 0xb0, 0x83,
+                          0x89,
+                          0xb3, 1, 2, 3, 4,
+                          0x21,
+                          0x2d,
+                          0x83, },
+      11,
+      &idef_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "ROLL",
+      /* PUSHB[4] 1 2 3 4 5
+         ROLL[] ; this should become 1 2 4 5 3 */
+      (unsigned char []) { 0xb4, 1, 2, 3, 4, 5,
+                          0x8a, },
+      7,
+      &roll_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "that ROLL correctly handles underflow",
+      /* PUSHB[1] 1 2
+         ROLL[] */
+      (unsigned char []) { 0xb1, 1, 2,
+                          0x8a, },
+      4,
+      &roll_1_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "MAX",
+      /* PUSHW[1] 0 70 255 186 ; 70, -70
+         MAX[] */
+      (unsigned char []) { 0xb9, 0, 70, 255, 186,
+                          0x8b, },
+      6,
+      &max_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "MIN",
+      /* PUSHW[1] 0 70 255 186 ; 70, -70
+         MIN[] */
+      (unsigned char []) { 0xb9, 0, 70, 255, 186,
+                          0x8c, },
+      6,
+      &min_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "SCANTYPE",
+      /* PUSHB[0] 0
+         SCANTYPE[] */
+      (unsigned char []) { 0xb0, 0,
+                          0x8d, },
+      3,
+      &scantype_test_args,
+      sfnt_generic_check,
+    },
+    {
+      "INSTCTRL",
+      /* PUSHB[1] 1 1
+        INSTCTRL[] ; (1 << 1) should now be set
+         PUSHB[1] 2 1
+        INSTCTRL[] ; (1 << 2) should now be set
+         PUSHB[1] 2 0
+        INSTCTRL[] ; (1 << 2) should no longer be set */
+      (unsigned char []) { 0xb1, 1, 1,
+                          0x8e,
+                          0xb1, 2, 1,
+                          0x8e,
+                          0xb1, 2, 0,
+                          0x8e, },
+      12,
+      NULL,
+      sfnt_check_instctrl,
+    },
+  };
+
+
+
+/* Instruction debugger.  */
+
+static void
+sfnt_setup_debugger (void)
+{
+  XGCValues gcv;
+  Font font;
+
+  display = XOpenDisplay (NULL);
+
+  if (!display)
+    exit (1);
+
+  window = XCreateSimpleWindow (display, DefaultRootWindow (display),
+                               0, 0, 200, 200, 0, 0,
+                               WhitePixel (display,
+                                           DefaultScreen (display)));
+  XMapWindow (display, window);
+
+  /* Select for the appropriate events.  */
+  XSelectInput (display, window, KeyPressMask | ExposureMask);
+
+  /* Find an appropriate font.  */
+  font = XLoadFont (display, "6x13");
+
+  if (!font)
+    exit (1);
+
+  /* The debugger has been set up.  Set up the GCs for drawing points
+     and backgrounds.  */
+
+  gcv.foreground = BlackPixel (display, DefaultScreen (display));
+  gcv.font = font;
+  point_gc = XCreateGC (display, window, GCForeground | GCFont,
+                       &gcv);
+  gcv.foreground = WhitePixel (display, DefaultScreen (display));
+  background_gc = XCreateGC (display, window, GCForeground, &gcv);
+}
+
+static const char *
+sfnt_name_instruction (unsigned char opcode)
+{
+  static const char *const opcode_names[256] = {
+    "7 SVTCA y",
+    "7 SVTCA x",
+    "8 SPvTCA y",
+    "8 SPvTCA x",
+    "8 SFvTCA y",
+    "8 SFvTCA x",
+    "8 SPvTL ||",
+    "7 SPvTL +",
+    "8 SFvTL ||",
+    "7 SFvTL +",
+    "5 SPvFS",
+    "5 SFvFS",
+    "3 GPv",
+    "3 GFv",
+    "6 SFvTPv",
+    "5 ISECT",
+
+    "4 SRP0",
+    "4 SRP1",
+    "4 SRP2",
+    "4 SZP0",
+    "4 SZP1",
+    "4 SZP2",
+    "4 SZPS",
+    "5 SLOOP",
+    "3 RTG",
+    "4 RTHG",
+    "3 SMD",
+    "4 ELSE",
+    "4 JMPR",
+    "6 SCvTCi",
+    "5 SSwCi",
+    "3 SSW",
+
+    "3 DUP",
+    "3 POP",
+    "5 CLEAR",
+    "4 SWAP",
+    "5 DEPTH",
+    "6 CINDEX",
+    "6 MINDEX",
+    "8 AlignPTS",
+    "7 INS_$28",
+    "3 UTP",
+    "8 LOOPCALL",
+    "4 CALL",
+    "4 FDEF",
+    "4 ENDF",
+    "7 MDAP[0]",
+    "7 MDAP[1]",
+
+    "6 IUP[0]",
+    "6 IUP[1]",
+    "6 SHP[0]",
+    "6 SHP[1]",
+    "6 SHC[0]",
+    "6 SHC[1]",
+    "6 SHZ[0]",
+    "6 SHZ[1]",
+    "5 SHPIX",
+    "2 IP",
+    "8 MSIRP[0]",
+    "8 MSIRP[1]",
+    "7 AlignRP",
+    "4 RTDG",
+    "7 MIAP[0]",
+    "7 MIAP[1]",
+
+    "6 NPushB",
+    "6 NPushW",
+    "2 WS",
+    "2 RS",
+    "5 WCvtP",
+    "4 RCvt",
+    "5 GC[0]",
+    "5 GC[1]",
+    "4 SCFS",
+    "5 MD[0]",
+    "5 MD[1]",
+    "5 MPPEM",
+    "3 MPS",
+    "6 FlipON",
+    "7 FlipOFF",
+    "5 DEBUG",
+
+    "2 LT",
+    "4 LTEQ",
+    "2 GT",
+    "4 GTEQ",
+    "2 EQ",
+    "3 NEQ",
+    "3 ODD",
+    "4 EVEN",
+    "2 IF",
+    "3 EIF",
+    "3 AND",
+    "2 OR",
+    "3 NOT",
+    "7 DeltaP1",
+    "3 SDB",
+    "3 SDS",
+
+    "3 ADD",
+    "3 SUB",
+    "3 DIV",
+    "3 MUL",
+    "3 ABS",
+    "3 NEG",
+    "5 FLOOR",
+    "7 CEILING",
+    "8 ROUND[0]",
+    "8 ROUND[1]",
+    "8 ROUND[2]",
+    "8 ROUND[3]",
+    "9 NROUND[0]",
+    "9 NROUND[1]",
+    "9 NROUND[2]",
+    "9 NROUND[3]",
+
+    "5 WCvtF",
+    "7 DeltaP2",
+    "7 DeltaP3",
+    "A DeltaCn[0]",
+    "A DeltaCn[1]",
+    "A DeltaCn[2]",
+    "6 SROUND",
+    "8 S45Round",
+    "4 JROT",
+    "4 JROF",
+    "4 ROFF",
+    "7 INS_$7B",
+    "4 RUTG",
+    "4 RDTG",
+    "5 SANGW",
+    "2 AA",
+
+    "6 FlipPT",
+    "8 FlipRgON",
+    "9 FlipRgOFF",
+    "7 INS_$83",
+    "7 INS_$84",
+    "8 ScanCTRL",
+    "9 SDPvTL[0]",
+    "9 SDPvTL[1]",
+    "7 GetINFO",
+    "4 IDEF",
+    "4 ROLL",
+    "3 MAX",
+    "3 MIN",
+    "8 ScanTYPE",
+    "8 InstCTRL",
+    "7 INS_$8F",
+
+    "7 INS_$90",
+    "7 GXAXIS",
+    "7 INS_$92",
+    "7 INS_$93",
+    "7 INS_$94",
+    "7 INS_$95",
+    "7 INS_$96",
+    "7 INS_$97",
+    "7 INS_$98",
+    "7 INS_$99",
+    "7 INS_$9A",
+    "7 INS_$9B",
+    "7 INS_$9C",
+    "7 INS_$9D",
+    "7 INS_$9E",
+    "7 INS_$9F",
+
+    "7 INS_$A0",
+    "7 INS_$A1",
+    "7 INS_$A2",
+    "7 INS_$A3",
+    "7 INS_$A4",
+    "7 INS_$A5",
+    "7 INS_$A6",
+    "7 INS_$A7",
+    "7 INS_$A8",
+    "7 INS_$A9",
+    "7 INS_$AA",
+    "7 INS_$AB",
+    "7 INS_$AC",
+    "7 INS_$AD",
+    "7 INS_$AE",
+    "7 INS_$AF",
+
+    "8 PushB[0]",
+    "8 PushB[1]",
+    "8 PushB[2]",
+    "8 PushB[3]",
+    "8 PushB[4]",
+    "8 PushB[5]",
+    "8 PushB[6]",
+    "8 PushB[7]",
+    "8 PushW[0]",
+    "8 PushW[1]",
+    "8 PushW[2]",
+    "8 PushW[3]",
+    "8 PushW[4]",
+    "8 PushW[5]",
+    "8 PushW[6]",
+    "8 PushW[7]",
+
+    "7 MDRP[G]",
+    "7 MDRP[B]",
+    "7 MDRP[W]",
+    "7 MDRP[?]",
+    "8 MDRP[rG]",
+    "8 MDRP[rB]",
+    "8 MDRP[rW]",
+    "8 MDRP[r?]",
+    "8 MDRP[mG]",
+    "8 MDRP[mB]",
+    "8 MDRP[mW]",
+    "8 MDRP[m?]",
+    "9 MDRP[mrG]",
+    "9 MDRP[mrB]",
+    "9 MDRP[mrW]",
+    "9 MDRP[mr?]",
+
+    "8 MDRP[pG]",
+    "8 MDRP[pB]",
+    "8 MDRP[pW]",
+    "8 MDRP[p?]",
+    "9 MDRP[prG]",
+    "9 MDRP[prB]",
+    "9 MDRP[prW]",
+    "9 MDRP[pr?]",
+    "9 MDRP[pmG]",
+    "9 MDRP[pmB]",
+    "9 MDRP[pmW]",
+    "9 MDRP[pm?]",
+    "A MDRP[pmrG]",
+    "A MDRP[pmrB]",
+    "A MDRP[pmrW]",
+    "A MDRP[pmr?]",
+
+    "7 MIRP[G]",
+    "7 MIRP[B]",
+    "7 MIRP[W]",
+    "7 MIRP[?]",
+    "8 MIRP[rG]",
+    "8 MIRP[rB]",
+    "8 MIRP[rW]",
+    "8 MIRP[r?]",
+    "8 MIRP[mG]",
+    "8 MIRP[mB]",
+    "8 MIRP[mW]",
+    "8 MIRP[m?]",
+    "9 MIRP[mrG]",
+    "9 MIRP[mrB]",
+    "9 MIRP[mrW]",
+    "9 MIRP[mr?]",
+
+    "8 MIRP[pG]",
+    "8 MIRP[pB]",
+    "8 MIRP[pW]",
+    "8 MIRP[p?]",
+    "9 MIRP[prG]",
+    "9 MIRP[prB]",
+    "9 MIRP[prW]",
+    "9 MIRP[pr?]",
+    "9 MIRP[pmG]",
+    "9 MIRP[pmB]",
+    "9 MIRP[pmW]",
+    "9 MIRP[pm?]",
+    "A MIRP[pmrG]",
+    "A MIRP[pmrB]",
+    "A MIRP[pmrW]",
+    "A MIRP[pmr?]"
+  };
+
+  return opcode_names[opcode];
+}
+
+static void
+sfnt_draw_debugger (struct sfnt_interpreter *interpreter)
+{
+  int x, y, i;
+  char buffer[80];
+  const char *name;
+  int opcode;
+
+  sprintf (buffer, "opcode:IP:depth: 0x%x:%d:%d",
+          interpreter->instructions[interpreter->IP],
+          interpreter->IP,
+          interpreter->call_depth);
+
+  /* Clear the window.  */
+  XFillRectangle (display, window, background_gc,
+                 0, 0, 65535, 65535);
+
+  /* Draw some information about the opcode.  */
+  XDrawString (display, window, point_gc, 0, 13, buffer,
+              strlen (buffer));
+
+  opcode = interpreter->instructions[interpreter->IP];
+
+  sprintf (buffer, "opcode: %s",
+          sfnt_name_instruction (opcode));
+
+  XDrawString (display, window, point_gc, 14, 27, buffer,
+              strlen (buffer));
+
+  if (interpreter->state.project
+      == sfnt_project_onto_x_axis_vector)
+    name = "X axis";
+  else if (interpreter->state.project
+          == sfnt_project_onto_y_axis_vector)
+    name = "Y axis";
+  else
+    name = "Any";
+
+  sprintf (buffer, "projection function: %s", name);
+
+  XDrawString (display, window, point_gc, 28, 42, buffer,
+              strlen (buffer));
+
+  /* Draw each point onto the window.  */
+  for (i = 0; i < interpreter->glyph_zone->num_points; ++i)
+    {
+      x = interpreter->glyph_zone->x_current[i] / 16;
+      y = (200 - interpreter->glyph_zone->y_current[i] / 16);
+
+      XFillRectangle (display, window, point_gc, x, y, 4, 4);
+    }
+}
+
+static void
+sfnt_run_hook (struct sfnt_interpreter *interpreter)
+{
+  pid_t pid;
+  XEvent event;
+
+#ifdef TEST_BREAK_AFTER
+  static unsigned int instructions;
+
+  if (++instructions < TEST_BREAK_AFTER)
+    return;
+#endif
+
+  pid = fork ();
+
+  if (pid == 0)
+    {
+      sfnt_setup_debugger ();
+
+      while (true)
+       {
+         XNextEvent (display, &event);
+
+         switch (event.type)
+           {
+           case KeyPress:
+             XDestroyWindow (display, window);
+             XCloseDisplay (display);
+             exit (0);
+             break;
+
+           case Expose:
+             sfnt_draw_debugger (interpreter);
+             break;
+           }
+       }
+    }
+  else
+    {
+      while (waitpid (pid, NULL, 0) != pid && errno == EINTR)
+       /* Spin.  */;
+    }
+}
+
+static struct sfnt_prep_table *exec_prep;
+static struct sfnt_fpgm_table *exec_fpgm;
+
+static const char *
+sfnt_identify_instruction (struct sfnt_interpreter *interpreter)
+{
+  static char buffer[256];
+  unsigned char *where;
+
+  where = interpreter->instructions + interpreter->IP;
+
+  if (exec_prep
+      && where >= exec_prep->instructions
+      && where < (exec_prep->instructions
+                 + exec_prep->num_instructions))
+    {
+      sprintf (buffer, "prep+%td",
+              where - exec_prep->instructions);
+      return buffer;
+    }
+
+  if (exec_fpgm->instructions
+      && where >= exec_fpgm->instructions
+      && where < (exec_fpgm->instructions
+                 + exec_fpgm->num_instructions))
+    {
+      sprintf (buffer, "fpgm+%td",
+              where - exec_fpgm->instructions);
+      return buffer;
+    }
+
+  sprintf (buffer, "IP+%td", where - interpreter->instructions);
+  return buffer;
+}
+
+static void
+sfnt_verbose (struct sfnt_interpreter *interpreter)
+{
+  struct sfnt_instructed_outline temp;
+  struct sfnt_glyph_outline *outline;
+  struct sfnt_raster *raster;
+  unsigned char opcode;
+  const char *name;
+  static unsigned int instructions;
+
+  /* Build a temporary outline containing the values of the
+     interpreter's glyph zone.  */
+
+  if (interpreter->glyph_zone)
+    {
+      temp.num_points = interpreter->glyph_zone->num_points;
+      temp.num_contours = interpreter->glyph_zone->num_contours;
+      temp.contour_end_points = interpreter->glyph_zone->contour_end_points;
+      temp.x_points = interpreter->glyph_zone->x_current;
+      temp.y_points = interpreter->glyph_zone->y_current;
+      temp.flags = interpreter->glyph_zone->flags;
+
+      outline = sfnt_build_instructed_outline (&temp);
+
+      if (!outline)
+       return;
+
+      printf ("outline bounds: %g %g, %g %g\n",
+             sfnt_coerce_fixed (outline->xmin),
+             sfnt_coerce_fixed (outline->ymin),
+             sfnt_coerce_fixed (outline->xmax),
+             sfnt_coerce_fixed (outline->ymax));
+
+      raster = sfnt_raster_glyph_outline (outline);
+
+      if (raster)
+       sfnt_test_raster (raster, NULL, 0);
+
+      xfree (outline);
+      xfree (raster);
+    }
+
+  opcode = interpreter->instructions[interpreter->IP];
+  printf ("opcode, number of instructions: %s %u\n",
+         sfnt_name_instruction (opcode), instructions++);
+  printf ("instruction: %s\n",
+         sfnt_identify_instruction (interpreter));
+
+  if (interpreter->state.project
+      == sfnt_project_onto_x_axis_vector)
+    name = "X axis";
+  else if (interpreter->state.project
+          == sfnt_project_onto_y_axis_vector)
+    name = "Y axis";
+  else
+    name = "Any";
+
+  printf ("projection function: %s\n", name);
+
+  printf ("proj and free vecs: %d %d %d %d\n",
+         interpreter->state.projection_vector.x,
+         interpreter->state.projection_vector.y,
+         interpreter->state.freedom_vector.x,
+         interpreter->state.freedom_vector.y);
+}
+
+static void
+sfnt_push_hook (struct sfnt_interpreter *interpreter,
+               uint32_t value)
+{
+  int32_t alternate;
+
+  alternate = value;
+
+  fprintf (stderr, "--> %"PRIi32"\n", alternate);
+}
+
+static void
+sfnt_pop_hook (struct sfnt_interpreter *interpreter,
+              uint32_t value)
+{
+  int32_t alternate;
+
+  alternate = value;
+
+  fprintf (stderr, "<<- %"PRIi32"\n", alternate);
+}
+
+
+
+static void
+sfnt_test_uvs (int fd, struct sfnt_cmap_format_14 *format14)
+{
+  struct sfnt_uvs_context *context;
+  size_t i, j;
+  sfnt_glyph glyph;
+  sfnt_char c;
+  struct sfnt_nondefault_uvs_table *uvs;
+
+  context = sfnt_create_uvs_context (format14, fd);
+
+  /* Print each variation selector and its associated ranges.  */
+
+  if (!context)
+    fprintf (stderr, "failed to read uvs data\n");
+  else
+    {
+      fprintf (stderr, "UVS context with %zu records and %zu tables\n",
+              context->num_records, context->nmemb);
+
+      for (i = 0; i < context->num_records; ++i)
+       {
+         if (!context->records[i].nondefault_uvs)
+           continue;
+
+         uvs = context->records[i].nondefault_uvs;
+
+         for (j = 0; j < uvs->num_uvs_mappings; ++j)
+           {
+             c = uvs->mappings[j].unicode_value;
+             glyph = sfnt_variation_glyph_for_char (uvs, c);
+
+             if (glyph != uvs->mappings[j].base_character_value)
+               abort ();
+
+             fprintf (stderr, "   UVS: %"PRIx32" (%"PRIx32") -> %"PRIu32"\n",
+                      c, context->records[i].selector, glyph);
+           }
+       }
+
+      sfnt_free_uvs_context (context);
+    }
+}
+
+
+
+/* Main entry point.  */
+
+/* Simple tests that were used while developing this file.  By the
+   time you are reading this, they probably no longer work.
+
+   Compile like so in this directory:
+
+    gcc -Demacs -I. -I. -I../lib -I../lib -MMD -MF deps/.d -MP
+    -fno-common -Wall -Warith-conversion -Wdate-time
+    -Wdisabled-optimization -Wdouble-promotion -Wduplicated-cond
+    -Wextra -Wformat-signedness -Winit-self -Winvalid-pch -Wlogical-op
+    -Wmissing-declarations -Wmissing-include-dirs -Wmissing-prototypes
+    -Wnested-externs -Wnull-dereference -Wold-style-definition
+    -Wopenmp-simd -Wpacked -Wpointer-arith -Wstrict-prototypes
+    -Wsuggest-attribute=format -Wsuggest-final-methods
+    -Wsuggest-final-types -Wtrampolines -Wuninitialized
+    -Wunknown-pragmas -Wunused-macros -Wvariadic-macros
+    -Wvector-operation-performance -Wwrite-strings -Warray-bounds=2
+    -Wattribute-alias=2 -Wformat=2 -Wformat-truncation=2
+    -Wimplicit-fallthrough=5 -Wshift-overflow=2 -Wuse-after-free=3
+    -Wvla-larger-than=4031 -Wredundant-decls
+    -Wno-missing-field-initializers -Wno-override-init
+    -Wno-sign-compare -Wno-type-limits -Wno-unused-parameter
+    -Wno-format-nonliteral -Wno-bidi-chars -g3 -O0 -DTEST sfnt.c -o
+    sfnt ../lib/libgnu.a -lX11 -lXrender
+
+   after gnulib has been built.  Then, run ./sfnt
+   /path/to/font.ttf.  */
+
+int
+main (int argc, char **argv)
+{
+  struct sfnt_offset_subtable *font;
+  struct sfnt_cmap_encoding_subtable *subtables;
+  struct sfnt_cmap_encoding_subtable_data **data;
+  struct sfnt_cmap_table *table;
+  int fd, i, j;
+  sfnt_char character;
+  struct sfnt_head_table *head;
+  struct sfnt_hhea_table *hhea;
+  struct sfnt_loca_table_short *loca_short;
+  struct sfnt_loca_table_long *loca_long;
+  struct sfnt_glyf_table *glyf;
+  struct sfnt_glyph *glyph;
+  sfnt_glyph code;
+  struct sfnt_test_dcontext dcontext;
+  struct sfnt_glyph_outline *outline;
+  struct timespec start, end, sub, sub1, sub2, sub3;
+  static struct sfnt_maxp_table *maxp;
+  struct sfnt_raster *raster;
+  struct sfnt_hmtx_table *hmtx;
+  struct sfnt_glyph_metrics metrics;
+  struct sfnt_name_table *name;
+  unsigned char *string;
+  struct sfnt_name_record record;
+  struct sfnt_meta_table *meta;
+  struct sfnt_ttc_header *ttc;
+  struct sfnt_interpreter *interpreter;
+  struct sfnt_cvt_table *cvt;
+  struct sfnt_fpgm_table *fpgm;
+  const char *trap;
+  struct sfnt_prep_table *prep;
+  struct sfnt_graphics_state state;
+  struct sfnt_instructed_outline *value;
+  struct sfnt_fvar_table *fvar;
+  struct sfnt_gvar_table *gvar;
+  struct sfnt_avar_table *avar;
+  struct sfnt_cvar_table *cvar;
+  sfnt_fixed scale;
+  char *fancy;
+  int *advances;
+  struct sfnt_raster **rasters;
+  size_t length;
+  char *axis_name;
+  struct sfnt_instance *instance;
+  struct sfnt_blend blend;
+  struct sfnt_metrics_distortion distortion;
+
+  if (argc < 2)
+    return 1;
+
+  instance = NULL;
+
+  if (!strcmp (argv[1], "--check-interpreter"))
+    {
+      interpreter = sfnt_make_test_interpreter ();
+
+      if (!interpreter)
+       abort ();
+
+      for (i = 0; i < ARRAYELTS (all_tests); ++i)
+       sfnt_run_interpreter_test (&all_tests[i], interpreter);
+
+      exit (0);
+    }
+
+  fd = open (argv[1], O_RDONLY);
+
+  if (fd < 1)
+    return 1;
+
+  ttc = NULL;
+
+  font = sfnt_read_table_directory (fd);
+
+  if (font == (struct sfnt_offset_subtable *) -1)
+    {
+      if (lseek (fd, 0, SEEK_SET) != 0)
+       return 1;
+
+      ttc = sfnt_read_ttc_header (fd);
+
+      if (!ttc)
+       return 1;
+
+      fprintf (stderr, "TrueType collection: %"PRIu32" fonts installed\n",
+              ttc->num_fonts);
+      fflush (stderr);
+
+      printf ("Which font? ");
+      if (scanf ("%d", &i) == EOF)
+       return 1;
+
+      if (i >= ttc->num_fonts || i < 0)
+       {
+         printf ("out of range\n");
+         return 1;
+       }
+
+      if (lseek (fd, ttc->offset_table[i], SEEK_SET)
+         != ttc->offset_table[i])
+       return 1;
+
+      font = sfnt_read_table_directory (fd);
+    }
+
+  if (!font || font == (struct sfnt_offset_subtable *) -1)
+    {
+      close (fd);
+      return 1;
+    }
+
+  for (i = 0; i < font->num_tables; ++i)
+    fprintf (stderr, "Found new subtable with tag %"PRIx32
+            " at offset %"PRIu32"\n",
+            font->subtables[i].tag,
+            font->subtables[i].offset);
+
+  table = sfnt_read_cmap_table (fd, font, &subtables, &data);
+
+  if (!table)
+    {
+      close (fd);
+      xfree (font);
+      return 1;
+    }
+
+  fprintf (stderr, "number of subtables: %"PRIu16"\n",
+          table->num_subtables);
+
+  for (i = 0; i < table->num_subtables; ++i)
+    {
+      fprintf (stderr, "Found cmap table %"PRIu32": %p\n",
+              subtables[i].offset, (void *) data[i]);
+
+      if (data[i])
+       fprintf (stderr, "  format: %"PRIu16"\n",
+                data[i]->format);
+    }
+
+  if (argc >= 3 && !strcmp (argv[2], "--check-variation-selectors"))
+    {
+      /* Look for a format 14 cmap table.  */
+
+      for (i = 0; i < table->num_subtables; ++i)
+       {
+         if (data[i]->format == 14)
+           {
+             fprintf (stderr, "format 14 subtable found\n");
+             sfnt_test_uvs (fd, (struct sfnt_cmap_format_14 *) data[i]);
+             return 0;
+           }
+       }
+
+      return 1;
+    }
+
+#define FANCY_PPEM 12
+#define EASY_PPEM  12
+
+  interpreter = NULL;
+  head = sfnt_read_head_table (fd, font);
+  hhea = sfnt_read_hhea_table (fd, font);
+  glyf = sfnt_read_glyf_table (fd, font);
+  maxp = sfnt_read_maxp_table (fd, font);
+  name = sfnt_read_name_table (fd, font);
+  meta = sfnt_read_meta_table (fd, font);
+  cvt  = sfnt_read_cvt_table (fd, font);
+  fpgm = sfnt_read_fpgm_table (fd, font);
+  prep = sfnt_read_prep_table (fd, font);
+  fvar = sfnt_read_fvar_table (fd, font);
+  gvar = sfnt_read_gvar_table (fd, font);
+  avar = sfnt_read_avar_table (fd, font);
+  cvar = NULL;
+  hmtx = NULL;
+
+  if (fvar && cvt)
+    cvar = sfnt_read_cvar_table (fd, font, fvar, cvt);
+
+  if (cvar)
+    fprintf (stderr, "cvar table found\n");
+
+  exec_prep = prep;
+  exec_fpgm = fpgm;
+  fancy = getenv ("SFNT_FANCY_TEST");
+
+  loca_long = NULL;
+  loca_short = NULL;
+
+  if (fvar)
+    {
+      fprintf (stderr, "FVAR table found!\n"
+              "version: %"PRIu16".%"PRIu16"\n"
+              "axis_count: %"PRIu16"\n"
+              "axis_size: %"PRIu16"\n"
+              "instance_count: %"PRIu16"\n"
+              "instance_size: %"PRIu16"\n",
+              fvar->major_version,
+              fvar->minor_version,
+              fvar->axis_count,
+              fvar->axis_size,
+              fvar->instance_count,
+              fvar->instance_size);
+
+      for (i = 0; i < fvar->axis_count; ++i)
+       {
+         if (name)
+           {
+             axis_name
+               = (char *) sfnt_find_name (name, fvar->axis[i].name_id,
+                                          &record);
+
+             if (axis_name)
+               fprintf (stderr, "axis no: %d; name: %.*s\n",
+                        i, record.length, axis_name);
+           }
+
+         fprintf (stderr, "  axis: %"PRIx32" %g %g %g\n",
+                  fvar->axis[i].axis_tag,
+                  sfnt_coerce_fixed (fvar->axis[i].min_value),
+                  sfnt_coerce_fixed (fvar->axis[i].default_value),
+                  sfnt_coerce_fixed (fvar->axis[i].max_value));
+       }
+
+      for (i = 0; i < fvar->instance_count; ++i)
+       {
+         if (name)
+           {
+             axis_name
+               = (char *) sfnt_find_name (name, fvar->instance[i].name_id,
+                                          &record);
+
+             if (axis_name)
+               fprintf (stderr, "instance no: %d; name: %.*s\n",
+                        i, record.length, axis_name);
+           }
+       }
+
+      if (fvar->instance_count > 1)
+       {
+         printf ("instance? ");
+
+         if (scanf ("%d", &i) == EOF)
+           goto free_lab;
+
+         if (i >= fvar->instance_count)
+           goto free_lab;
+
+         if (i >= 0)
+           instance = &fvar->instance[i];
+       }
+    }
+
+  if (gvar)
+    fprintf (stderr, "gvar table found\n");
+
+  if (avar)
+    {
+      fprintf (stderr, "avar table found\n");
+
+      for (i = 0; i < avar->axis_count; ++i)
+       {
+         fprintf (stderr, "axis: %d, %"PRIu16" pairs\n",
+                  i, avar->segments[i].pair_count);
+
+         for (j = 0; j < avar->segments[i].pair_count; ++j)
+           fprintf (stderr, "pair: %g, %g\n",
+                    (avar->segments[i].correspondence[j].from_coord
+                     / 16384.0),
+                    (avar->segments[i].correspondence[j].to_coord
+                     / 16384.0));
+       }
+    }
+
+  memset (&blend, 0, sizeof blend);
+
+  if (instance && gvar)
+    {
+      sfnt_init_blend (&blend, fvar, gvar, avar,
+                      cvar);
+
+      for (i = 0; i < fvar->axis_count; ++i)
+       blend.coords[i] = instance->coords[i];
+
+      sfnt_normalize_blend (&blend);
+    }
+
+  if (fancy)
+    {
+      length = strlen (fancy);
+      scale = sfnt_div_fixed (FANCY_PPEM, head->units_per_em);
+
+      if (hhea && maxp)
+       hmtx = sfnt_read_hmtx_table (fd, font, hhea, maxp);
+
+      if (!maxp || !head || !prep || !hmtx || !hhea
+         || table->num_subtables < 1)
+       exit (1);
+
+      if (head->index_to_loc_format)
+       {
+         loca_long = sfnt_read_loca_table_long (fd, font);
+         if (!loca_long)
+           return 1;
+
+         fprintf (stderr, "long loca table has %zu glyphs\n",
+                  loca_long->num_offsets);
+       }
+      else
+       {
+         loca_short = sfnt_read_loca_table_short (fd, font);
+         if (!loca_short)
+           return 1;
+
+         fprintf (stderr, "short loca table has %zu glyphs\n",
+                  loca_short->num_offsets);
+       }
+
+      interpreter = sfnt_make_interpreter (maxp, cvt, head, fvar,
+                                          FANCY_PPEM, FANCY_PPEM);
+      if (instance && gvar)
+       sfnt_vary_interpreter (interpreter, &blend);
+
+      if (!interpreter)
+       exit (1);
+
+      if (fpgm)
+       {
+         fprintf (stderr, "interpreting the font program, with"
+                  " %zu instructions\n", fpgm->num_instructions);
+         trap = sfnt_interpret_font_program (interpreter, fpgm);
+
+         if (trap)
+           fprintf (stderr, "**TRAP**: %s\n", trap);
+       }
+
+      if (prep)
+       {
+         fprintf (stderr, "interpreting the control value program, with"
+                  " %zu instructions\n", prep->num_instructions);
+         trap = sfnt_interpret_control_value_program (interpreter, prep,
+                                                      &state);
+
+         if (trap)
+           fprintf (stderr, "**TRAP**: %s\n", trap);
+       }
+
+      state = interpreter->state;
+
+      advances = alloca (sizeof *advances * length);
+      rasters = alloca (sizeof *rasters * length);
+
+      for (i = 0; i < length; ++i)
+       {
+         code = sfnt_lookup_glyph (fancy[i], data[0]);
+
+         if (!code)
+           exit (2);
+
+         glyph = sfnt_read_glyph (code, glyf, loca_short,
+                                  loca_long);
+
+         if (!glyph || !glyph->simple)
+           exit (3);
+
+         if (instance && gvar)
+           sfnt_vary_simple_glyph (&blend, code, glyph,
+                                   &distortion);
+
+         if (sfnt_lookup_glyph_metrics (code, -1,
+                                        &metrics,
+                                        hmtx, hhea,
+                                        head, maxp))
+           exit (4);
+
+         interpreter->state = state;
+         trap = sfnt_interpret_simple_glyph (glyph, interpreter,
+                                             &metrics, &value);
+
+         if (trap)
+           {
+             fprintf (stderr, "*TRAP*: %s\n", trap);
+             exit (5);
+           }
+
+         outline = sfnt_build_instructed_outline (value);
+
+         if (!outline)
+           exit (6);
+
+         xfree (value);
+
+         raster = sfnt_raster_glyph_outline (outline);
+
+         if (!raster)
+           exit (7);
+
+         xfree (outline);
+
+         rasters[i] = raster;
+         advances[i] = (sfnt_mul_fixed (metrics.advance, scale)
+                        + sfnt_mul_fixed (distortion.advance, scale));
+       }
+
+      sfnt_x_raster (rasters, advances, length, hhea, scale);
+      exit (0);
+    }
+
+  if (hhea && maxp)
+    hmtx = sfnt_read_hmtx_table (fd, font, hhea, maxp);
+
+  if (maxp)
+    fprintf (stderr, "maxp says num glyphs is %"PRIu16"\n",
+            maxp->num_glyphs);
+
+  if (name)
+    {
+      fprintf (stderr, "name table of format: %"PRIu16" count: %"
+              PRIu16"\n", name->format, name->count);
+
+      string = sfnt_find_name (name, SFNT_NAME_FONT_FAMILY,
+                              &record);
+
+      if (string)
+       fprintf (stderr, "FONT_FAMILY: %"PRIu16", %"PRIu16"\n",
+                record.platform_id, record.length);
+    }
+
+  if (meta)
+    {
+      fprintf (stderr, "meta table with count: %"PRIu32"\n",
+              meta->num_data_maps);
+
+      for (i = 0; i < meta->num_data_maps; ++i)
+       fprintf (stderr, "  meta tag: %"PRIx32"\n",
+                meta->data_maps[i].tag);
+    }
+
+  loca_long = NULL;
+  loca_short = NULL;
+
+  if (head)
+    {
+      fprintf (stderr, "HEAD table:\n"
+              "version: \t\t\t%g\n"
+              "revision: \t\t\t%g\n"
+              "checksum_adjustment: \t\t%"PRIu32"\n"
+              "magic: \t\t\t\t%"PRIx32"\n"
+              "flags: \t\t\t\t%"PRIx16"\n"
+              "units_per_em: \t\t\t%"PRIu16"\n"
+              "xmin, ymin, xmax, ymax: \t%d, %d, %d, %d\n"
+              "mac_style: \t\t\t%"PRIx16"\n"
+              "lowest_rec_ppem: \t\t%"PRIu16"\n"
+              "font_direction_hint: \t\t%"PRIi16"\n"
+              "index_to_loc_format: \t\t%"PRIi16"\n"
+              "glyph_data_format: \t\t%"PRIi16"\n",
+              sfnt_coerce_fixed (head->version),
+              sfnt_coerce_fixed (head->revision),
+              head->checksum_adjustment,
+              head->magic,
+              head->flags,
+              head->units_per_em,
+              (int) head->xmin,
+              (int) head->ymin,
+              (int) head->xmax,
+              (int) head->ymax,
+              head->mac_style,
+              head->lowest_rec_ppem,
+              head->font_direction_hint,
+              head->index_to_loc_format,
+              head->glyph_data_format);
+
+      if (head->index_to_loc_format)
+       {
+         loca_long = sfnt_read_loca_table_long (fd, font);
+         if (!loca_long)
+           return 1;
+
+         fprintf (stderr, "long loca table has %zu glyphs\n",
+                  loca_long->num_offsets);
+       }
+      else
+       {
+         loca_short = sfnt_read_loca_table_short (fd, font);
+         if (!loca_short)
+           return 1;
+
+         fprintf (stderr, "short loca table has %zu glyphs\n",
+                  loca_short->num_offsets);
+       }
+    }
+
+  if (hhea)
+    fprintf (stderr, "HHEA table:\n"
+            "version: \t\t\t%g\n"
+            "ascent, descent: \t\t%d %d\n"
+            "line_gap: \t\t\t%d\n"
+            "advance_width_max: \t\t%u\n"
+            "min_lsb: \t\t\t%d\n"
+            "min_rsb: \t\t\t%d\n"
+            "caret_srise: \t\t\t%d\n"
+            "caret_srun: \t\t\t%d\n",
+            sfnt_coerce_fixed (hhea->version),
+            (int) hhea->ascent,
+            (int) hhea->descent,
+            (int) hhea->line_gap,
+            (unsigned int) hhea->advance_width_max,
+            (int) hhea->min_left_side_bearing,
+            (int) hhea->min_right_side_bearing,
+            (int) hhea->caret_slope_rise,
+            (int) hhea->caret_slope_run);
+
+  if (head && maxp && maxp->version >= 0x00010000)
+    {
+      fprintf (stderr, "creating interpreter\n"
+              "the size of the stack is %"PRIu16"\n"
+              "the size of the twilight zone is %"PRIu16"\n"
+              "the size of the storage area is %"PRIu16"\n"
+              "there are at most %"PRIu16" idefs\n"
+              "there are at most %"PRIu16" fdefs\n"
+              "the cvt is %zu fwords in length\n",
+              maxp->max_stack_elements,
+              maxp->max_twilight_points,
+              maxp->max_storage,
+              maxp->max_instruction_defs,
+              maxp->max_function_defs,
+              cvt ? cvt->num_elements : 0ul);
+
+      interpreter = sfnt_make_interpreter (maxp, cvt, head,
+                                          fvar, FANCY_PPEM,
+                                          FANCY_PPEM);
+      state = interpreter->state;
+
+      if (instance && gvar)
+       sfnt_vary_interpreter (interpreter, &blend);
+
+      if (fpgm)
+       {
+         fprintf (stderr, "interpreting the font program, with"
+                  " %zu instructions\n", fpgm->num_instructions);
+
+         trap = sfnt_interpret_font_program (interpreter, fpgm);
+
+         if (trap)
+           fprintf (stderr, "**TRAP**: %s\n", trap);
+       }
+
+      if (prep)
+       {
+         fprintf (stderr, "interpreting the control value program, with"
+                  " %zu instructions\n", prep->num_instructions);
+
+         trap = sfnt_interpret_control_value_program (interpreter, prep,
+                                                      &state);
+
+         if (trap)
+           fprintf (stderr, "**TRAP**: %s\n", trap);
+       }
+    }
+
+  while (true)
+    {
+      printf ("table, character? ");
+
+      if (scanf ("%d %"SCNu32"", &i, &character) == EOF)
+       break;
+
+      if (i < 0 || i >= table->num_subtables)
+       {
+         printf ("table out of range\n");
+         continue;
+       }
+
+      if (!data[i])
+       {
+         printf ("table not present\n");
+         continue;
+       }
+
+      code = sfnt_lookup_glyph (character, data[i]);
+      printf ("glyph is %"PRIu32"\n", code);
+
+      if ((loca_long || loca_short) && glyf)
+       {
+         scale = sfnt_div_fixed (EASY_PPEM, head->units_per_em);
+         glyph = sfnt_read_glyph (code, glyf, loca_short,
+                                  loca_long);
+
+         if (glyph)
+           {
+             printf ("glyph is: %s\n",
+                     glyph->simple ? "simple" : "compound");
+
+             dcontext.glyf = glyf;
+             dcontext.loca_short = loca_short;
+             dcontext.loca_long = loca_long;
+
+             if (instance && gvar)
+               dcontext.blend = &blend;
+             else
+               dcontext.blend = NULL;
+
+             if (glyph->simple && instance && gvar)
+               {
+                 printf ("applying variations to simple glyph...\n");
+
+                 clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
+                 if (sfnt_vary_simple_glyph (&blend, code, glyph,
+                                             &distortion))
+                   printf ("variation failed!\n");
+                 clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
+                 sub = timespec_sub (end, start);
+
+                 printf ("time spent varying: %lld sec %ld nsec\n",
+                         (long long) sub.tv_sec, sub.tv_nsec);
+                 printf ("distortions: %"PRIi16", %"PRIi16"\n",
+                         distortion.origin, distortion.advance);
+               }
+             else if (instance && gvar)
+               {
+                 printf ("applying variations to compound glyph...\n");
+
+                 if (sfnt_vary_compound_glyph (&blend, code, glyph,
+                                               &distortion))
+                   printf ("variation failed!\n");
+               }
+
+             if (sfnt_decompose_glyph (glyph, sfnt_test_move_to,
+                                       sfnt_test_line_to,
+                                       sfnt_test_curve_to,
+                                       sfnt_test_get_glyph,
+                                       sfnt_test_free_glyph,
+                                       &dcontext))
+               printf ("decomposition failure\n");
+
+             if (sfnt_lookup_glyph_metrics (code, -1,
+                                            &metrics,
+                                            hmtx, hhea,
+                                            head, maxp))
+               {
+                 printf ("metrics lookup failure");
+                 memset (&metrics, 0, sizeof metrics);
+               }
+
+             /* Time this important bit.  */
+             clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
+             outline = sfnt_build_glyph_outline (glyph, scale,
+                                                 &metrics,
+                                                 sfnt_test_get_glyph,
+                                                 sfnt_test_free_glyph,
+                                                 &dcontext);
+
+             clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
+             sub = timespec_sub (end, start);
+             memset (&sub1, 0, sizeof sub1);
+
+             if (outline)
+               {
+                 fprintf (stderr, "outline origin, rbearing: %"
+                          PRIi32" %"PRIi32"\n",
+                          outline->origin,
+                          outline->ymax - outline->origin);
+                 sfnt_test_max = outline->ymax - outline->ymin;
+
+                 for (i = 0; i < outline->outline_used; i++)
+                   printf ("ctx.%s (%g, %g) /* %g, %g */\n",
+                           ((outline->outline[i].flags
+                             & SFNT_GLYPH_OUTLINE_LINETO)
+                            ? "lineTo" : "moveTo"),
+                           sfnt_coerce_fixed (outline->outline[i].x
+                                              - outline->xmin),
+                           sfnt_coerce_fixed (sfnt_test_max
+                                              - (outline->outline[i].y
+                                                 - outline->ymin)),
+                           sfnt_coerce_fixed (outline->outline[i].x
+                                              - outline->xmin),
+                           sfnt_coerce_fixed (outline->outline[i].y
+                                              - outline->ymin));
+
+                 clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
+                 sfnt_build_outline_edges (outline, sfnt_test_edge_ignore,
+                                           NULL);
+                 clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
+                 sub1 = timespec_sub (end, start);
+
+                 sfnt_build_outline_edges (outline, sfnt_test_edge,
+                                           NULL);
+
+                 raster = NULL;
+
+                 clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
+
+                 for (i = 0; i < 120; ++i)
+                   {
+                     xfree (raster);
+                     raster = sfnt_raster_glyph_outline (outline);
+                   }
+
+                 clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
+                 sub2 = timespec_sub (end, start);
+
+                 /* Print out the raster.  */
+                 sfnt_test_raster (raster, hhea, scale);
+                 printf ("raster offsets: %d, %d\n",
+                         raster->offx, raster->offy);
+
+                 xfree (raster);
+
+                 printf ("outline bounds: %g %g, %g %g\n",
+                         sfnt_coerce_fixed (outline->xmin),
+                         sfnt_coerce_fixed (outline->ymin),
+                         sfnt_coerce_fixed (outline->xmax),
+                         sfnt_coerce_fixed (outline->ymax));
+               }
+
+             if (hmtx && head)
+               {
+                 if (!sfnt_lookup_glyph_metrics (code, EASY_PPEM,
+                                                 &metrics,
+                                                 hmtx, hhea,
+                                                 head, maxp))
+                   printf ("lbearing, advance: %g, %g\n",
+                           sfnt_coerce_fixed (metrics.lbearing),
+                           sfnt_coerce_fixed (metrics.advance));
+
+                 if (interpreter)
+                   {
+                     if (getenv ("SFNT_DEBUG"))
+                       interpreter->run_hook = sfnt_run_hook;
+                     else if (getenv ("SFNT_VERBOSE"))
+                       {
+                         interpreter->run_hook = sfnt_verbose;
+                         interpreter->push_hook = sfnt_push_hook;
+                         interpreter->pop_hook = sfnt_pop_hook;
+                       }
+
+                     if (!sfnt_lookup_glyph_metrics (code, -1,
+                                                     &metrics,
+                                                     hmtx, hhea,
+                                                     head, maxp))
+                       {
+                         printf ("interpreting glyph\n");
+                         interpreter->state = state;
+                         clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
+                         if (glyph->simple)
+                           trap
+                             = sfnt_interpret_simple_glyph (glyph,
+                                                            interpreter,
+                                                            &metrics,
+                                                            &value);
+                         else
+#define GG sfnt_test_get_glyph
+#define FG sfnt_test_free_glyph
+                           trap
+                             = sfnt_interpret_compound_glyph (glyph,
+                                                              interpreter,
+                                                              &state,
+                                                              GG, FG,
+                                                              hmtx, hhea,
+                                                              maxp,
+                                                              &metrics,
+                                                              &dcontext,
+                                                              &value);
+#undef GG
+#undef FG
+                         clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
+                         sub3 = timespec_sub (end, start);
+
+                         if (trap)
+                           printf ("**TRAP**: %s\n", trap);
+                         else
+                           {
+                             printf ("rasterizing instructed outline\n");
+                             if (outline)
+                               xfree (outline);
+                             outline = sfnt_build_instructed_outline (value);
+                             xfree (value);
+
+                             if (outline)
+                               {
+                                 raster = sfnt_raster_glyph_outline (outline);
+
+                                 if (raster)
+                                   {
+                                     sfnt_test_raster (raster, hhea, scale);
+                                     printf ("raster offsets: %d, %d\n",
+                                             raster->offx, raster->offy);
+                                     xfree (raster);
+                                   }
+                               }
+                           }
+
+                         fprintf (stderr, "execution time: %lld sec %ld nse"
+                                  "c\n",
+                                  (long long) sub3.tv_sec, sub3.tv_nsec);
+                       }
+
+                     interpreter->run_hook = NULL;
+                   }
+               }
+
+             printf ("time spent outlining: %lld sec %ld nsec\n",
+                     (long long) sub.tv_sec, sub.tv_nsec);
+             printf ("time spent building edges: %lld sec %ld nsec\n",
+                     (long long) sub1.tv_sec, sub1.tv_nsec);
+             printf ("time spent rasterizing: %lld sec %ld nsec\n",
+                     (long long) sub2.tv_sec / 120, sub2.tv_nsec / 120);
+
+             xfree (outline);
+           }
+
+         sfnt_free_glyph (glyph);
+       }
+    }
+
+ free_lab:
+
+  xfree (font);
+
+  for (i = 0; i < table->num_subtables; ++i)
+    xfree (data[i]);
+
+  if (instance && gvar)
+    sfnt_free_blend (&blend);
+
+  xfree (table);
+  xfree (data);
+  xfree (subtables);
+  xfree (head);
+  xfree (hhea);
+  xfree (loca_long);
+  xfree (loca_short);
+  xfree (glyf);
+  xfree (maxp);
+  xfree (hmtx);
+  xfree (name);
+  xfree (meta);
+  xfree (ttc);
+  xfree (cvt);
+  xfree (fpgm);
+  xfree (interpreter);
+  xfree (prep);
+  xfree (fvar);
+  xfree (gvar);
+  xfree (avar);
+  xfree (cvar);
+
+  return 0;
+}
+
+#endif
diff --git a/src/sfnt.h b/src/sfnt.h
new file mode 100644
index 00000000000..365595fa37d
--- /dev/null
+++ b/src/sfnt.h
@@ -0,0 +1,1994 @@
+/* sfnt format font support for GNU Emacs.
+
+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, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef _SFNT_H_
+#define _SFNT_H_
+
+#include <stdint.h>
+#include <stddef.h>
+#include <setjmp.h>
+
+#include <sys/types.h>
+
+
+
+/* Container structure and enumerator definitions.  */
+
+/* The sfnt container format is organized into different tables, such
+   as ``cmap'' or ``glyf''.  Each of these tables has a specific
+   format and use.  These are all the tables known to Emacs.  */
+
+enum sfnt_table
+  {
+    SFNT_TABLE_CMAP,
+    SFNT_TABLE_GLYF,
+    SFNT_TABLE_HEAD,
+    SFNT_TABLE_HHEA,
+    SFNT_TABLE_HMTX,
+    SFNT_TABLE_LOCA,
+    SFNT_TABLE_MAXP,
+    SFNT_TABLE_NAME,
+    SFNT_TABLE_META,
+    SFNT_TABLE_CVT ,
+    SFNT_TABLE_FPGM,
+    SFNT_TABLE_PREP,
+    SFNT_TABLE_FVAR,
+    SFNT_TABLE_GVAR,
+    SFNT_TABLE_CVAR,
+    SFNT_TABLE_AVAR,
+  };
+
+#define SFNT_ENDOF(type, field, type1)                 \
+  ((size_t) offsetof (type, field) + sizeof (type1))
+
+/* Each of these structures must be aligned so that no compiler will
+   ever generate padding bytes on platforms where the alignment
+   requirements for uint32_t and uint16_t are no larger than 4 and 2
+   bytes respectively.
+
+   Pointer types are assumed to impose an alignmnent requirement no
+   less than that of uint32_t.
+
+   If a table has more than one kind of variable-length subtable array
+   at the end, make sure to pad subsequent subtables
+   appropriately.  */
+
+struct sfnt_offset_subtable
+{
+  /* The scaler type.  */
+  uint32_t scaler_type;
+
+  /* The number of tables.  */
+  uint16_t num_tables;
+
+  /* (Maximum power of 2 <= numTables) * 16.  */
+  uint16_t search_range;
+
+  /* log2 (maximum power of 2 <= numTables) */
+  uint16_t entry_selector;
+
+  /* numTables * 16 - searchRange.  */
+  uint16_t range_shift;
+
+  /* Variable length data.  */
+  struct sfnt_table_directory *subtables;
+};
+
+/* The table directory.  Follows the offset subtable, with one for
+   each table.  */
+
+struct sfnt_table_directory
+{
+  /* 4-byte identifier for each table.  See sfnt_table_names.  */
+  uint32_t tag;
+
+  /* Table checksum.  */
+  uint32_t checksum;
+
+  /* Offset from the start of the file.  */
+  uint32_t offset;
+
+  /* Length of the table in bytes, not subject to padding.  */
+  uint32_t length;
+};
+
+enum sfnt_scaler_type
+  {
+    SFNT_SCALER_TRUE = 0x74727565,
+    SFNT_SCALER_VER1 = 0x00010000,
+    SFNT_SCALER_TYP1 = 0x74797031,
+    SFNT_SCALER_OTTO = 0x4F54544F,
+  };
+
+typedef int32_t sfnt_fixed;
+typedef int16_t sfnt_fword;
+typedef uint16_t sfnt_ufword;
+
+#define sfnt_coerce_fixed(fixed) ((sfnt_fixed) (fixed) / 65535.0)
+
+typedef unsigned int sfnt_glyph;
+typedef unsigned int sfnt_char;
+
+struct sfnt_head_table
+{
+  /* The version.  This is a 16.16 fixed point number.  */
+  sfnt_fixed version;
+
+  /* The revision.  */
+  sfnt_fixed revision;
+
+  /* Checksum adjustment.  */
+  uint32_t checksum_adjustment;
+
+  /* Magic number, should be 0x5F0F3CF5.  */
+  uint32_t magic;
+
+  /* Flags for the font.  */
+  uint16_t flags;
+
+  /* Units per em.  */
+  uint16_t units_per_em;
+
+  /* Time of creation.  */
+  uint32_t created_high, created_low;
+
+  /* Time of modification.  */
+  uint32_t modified_high, modified_low;
+
+  /* Minimum bounds.  */
+  sfnt_fword xmin, ymin, xmax, ymax;
+
+  /* Mac specific stuff.  */
+  uint16_t mac_style;
+
+  /* Smallest readable size in pixels.  */
+  uint16_t lowest_rec_ppem;
+
+  /* Font direction hint.  */
+  int16_t font_direction_hint;
+
+  /* Index to loc format.  0 for short offsets, 1 for long.  */
+  int16_t index_to_loc_format;
+
+  /* Unused.  */
+  int16_t glyph_data_format;
+};
+
+struct sfnt_hhea_table
+{
+  /* The version.  This is a 16.16 fixed point number.  */
+  sfnt_fixed version;
+
+  /* The maximum ascent and descent values for this font.  */
+  sfnt_fword ascent, descent;
+
+  /* The typographic line gap.  */
+  sfnt_fword line_gap;
+
+  /* The maximum advance width.  */
+  sfnt_ufword advance_width_max;
+
+  /* The minimum bearings on either side.  */
+  sfnt_fword min_left_side_bearing, min_right_side_bearing;
+
+  /* The maximum extent.  */
+  sfnt_fword x_max_extent;
+
+  /* Caret slope.  */
+  int16_t caret_slope_rise, caret_slope_run;
+
+  /* Caret offset for non slanted fonts.  */
+  sfnt_fword caret_offset;
+
+  /* Reserved values.  */
+  int16_t reserved1, reserved2, reserved3, reserved4;
+
+  /* Should always be zero.  */
+  int16_t metric_data_format;
+
+  /* Number of advanced widths in metrics table.  */
+  uint16_t num_of_long_hor_metrics;
+};
+
+struct sfnt_cmap_table
+{
+  /* Should be zero.  */
+  uint16_t version;
+
+  /* Number of subtables.  */
+  uint16_t num_subtables;
+};
+
+enum sfnt_platform_id
+  {
+    SFNT_PLATFORM_UNICODE   = 0,
+    SFNT_PLATFORM_MACINTOSH = 1,
+    SFNT_PLATFORM_RESERVED  = 2,
+    SFNT_PLATFORM_MICROSOFT = 3,
+  };
+
+enum sfnt_unicode_platform_specific_id
+  {
+    SFNT_UNICODE_1_0                = 0,
+    SFNT_UNICODE_1_1                = 1,
+    SFNT_UNICODE_ISO_10646_1993             = 2,
+    SFNT_UNICODE_2_0_BMP            = 3,
+    SFNT_UNICODE_2_0                = 4,
+    SFNT_UNICODE_VARIATION_SEQUENCES = 5,
+    SFNT_UNICODE_LAST_RESORT        = 6,
+  };
+
+enum sfnt_macintosh_platform_specific_id
+  {
+    SFNT_MACINTOSH_ROMAN              = 0,
+    SFNT_MACINTOSH_JAPANESE           = 1,
+    SFNT_MACINTOSH_TRADITIONAL_CHINESE = 2,
+    SFNT_MACINTOSH_KOREAN             = 3,
+    SFNT_MACINTOSH_ARABIC             = 4,
+    SFNT_MACINTOSH_HEBREW             = 5,
+    SFNT_MACINTOSH_GREEK              = 6,
+    SFNT_MACINTOSH_RUSSIAN            = 7,
+    SFNT_MACINTOSH_RSYMBOL            = 8,
+    SFNT_MACINTOSH_DEVANGARI          = 9,
+    SFNT_MACINTOSH_GURMUKHI           = 10,
+    SFNT_MACINTOSH_GUJARATI           = 11,
+    SFNT_MACINTOSH_ORIYA              = 12,
+    SFNT_MACINTOSH_BENGALI            = 13,
+    SFNT_MACINTOSH_TAMIL              = 14,
+    SFNT_MACINTOSH_TELUGU             = 15,
+    SFNT_MACINTOSH_KANNADA            = 16,
+    SFNT_MACINTOSH_MALAYALAM          = 17,
+    SFNT_MACINTOSH_SINHALESE          = 18,
+    SFNT_MACINTOSH_BURMESE            = 19,
+    SFNT_MACINTOSH_KHMER              = 20,
+    SFNT_MACINTOSH_THAI                       = 21,
+    SFNT_MACINTOSH_LAOTIAN            = 22,
+    SFNT_MACINTOSH_GEORGIAN           = 23,
+    SFNT_MACINTOSH_ARMENIAN           = 24,
+    SFNT_MACINTOSH_SIMPLIFIED_CHINESE  = 25,
+    SFNT_MACINTOSH_TIBETIAN           = 26,
+    SFNT_MACINTOSH_MONGOLIAN          = 27,
+    SFNT_MACINTOSH_GEEZ                       = 28,
+    SFNT_MACINTOSH_SLAVIC             = 29,
+    SFNT_MACINTOSH_VIETNAMESE         = 30,
+    SFNT_MACINTOSH_SINDHI             = 31,
+    SFNT_MACINTOSH_UNINTERPRETED       = 32,
+  };
+
+enum sfnt_microsoft_platform_specific_id
+  {
+    SFNT_MICROSOFT_SYMBOL       = 0,
+    SFNT_MICROSOFT_UNICODE_BMP  = 1,
+    SFNT_MICROSOFT_SHIFT_JIS    = 2,
+    SFNT_MICROSOFT_PRC          = 3,
+    SFNT_MICROSOFT_BIG_FIVE     = 4,
+    SFNT_MICROSOFT_WANSUNG      = 5,
+    SFNT_MICROSOFT_JOHAB        = 6,
+    SFNT_MICROSOFT_UNICODE_UCS_4 = 10,
+  };
+
+struct sfnt_cmap_encoding_subtable
+{
+  /* The platform ID.  */
+  uint16_t platform_id;
+
+  /* Platform specific ID.  */
+  uint16_t platform_specific_id;
+
+  /* Mapping table offset.  */
+  uint32_t offset;
+};
+
+struct sfnt_cmap_encoding_subtable_data
+{
+  /* Format and possibly the length in bytes.  */
+  uint16_t format, length;
+};
+
+struct sfnt_cmap_format_0
+{
+  /* Format, set to 0.  */
+  uint16_t format;
+
+  /* Length in bytes.  Should be 262.  */
+  uint16_t length;
+
+  /* Language code.  */
+  uint16_t language;
+
+  /* Character code to glyph index map.  */
+  uint8_t glyph_index_array[256];
+};
+
+struct sfnt_cmap_format_2_subheader
+{
+  uint16_t first_code;
+  uint16_t entry_count;
+  int16_t id_delta;
+  uint16_t id_range_offset;
+};
+
+struct sfnt_cmap_format_2
+{
+  /* Format, set to 2.  */
+  uint16_t format;
+
+  /* Length in bytes.  */
+  uint16_t length;
+
+  /* Language code.  */
+  uint16_t language;
+
+  /* Array mapping high bytes to subheaders.  */
+  uint16_t sub_header_keys[256];
+
+  /* Variable length data.  */
+  struct sfnt_cmap_format_2_subheader *subheaders;
+  uint16_t *glyph_index_array;
+  uint16_t num_glyphs;
+};
+
+struct sfnt_cmap_format_4
+{
+  /* Format, set to 4.  */
+  uint16_t format;
+
+  /* Length in bytes.  */
+  uint16_t length;
+
+  /* Language code.  */
+  uint16_t language;
+
+  /* 2 * seg_count.  */
+  uint16_t seg_count_x2;
+
+  /* 2 * (2**FLOOR(log2(segCount))) */
+  uint16_t search_range;
+
+  /* log2(searchRange/2) */
+  uint16_t entry_selector;
+
+  /* Variable-length data.  */
+  uint16_t *end_code;
+  uint16_t *reserved_pad;
+  uint16_t *start_code;
+  int16_t *id_delta;
+  int16_t *id_range_offset;
+  uint16_t *glyph_index_array;
+
+  /* The number of elements in glyph_index_array.  */
+  size_t glyph_index_size;
+};
+
+struct sfnt_cmap_format_6
+{
+  /* Format, set to 6.  */
+  uint16_t format;
+
+  /* Length in bytes.  */
+  uint16_t length;
+
+  /* Language code.  */
+  uint16_t language;
+
+  /* First character code in subrange.  */
+  uint16_t first_code;
+
+  /* Number of character codes.  */
+  uint16_t entry_count;
+
+  /* Variable-length data.  */
+  uint16_t *glyph_index_array;
+};
+
+struct sfnt_cmap_format_8_or_12_group
+{
+  uint32_t start_char_code;
+  uint32_t end_char_code;
+  uint32_t start_glyph_code;
+};
+
+struct sfnt_cmap_format_8
+{
+  /* Format, set to 8.  */
+  uint16_t format;
+
+  /* Reserved.  */
+  uint16_t reserved;
+
+  /* Length in bytes.  */
+  uint32_t length;
+
+  /* Language code.  */
+  uint32_t language;
+
+  /* Tightly packed array of bits (8K bytes total) indicating whether
+     the particular 16-bit (index) value is the start of a 32-bit
+     character code.  */
+  uint8_t is32[65536];
+
+  /* Number of groups.  */
+  uint32_t num_groups;
+
+  /* Variable length data.  */
+  struct sfnt_cmap_format_8_or_12_group *groups;
+};
+
+/* cmap formats 10, 13 unsupported.  */
+
+struct sfnt_cmap_format_12
+{
+  /* Format, set to 12.  */
+  uint16_t format;
+
+  /* Reserved.  */
+  uint16_t reserved;
+
+  /* Length in bytes.  */
+  uint32_t length;
+
+  /* Language code.  */
+  uint32_t language;
+
+  /* Number of groups.  */
+  uint32_t num_groups;
+
+  /* Variable length data.  */
+  struct sfnt_cmap_format_8_or_12_group *groups;
+};
+
+struct sfnt_cmap_format_14
+{
+  /* Format, set to 14.  */
+  uint16_t format;
+
+  /* The length of the table in bytes.  */
+  uint32_t length;
+
+  /* Number of variation selector records.  */
+  uint16_t num_var_selector_records;
+
+  /* The offset of this table in the font file.  */
+  off_t offset;
+
+  /* Variable length data.  */
+  struct sfnt_variation_selector_record *records;
+};
+
+struct sfnt_variation_selector_record
+{
+  /* 24-bit unsigned variation selector.  */
+  unsigned int var_selector;
+
+  /* Offset to default UVS table.  */
+  uint32_t default_uvs_offset;
+
+  /* Offset to non-default UVS table.  */
+  uint32_t nondefault_uvs_offset;
+};
+
+struct sfnt_maxp_table
+{
+  /* Table version.  */
+  sfnt_fixed version;
+
+  /* The number of glyphs in this font - 1.  Set at version 0.5 or
+     later.  */
+  uint16_t num_glyphs;
+
+  /* These fields are only set in version 1.0 or later.  Maximum
+     points in a non-composite glyph.  */
+  uint16_t max_points;
+
+  /* Maximum contours in a non-composite glyph.  */
+  uint16_t max_contours;
+
+  /* Maximum points in a composite glyph.  */
+  uint16_t max_composite_points;
+
+  /* Maximum contours in a composite glyph.  */
+  uint16_t max_composite_contours;
+
+  /* 1 if instructions do not use the twilight zone (Z0), or 2 if
+     instructions do use Z0; should be set to 2 in most cases.  */
+  uint16_t max_zones;
+
+  /* Maximum points used in Z0.  */
+  uint16_t max_twilight_points;
+
+  /* Number of Storage Area locations.  */
+  uint16_t max_storage;
+
+  /* Number of FDEFs, equal to the highest function number + 1.  */
+  uint16_t max_function_defs;
+
+  /* Number of IDEFs.  */
+  uint16_t max_instruction_defs;
+
+  /* Maximum stack depth across Font Program ('fpgm' table), CVT
+     Program ('prep' table) and all glyph instructions (in the 'glyf'
+     table).  */
+  uint16_t max_stack_elements;
+
+  /* Maximum byte count for glyph instructions.  */
+  uint16_t max_size_of_instructions;
+
+  /* Maximum number of components referenced at ``top level'' for any
+     composite glyph.  */
+  uint16_t max_component_elements;
+
+  /* Maximum levels of recursion; 1 for simple components.  */
+  uint16_t max_component_depth;
+};
+
+struct sfnt_loca_table_short
+{
+  /* Offsets to glyph data divided by two.  */
+  uint16_t *offsets;
+
+  /* Size of the offsets list.  */
+  size_t num_offsets;
+};
+
+struct sfnt_loca_table_long
+{
+  /* Offsets to glyph data.  */
+  uint32_t *offsets;
+
+  /* Size of the offsets list.  */
+  size_t num_offsets;
+};
+
+struct sfnt_glyf_table
+{
+  /* Size of the glyph data.  */
+  size_t size;
+
+  /* Pointer to possibly unaligned glyph data.  */
+  unsigned char *glyphs;
+
+  /* Pointer to the start of the mapping.
+     Only initialized if this table was mmapped.  */
+  unsigned char *start;
+};
+
+struct sfnt_simple_glyph
+{
+  /* The total number of points in this glyph.  */
+  size_t number_of_points;
+
+  /* Array containing the last points of each contour.  */
+  uint16_t *restrict end_pts_of_contours;
+
+  /* Total number of bytes needed for instructions.  */
+  uint16_t instruction_length;
+
+  /* Instruction data.  */
+  uint8_t *restrict instructions;
+
+  /* Array of flags.  */
+  uint8_t *restrict flags;
+
+  /* Array of X coordinates.  */
+  int16_t *restrict x_coordinates;
+
+  /* Array of Y coordinates.  */
+  int16_t *restrict y_coordinates;
+
+  /* Pointer to the end of that array.  */
+  int16_t *restrict y_coordinates_end;
+};
+
+struct sfnt_compound_glyph_component
+{
+  /* Compound glyph flags.  */
+  uint16_t flags;
+
+  /* Component glyph index.  */
+  uint16_t glyph_index;
+
+  /* X-offset for component or point number; type depends on bits 0
+     and 1 in component flags.  */
+  union {
+    uint8_t a;
+    int8_t b;
+    uint16_t c;
+    int16_t d;
+  } argument1;
+
+  /* Y-offset for component or point number; type depends on bits 0
+     and 1 in component flags.  */
+  union {
+    uint8_t a;
+    int8_t b;
+    uint16_t c;
+    int16_t d;
+  } argument2;
+
+  /* Various scale formats.  */
+  union {
+    uint16_t scale;
+    struct {
+      uint16_t xscale;
+      uint16_t yscale;
+    } a;
+    struct {
+      uint16_t xscale;
+      uint16_t scale01;
+      uint16_t scale10;
+      uint16_t yscale;
+    } b;
+  } u;
+};
+
+struct sfnt_compound_glyph
+{
+  /* Pointer to array of components.  */
+  struct sfnt_compound_glyph_component *components;
+
+  /* Number of elements in that array.  */
+  size_t num_components;
+
+  /* Instruction data.  */
+  uint8_t *instructions;
+
+  /* Length of instructions.  */
+  uint16_t instruction_length;
+};
+
+struct sfnt_glyph
+{
+  /* Number of contours in this glyph.  */
+  int16_t number_of_contours;
+
+  /* Coordinate bounds.  */
+  sfnt_fword xmin, ymin, xmax, ymax;
+
+  /* Distortion applied to the right side phantom point.  */
+  sfnt_fword advance_distortion;
+
+  /* Distortion applied to the origin point.  */
+  sfnt_fword origin_distortion;
+
+  /* Either a simple glyph or a compound glyph, depending on which is
+     set.  */
+  struct sfnt_simple_glyph *simple;
+  struct sfnt_compound_glyph *compound;
+};
+
+
+
+/* Glyph outline decomposition.  */
+
+struct sfnt_point
+{
+  /* X and Y in em space.  */
+  sfnt_fixed x, y;
+};
+
+typedef void (*sfnt_move_to_proc) (struct sfnt_point, void *);
+typedef void (*sfnt_line_to_proc) (struct sfnt_point, void *);
+typedef void (*sfnt_curve_to_proc) (struct sfnt_point,
+                                   struct sfnt_point,
+                                   void *);
+
+typedef struct sfnt_glyph *(*sfnt_get_glyph_proc) (sfnt_glyph, void *,
+                                                  bool *);
+typedef void (*sfnt_free_glyph_proc) (struct sfnt_glyph *, void *);
+
+
+
+/* Decomposed glyph outline.  */
+
+struct sfnt_glyph_outline_command
+{
+  /* Flags for this outline command.  */
+  int flags;
+
+  /* X and Y position of this command.  */
+  sfnt_fixed x, y;
+};
+
+/* Structure describing a single recorded outline in fixed pixel
+   space.  */
+
+struct sfnt_glyph_outline
+{
+  /* Array of outlines elements.  */
+  struct sfnt_glyph_outline_command *outline;
+
+  /* Size of the outline data, and how much is full.  */
+  size_t outline_size, outline_used;
+
+  /* Rectangle defining bounds of the outline.  Namely, the minimum
+     and maximum X and Y positions.  */
+  sfnt_fixed xmin, ymin, xmax, ymax;
+
+  /* The origin point of the outline on the X axis.  Value defaults to
+     0.  */
+  sfnt_fixed origin;
+
+  /* Reference count.  Initially zero.  */
+  short refcount;
+};
+
+enum sfnt_glyph_outline_flags
+  {
+    SFNT_GLYPH_OUTLINE_LINETO    = (1 << 1),
+  };
+
+
+
+/* Glyph rasterization.  */
+
+struct sfnt_raster
+{
+  /* Pointer to coverage data.  */
+  unsigned char *cells;
+
+  /* Basic dimensions of the raster.  */
+  unsigned short width, height;
+
+  /* Integer offset to apply to positions in the raster so that they
+     start from the origin point of the glyph.  */
+  short offx, offy;
+
+  /* The raster stride.  */
+  unsigned short stride;
+
+  /* Reference count.  Initially zero.  */
+  unsigned short refcount;
+};
+
+struct sfnt_edge
+{
+  /* Next edge in this chain.  */
+  struct sfnt_edge *next;
+
+  /* Winding direction.  1 if clockwise, -1 if counterclockwise.  */
+  int winding;
+
+  /* X position, top and bottom of edges.  */
+  sfnt_fixed x, top, bottom;
+
+  /* Amount to move X by upon each change of Y.  */
+  sfnt_fixed step_x;
+};
+
+
+
+/* Polygon rasterization constants.  */
+
+enum
+  {
+    SFNT_POLY_SHIFT  = 3,
+    SFNT_POLY_SAMPLE = (1 << SFNT_POLY_SHIFT),
+    SFNT_POLY_MASK   = (SFNT_POLY_SAMPLE - 1),
+    SFNT_POLY_STEP   = (0x10000 >> SFNT_POLY_SHIFT),
+    SFNT_POLY_START  = (SFNT_POLY_STEP >> 1),
+  };
+
+
+
+/* Glyph metrics computation.  */
+
+struct sfnt_long_hor_metric
+{
+  uint16_t advance_width;
+  int16_t left_side_bearing;
+};
+
+struct sfnt_hmtx_table
+{
+  /* Array of horizontal metrics for each glyph.  */
+  struct sfnt_long_hor_metric *h_metrics;
+
+  /* Lbearing for remaining glyphs.  */
+  int16_t *left_side_bearing;
+};
+
+/* Structure describing the metrics of a single glyph.  The fields
+   mean the same as in XCharStruct, except they are 16.16 fixed point
+   values, and are missing significant information.  */
+
+struct sfnt_glyph_metrics
+{
+  /* Distance between origin and left edge of raster.  Positive
+     changes move rightwards.
+
+     If sfnt_lookup_glyph_metrics is given a pixel size of -1,
+     this is actually a sign extended fword.  */
+  sfnt_fixed lbearing;
+
+  /* Advance to next glyph's origin.
+
+     If sfnt_lookup_glyph_metrics is given a pixel size of -1, this is
+     actually a sign extended fword.  */
+  sfnt_fixed advance;
+};
+
+
+
+/* Font style parsing.  */
+
+struct sfnt_name_record
+{
+  /* Platform identifier code.  */
+  uint16_t platform_id;
+
+  /* Platform specific ID.  */
+  uint16_t platform_specific_id;
+
+  /* Language identifier.  */
+  uint16_t language_id;
+
+  /* Name identifier.  */
+  uint16_t name_id;
+
+  /* String length in bytes.  */
+  uint16_t length;
+
+  /* Offset from start of storage area.  */
+  uint16_t offset;
+};
+
+struct sfnt_name_table
+{
+  /* Format selector of name table.  */
+  uint16_t format;
+
+  /* Number of name records.  */
+  uint16_t count;
+
+  /* Offset to start of string data.  */
+  uint16_t string_offset;
+
+  /* Variable length data.  */
+  struct sfnt_name_record *name_records;
+
+  /* Start of string data.  */
+  unsigned char *data;
+};
+
+/* Name identifier codes.  These are Apple's codes, not
+   Microsoft's.  */
+
+enum sfnt_name_identifier_code
+  {
+    SFNT_NAME_COPYRIGHT_NOTICE                 = 0,
+    SFNT_NAME_FONT_FAMILY                      = 1,
+    SFNT_NAME_FONT_SUBFAMILY                   = 2,
+    SFNT_NAME_UNIQUE_SUBFAMILY_IDENTIFICATION  = 3,
+    SFNT_NAME_FULL_NAME                                = 4,
+    SFNT_NAME_NAME_TABLE_VERSION               = 5,
+    SFNT_NAME_POSTSCRIPT_NAME                  = 6,
+    SFNT_NAME_TRADEMARK_NOTICE                 = 7,
+    SFNT_NAME_MANUFACTURER_NAME                        = 8,
+    SFNT_NAME_DESIGNER                         = 9,
+    SFNT_NAME_DESCRIPTION                      = 10,
+    SFNT_NAME_FONT_VENDOR_URL                  = 11,
+    SFNT_NAME_FONT_DESIGNER_URL                        = 12,
+    SFNT_NAME_LICENSE_DESCRIPTION              = 13,
+    SFNT_NAME_LICENSE_INFORMATION_URL          = 14,
+    SFNT_NAME_PREFERRED_FAMILY                 = 16,
+    SFNT_NAME_PREFERRED_SUBFAMILY              = 17,
+    SFNT_NAME_COMPATIBLE_FULL                  = 18,
+    SFNT_NAME_SAMPLE_TEXT                      = 19,
+    SFNT_NAME_VARIATIONS_POSTSCRIPT_NAME_PREFIX = 25,
+  };
+
+struct sfnt_meta_data_map
+{
+  /* Identifier for the tag.  */
+  uint32_t tag;
+
+  /* Offset from start of table to data.  */
+  uint32_t data_offset;
+
+  /* Length of the data.  */
+  uint32_t data_length;
+};
+
+struct sfnt_meta_table
+{
+  /* Version of the table.  Currently set to 1.  */
+  uint32_t version;
+
+  /* Flags.  Currently 0.  */
+  uint32_t flags;
+
+  /* Offset from start of table to beginning of variable length
+     data.  */
+  uint32_t data_offset;
+
+  /* Number of data maps in the table.  */
+  uint32_t num_data_maps;
+
+  /* Beginning of variable length data.  */
+  struct sfnt_meta_data_map *data_maps;
+
+  /* The whole table contents.  */
+  unsigned char *data;
+};
+
+enum sfnt_meta_data_tag
+  {
+    SFNT_META_DATA_TAG_DLNG = 0x646c6e67,
+    SFNT_META_DATA_TAG_SLNG = 0x736c6e67,
+  };
+
+
+
+/* TrueType collection format support.  */
+
+struct sfnt_ttc_header
+{
+  /* TrueType collection ID tag.  */
+  uint32_t ttctag;
+
+  /* Version of the TTC header.  */
+  uint32_t version;
+
+  /* Number of fonts in the TTC header.  */
+  uint32_t num_fonts;
+
+  /* Array of offsets to the offset table for each font in the
+     file.  */
+  uint32_t *offset_table;
+
+  /* Tag indicating that a DSIG table exists, or 0.  Fields from here
+     on are only set on version 2.0 headers or later.  */
+  uint32_t ul_dsig_tag;
+
+  /* Length in bytes of the signature table, or 0 if there is no
+     signature.  */
+  uint32_t ul_dsig_length;
+
+  /* Offset in bytes of the dsig table from the beginning of the TTC
+     file.  */
+  uint32_t ul_dsig_offset;
+};
+
+enum sfnt_ttc_tag
+  {
+    SFNT_TTC_TTCF = 0x74746366,
+    SFNT_TTC_DSIG = 0x44534947,
+  };
+
+
+
+/* Unicode Variation Sequence (UVS) support.  */
+
+struct sfnt_default_uvs_table
+{
+  /* Number of ranges that follow.  */
+  uint32_t num_unicode_value_ranges;
+
+  /* Variable length data.  */
+  struct sfnt_unicode_value_range *ranges;
+};
+
+struct sfnt_unicode_value_range
+{
+  /* First value in this range.  */
+  unsigned int start_unicode_value;
+
+  /* Number of additional values in this range.  */
+  unsigned char additional_count;
+};
+
+struct sfnt_nondefault_uvs_table
+{
+  /* Number of UVS mappings which follow.  */
+  uint32_t num_uvs_mappings;
+
+  /* Variable length data.  */
+  struct sfnt_uvs_mapping *mappings;
+};
+
+struct sfnt_uvs_mapping
+{
+  /* Base character value.  */
+  unsigned int unicode_value;
+
+  /* Glyph ID of the base character value.  */
+  uint16_t base_character_value;
+};
+
+struct sfnt_mapped_variation_selector_record
+{
+  /* The variation selector.  */
+  unsigned int selector;
+
+  /* Its default UVS table.  */
+  struct sfnt_default_uvs_table *default_uvs;
+
+  /* Its nondefault UVS table.  */
+  struct sfnt_nondefault_uvs_table *nondefault_uvs;
+};
+
+/* Structure describing a single offset to load into a variation
+   selection context.  */
+
+struct sfnt_table_offset_rec
+{
+  /* The offset from the start of the font file.  */
+  off_t offset;
+
+  /* Whether or not the offset points to a non-default UVS table.  */
+  bool is_nondefault_table;
+
+  /* Pointer to the UVS table.  */
+  void *table;
+};
+
+struct sfnt_uvs_context
+{
+  /* Number of records and tables.  */
+  size_t num_records, nmemb;
+
+  /* Array of UVS tables.  */
+  struct sfnt_table_offset_rec *tables;
+
+  /* Array of variation selector records mapped to
+     their corresponding tables.  */
+  struct sfnt_mapped_variation_selector_record *records;
+};
+
+
+
+#if defined HAVE_MMAP && !defined TEST
+
+/* Memory mapping support.  */
+
+struct sfnt_mapped_table
+{
+  /* Pointer to table data.  */
+  void *data;
+
+  /* Pointer to table mapping.  */
+  void *mapping;
+
+  /* Size of mapped data and size of mapping.  */
+  size_t length, size;
+};
+
+#endif /* HAVE_MMAP && !TEST */
+
+
+
+/* Glyph variation support.  */
+
+/* 2.14 fixed point type used to represent versors of unit
+   vectors.  */
+typedef int16_t sfnt_f2dot14;
+
+/* Forward declaration used only for the distortable font stuff.  */
+struct sfnt_cvt_table;
+
+struct sfnt_variation_axis
+{
+  /* The axis tag.  */
+  uint32_t axis_tag;
+
+  /* The minimum style coordinate for the axis.  */
+  sfnt_fixed min_value;
+
+  /* The default style coordinate for the axis.  */
+  sfnt_fixed default_value;
+
+  /* The maximum style coordinate for the axis.  */
+  sfnt_fixed max_value;
+
+  /* Set to zero.  */
+  uint16_t flags;
+
+  /* Identifier under which this axis's name will be found in the
+     `name' table.  */
+  uint16_t name_id;
+};
+
+struct sfnt_instance
+{
+  /* The instance name ID.  */
+  uint16_t name_id;
+
+  /* Flags.  */
+  uint16_t flags;
+
+  /* Optional PostScript name.  */
+  uint16_t ps_name_id;
+
+  /* Coordinates of each defined instance.  */
+  sfnt_fixed *coords;
+};
+
+struct sfnt_fvar_table
+{
+  /* Major version; should be 1.  */
+  uint16_t major_version;
+
+  /* Minor version; should be 0.  */
+  uint16_t minor_version;
+
+  /* Offset in bytes from the beginning of the table to the beginning
+     of the first axis data.  */
+  uint16_t offset_to_data;
+
+  /* Reserved field; always 2.  */
+  uint16_t count_size_pairs;
+
+  /* Number of style axes in this font.  */
+  uint16_t axis_count;
+
+  /* The number of bytes in each variation axis record.  Currently 20
+     bytes.  */
+  uint16_t axis_size;
+
+  /* The number of named instances for the font found in the
+     instance array.  */
+  uint16_t instance_count;
+
+  /* The size of each instance record.  */
+  uint16_t instance_size;
+
+  /* Variable length data.  */
+  struct sfnt_variation_axis *axis;
+  struct sfnt_instance *instance;
+};
+
+struct sfnt_short_frac_correspondence
+{
+  /* Value in normalized user space.  */
+  sfnt_f2dot14 from_coord;
+
+  /* Value in normalized axis space.  */
+  sfnt_f2dot14 to_coord;
+};
+
+struct sfnt_short_frac_segment
+{
+  /* The number of pairs for this axis.  */
+  uint16_t pair_count;
+
+  /* Variable length data.  */
+  struct sfnt_short_frac_correspondence *correspondence;
+};
+
+struct sfnt_avar_table
+{
+  /* The version of the table.  Should be 1.0.  */
+  sfnt_fixed version;
+
+  /* Number of variation axes defined in this table.
+     XXX: why is this signed? */
+  int32_t axis_count;
+
+  /* Variable length data.  */
+  struct sfnt_short_frac_segment *segments;
+};
+
+struct sfnt_tuple_variation
+{
+  /* Tuple point numbers.  */
+  uint16_t *points;
+
+  /* Deltas.  */
+  sfnt_fword *deltas;
+
+  /* Tuple coordinates.  One for each axis specified in the [gaf]var
+     tables.  */
+  sfnt_f2dot14 *coordinates;
+
+  /* Intermediate start and end coordinates.  */
+  sfnt_f2dot14 *restrict intermediate_start;
+
+  /* Intermediate start and end coordinates.  */
+  sfnt_f2dot14 *restrict intermediate_end;
+
+  /* The number of points and deltas present.
+
+     UINT16_MAX and POINTS set to NULL means there are deltas for each
+     CVT entry.  */
+  uint16_t num_points;
+};
+
+struct sfnt_cvar_table
+{
+  /* The version of this CVT variations table.  */
+  sfnt_fixed version;
+
+  /* Flags.  */
+  uint16_t tuple_count;
+
+  /* Offset from the beginning of the table to the tuple data.  */
+  uint16_t data_offset;
+
+  /* Variable length data.  */
+  struct sfnt_tuple_variation *variation;
+};
+
+struct sfnt_gvar_table
+{
+  /* Version of the glyph variations table.  */
+  uint16_t version;
+
+  /* Reserved, currently 0.  */
+  uint16_t reserved;
+
+  /* The number of style axes for this font.  This must be the same
+     number as axisCount in the 'fvar' table.  */
+  uint16_t axis_count;
+
+  /* The number of shared coordinates.  */
+  uint16_t shared_coord_count;
+
+  /* Byte offset from the beginning of this table to the list of
+     shared style coordinates.  */
+  uint32_t offset_to_coord;
+
+  /* The number of glyphs in this font; this should match the number
+     of the glyphs store elsewhere in the font.  */
+  uint16_t glyph_count;
+
+  /* Bit-field that gives the format of the offset array that
+     follows. If the flag is 0, the type is uint16. If the flag is 1,
+     the type is unit 32.  */
+  uint16_t flags;
+
+  /* Byte offset from the beginning of this table to the first glyph
+     glyphVariationData.  */
+  uint32_t offset_to_data;
+
+  /* Number of bytes in the glyph variation data.  */
+  size_t data_size;
+
+  /* Byte offsets from the beginning of the glyphVariationData array
+     to the glyphVariationData for each glyph in the font.  The format
+     of this field is set by the flags field.  */
+  union {
+    uint16_t *offset_word;
+    uint32_t *offset_long;
+  } u;
+
+  /* Other variable length data.  */
+  sfnt_f2dot14 *global_coords;
+  unsigned char *glyph_variation_data;
+};
+
+/* Structure repesenting a set of axis coordinates and their
+   normalized equivalents.
+
+   To use this structure, call
+
+     sfnt_init_blend (&blend, fvar, gvar)
+
+   on a `struct sfnt_blend *', with an appropriate fvar and gvar
+   table.
+
+   Then, fill in blend.coords with the un-normalized coordinates,
+   and call
+
+     sfnt_normalize_blend (&blend)
+
+   finally, call sfnt_vary_simple_glyph and related functions.  */
+
+struct sfnt_blend
+{
+  /* The fvar table.  This determines the number of elements in each
+     of the arrays below.  */
+  struct sfnt_fvar_table *fvar;
+
+  /* The gvar table.  This provides the glyph variation data.  */
+  struct sfnt_gvar_table *gvar;
+
+  /* The avar table.  This provides adjustments to normalized axis
+     values, and may be NULL.  */
+  struct sfnt_avar_table *avar;
+
+  /* The cvar table.  This provides adjustments to CVT values, and may
+     be NULL.  */
+  struct sfnt_cvar_table *cvar;
+
+  /* Un-normalized coordinates.  */
+  sfnt_fixed *coords;
+
+  /* Normalized coordinates.  */
+  sfnt_fixed *norm_coords;
+};
+
+struct sfnt_metrics_distortion
+{
+  /* Distortion applied to the origin point.  */
+  sfnt_fword origin;
+
+  /* Distortion applied to the advance point.  */
+  sfnt_fword advance;
+};
+
+
+
+#define SFNT_CEIL_FIXED(fixed) (((fixed) + 0177777) & 037777600000)
+#define SFNT_FLOOR_FIXED(fixed) ((fixed) & 037777600000)
+
+
+
+/* Function declarations.  Keep these sorted by the order in which
+   they appear in sfnt.c.  Keep each line no longer than 80
+   columns.  */
+
+#ifndef TEST
+
+extern struct sfnt_offset_subtable *sfnt_read_table_directory (int);
+
+#define PROTOTYPE                              \
+  int, struct sfnt_offset_subtable *,          \
+  struct sfnt_cmap_encoding_subtable **,       \
+  struct sfnt_cmap_encoding_subtable_data ***
+extern struct sfnt_cmap_table *sfnt_read_cmap_table (PROTOTYPE);
+#undef PROTOTYPE
+
+extern sfnt_glyph sfnt_lookup_glyph (sfnt_char,
+                                    struct sfnt_cmap_encoding_subtable_data *);
+
+#define PROTOTYPE int, struct sfnt_offset_subtable *
+extern struct sfnt_head_table *sfnt_read_head_table (PROTOTYPE);
+extern struct sfnt_hhea_table *sfnt_read_hhea_table (PROTOTYPE);
+extern struct sfnt_loca_table_short *sfnt_read_loca_table_short (PROTOTYPE);
+extern struct sfnt_loca_table_long *sfnt_read_loca_table_long (PROTOTYPE);
+extern struct sfnt_maxp_table *sfnt_read_maxp_table (PROTOTYPE);
+extern struct sfnt_glyf_table *sfnt_read_glyf_table (PROTOTYPE);
+
+#ifdef HAVE_MMAP
+extern struct sfnt_glyf_table *sfnt_map_glyf_table (PROTOTYPE);
+extern int sfnt_unmap_glyf_table (struct sfnt_glyf_table *);
+#endif /* HAVE_MMAP */
+#undef PROTOTYPE
+
+extern struct sfnt_glyph *sfnt_read_glyph (sfnt_glyph, struct sfnt_glyf_table 
*,
+                                          struct sfnt_loca_table_short *,
+                                          struct sfnt_loca_table_long *);
+extern void sfnt_free_glyph (struct sfnt_glyph *);
+
+#define PROTOTYPE              \
+  struct sfnt_glyph *,         \
+  sfnt_fixed,                  \
+  struct sfnt_glyph_metrics *, \
+  sfnt_get_glyph_proc,         \
+  sfnt_free_glyph_proc,                \
+  void *
+extern struct sfnt_glyph_outline *sfnt_build_glyph_outline (PROTOTYPE);
+#undef PROTOTYPE
+
+extern void sfnt_prepare_raster (struct sfnt_raster *,
+                                struct sfnt_glyph_outline *);
+
+#define PROTOTYPE struct sfnt_glyph_outline *
+extern struct sfnt_raster *sfnt_raster_glyph_outline (PROTOTYPE);
+#undef PROTOTYPE
+
+#define PROTOTYPE                      \
+  int,                                 \
+  struct sfnt_offset_subtable *,       \
+  struct sfnt_hhea_table *,            \
+  struct sfnt_maxp_table *
+extern struct sfnt_hmtx_table *sfnt_read_hmtx_table (PROTOTYPE);
+#undef PROTOTYPE
+
+extern int sfnt_lookup_glyph_metrics (sfnt_glyph, int,
+                                     struct sfnt_glyph_metrics *,
+                                     struct sfnt_hmtx_table *,
+                                     struct sfnt_hhea_table *,
+                                     struct sfnt_head_table *,
+                                     struct sfnt_maxp_table *);
+
+extern void sfnt_scale_metrics (struct sfnt_glyph_metrics *,
+                               sfnt_fixed);
+extern sfnt_fixed sfnt_get_scale (struct sfnt_head_table *, int);
+
+#define PROTOTYPE int, struct sfnt_offset_subtable *
+extern struct sfnt_name_table *sfnt_read_name_table (PROTOTYPE);
+#undef PROTOTYPE
+
+extern unsigned char *sfnt_find_name (struct sfnt_name_table *,
+                                     enum sfnt_name_identifier_code,
+                                     struct sfnt_name_record *);
+
+#define PROTOTYPE int, struct sfnt_offset_subtable *
+extern struct sfnt_meta_table *sfnt_read_meta_table (PROTOTYPE);
+#undef PROTOTYPE
+
+extern char *sfnt_find_metadata (struct sfnt_meta_table *,
+                                enum sfnt_meta_data_tag,
+                                struct sfnt_meta_data_map *);
+
+extern struct sfnt_ttc_header *sfnt_read_ttc_header (int);
+
+
+
+#define PROTOTYPE struct sfnt_cmap_format_14 *, int
+
+extern struct sfnt_uvs_context *sfnt_create_uvs_context (PROTOTYPE);
+
+#undef PROTOTYPE
+
+extern void sfnt_free_uvs_context (struct sfnt_uvs_context *);
+
+#define PROTOTYPE struct sfnt_nondefault_uvs_table *, sfnt_char
+
+extern sfnt_glyph sfnt_variation_glyph_for_char (PROTOTYPE);
+
+#undef PROTOTYPE
+
+
+
+#ifdef HAVE_MMAP
+
+extern int sfnt_map_table (int, struct sfnt_offset_subtable *,
+                          uint32_t, struct sfnt_mapped_table *);
+extern int sfnt_unmap_table (struct sfnt_mapped_table *);
+
+#endif /* HAVE_MMAP */
+
+
+
+extern void *sfnt_read_table (int, struct sfnt_offset_subtable *,
+                             uint32_t, size_t *);
+
+
+
+#define PROTOTYPE int, struct sfnt_offset_subtable *
+
+extern struct sfnt_fvar_table *sfnt_read_fvar_table (PROTOTYPE);
+extern struct sfnt_gvar_table *sfnt_read_gvar_table (PROTOTYPE);
+extern struct sfnt_avar_table *sfnt_read_avar_table (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE                              \
+  int,                                         \
+  struct sfnt_offset_subtable *,               \
+  struct sfnt_fvar_table *,                    \
+  struct sfnt_cvt_table *
+
+extern struct sfnt_cvar_table *sfnt_read_cvar_table (PROTOTYPE);
+
+#undef PROTOTYPE
+
+
+
+extern void sfnt_init_blend (struct sfnt_blend *,
+                            struct sfnt_fvar_table *,
+                            struct sfnt_gvar_table *,
+                            struct sfnt_avar_table *,
+                            struct sfnt_cvar_table *);
+extern void sfnt_free_blend (struct sfnt_blend *);
+extern void sfnt_normalize_blend (struct sfnt_blend *);
+
+
+
+extern int sfnt_vary_simple_glyph (struct sfnt_blend *, sfnt_glyph,
+                                  struct sfnt_glyph *,
+                                  struct sfnt_metrics_distortion *);
+extern int sfnt_vary_compound_glyph (struct sfnt_blend *, sfnt_glyph,
+                                    struct sfnt_glyph *,
+                                    struct sfnt_metrics_distortion *);
+
+#endif /* TEST */
+
+
+
+/* TrueType hinting support.  */
+
+/* Structure definitions for tables used by the TrueType
+   interpreter.  */
+
+struct sfnt_cvt_table
+{
+  /* Number of elements in the control value table.  */
+  size_t num_elements;
+
+  /* Pointer to elements in the control value table.  */
+  sfnt_fword *values;
+};
+
+struct sfnt_fpgm_table
+{
+  /* Number of instructions in the font program table.  */
+  size_t num_instructions;
+
+  /* Pointer to elements in the font program table.  */
+  unsigned char *instructions;
+};
+
+struct sfnt_prep_table
+{
+  /* Number of instructions in the control value program (pre-program)
+     table.  */
+  size_t num_instructions;
+
+  /* Pointer to elements in the preprogram table.  */
+  unsigned char *instructions;
+};
+
+
+
+/* Fixed point types used by the TrueType interpreter.  */
+
+/* 26.6 fixed point type used within the interpreter.  */
+typedef int32_t sfnt_f26dot6;
+
+/* 18.14 fixed point type used to calculate rounding details.  */
+typedef int32_t sfnt_f18dot14;
+
+
+
+/* Interpreter execution environment.  */
+
+struct sfnt_unit_vector
+{
+  /* X and Y versors of the 2d unit vector.  */
+  sfnt_f2dot14 x, y;
+};
+
+struct sfnt_interpreter_definition
+{
+  /* The opcode of this instruction or function.  */
+  uint16_t opcode;
+
+  /* The number of instructions.  */
+  uint16_t instruction_count;
+
+  /* Pointer to instructions belonging to the definition.  This
+     pointer points directly into the control value or font program.
+     Make sure both programs are kept around as long as the
+     interpreter continues to exist.  */
+  unsigned char *instructions;
+};
+
+/* This structure represents a ``struct sfnt_glyph'' that has been
+   scaled to a given pixel size.
+
+   It can either contain a simple glyph, or a decomposed compound
+   glyph; instructions are interpreted for both simple glyphs, simple
+   glyph components inside a compound glyph, and compound glyphs as a
+   whole.
+
+   In addition to the glyph data itself, it also records various
+   information for the instruction interpretation process:
+
+     - ``current'' point coordinates, which have been modified
+       by the instructing process.
+
+     - two phantom points at the origin and the advance of the
+       glyph.  */
+
+struct sfnt_interpreter_zone
+{
+  /* The number of points in this zone, including the two phantom
+     points at the end.  */
+  size_t num_points;
+
+  /* The number of contours in this zone.  */
+  size_t num_contours;
+
+  /* The end points of each contour.  */
+  size_t *contour_end_points;
+
+  /* Pointer to the X axis point data.  */
+  sfnt_f26dot6 *restrict x_points;
+
+  /* Pointer to the X axis current point data.  */
+  sfnt_f26dot6 *restrict x_current;
+
+  /* Pointer to the Y axis point data.  */
+  sfnt_f26dot6 *restrict y_points;
+
+  /* Pointer to the Y axis current point data.  */
+  sfnt_f26dot6 *restrict y_current;
+
+  /* Pointer to the flags associated with this data.  */
+  unsigned char *flags;
+};
+
+enum
+  {
+    /* Bits 1 stands for X_SHORT_VECTOR on disk and in the tables, but
+       this representation is not useful in memory.  Inside an
+       instructed glyph, this bit is repurposed to mean that the
+       corresponding point is a phantom point.  */
+    SFNT_POINT_PHANTOM = (1 << 1),
+    /* Bits 7 and 6 of a glyph point's flags is reserved.  This scaler
+       uses it to mean that the point has been touched in one axis or
+       another.  */
+    SFNT_POINT_TOUCHED_X = (1 << 7),
+    SFNT_POINT_TOUCHED_Y = (1 << 6),
+    SFNT_POINT_TOUCHED_BOTH = (SFNT_POINT_TOUCHED_X
+                              | SFNT_POINT_TOUCHED_Y),
+  };
+
+/* This is needed because `round' below needs an interpreter
+   argument.  */
+struct sfnt_interpreter;
+
+struct sfnt_graphics_state
+{
+  /* Pointer to the function used for rounding.  This function is
+     asymmetric, so -0.5 rounds up to 0, not -1.  It is up to the
+     caller to handle negative values.
+
+     Value is undefined unless sfnt_validate_gs has been called, and
+     the second argument may be used to provide detailed rounding
+     information (``super rounding state''.)  */
+  sfnt_f26dot6 (*round) (sfnt_f26dot6, struct sfnt_interpreter *);
+
+  /* Pointer to the function used to project euclidean vectors onto
+     the projection vector.  Value is the magnitude of the projected
+     vector.  */
+  sfnt_f26dot6 (*project) (sfnt_f26dot6, sfnt_f26dot6,
+                          struct sfnt_interpreter *);
+
+  /* Pointer to the function used to project euclidean vectors onto
+     the dual projection vector.  Value is the magnitude of the
+     projected vector.  */
+  sfnt_f26dot6 (*dual_project) (sfnt_f26dot6, sfnt_f26dot6,
+                               struct sfnt_interpreter *);
+
+  /* Pointer to the function used to move specified points
+     along the freedom vector by a distance specified in terms
+     of the projection vector.  */
+  void (*move) (sfnt_f26dot6 *restrict,
+               sfnt_f26dot6 *restrict, size_t,
+               struct sfnt_interpreter *,
+               sfnt_f26dot6, unsigned char *);
+
+  /* Dot product between the freedom and the projection vectors.  */
+  sfnt_f2dot14 vector_dot_product;
+
+  /* Controls whether the sign of control value table entries will be
+     changed to match the sign of the actual distance measurement with
+     which it is compared.  Setting auto flip to TRUE makes it
+     possible to control distances measured with or against the
+     projection vector with a single control value table entry. When
+     auto flip is set to FALSE, distances must be measured with the
+     projection vector.  */
+  bool auto_flip;
+
+  /* Limits the regularizing effects of control value table entries to
+     cases where the difference between the table value and the
+     measurement taken from the original outline is sufficiently
+     small.  */
+  sfnt_f26dot6 cvt_cut_in;
+
+  /* Establishes the base value used to calculate the range of point
+     sizes to which a given DELTAC[] or DELTAP[] instruction will
+     apply.  The formulas given below are used to calculate the range
+     of the various DELTA instructions.
+
+     DELTAC1 DELTAP1 (delta_base) through (delta_base + 15)
+     DELTAC2 DELTAP2 (delta_base + 16) through (delta_base + 31)
+     DELTAC3 DELTAP3 (delta_base + 32) through (delta_base + 47)
+
+     Please keep this documentation in sync with the TrueType
+     reference manual.  */
+  unsigned short delta_base;
+
+  /* Determines the range of movement and smallest magnitude of
+     movement (the step) in a DELTAC[] or DELTAP[] instruction.
+     Changing the value of the delta shift makes it possible to trade
+     off fine control of point movement for range of movement.  A low
+     delta shift favors range of movement over fine control.  A high
+     delta shift favors fine control over range of movement.  The step
+     has the value 1/2 to the power delta shift.  The range of
+     movement is calculated by taking the number of steps allowed (16)
+     and multiplying it by the step.
+
+     The legal range for delta shift is zero through six.  Negative
+     values are illegal.  */
+  unsigned short delta_shift;
+
+  /* A second projection vector set to a line defined by the original
+     outline location of two points.  The dual projection vector is
+     used when it is necessary to measure distances from the scaled
+     outline before any instructions were executed.  */
+  struct sfnt_unit_vector dual_projection_vector;
+
+  /* A unit vector that establishes an axis along which points can
+     move.  */
+  struct sfnt_unit_vector freedom_vector;
+
+  /* Makes it possible to turn off instructions under some
+     circumstances.  When flag 1 is set, changes to the graphics state
+     made in the control value program will be ignored.  When flag is
+     1, grid fitting instructions will be ignored.  */
+  unsigned char instruct_control;
+
+  /* Makes it possible to repeat certain instructions a designated
+     number of times.  The default value of one assures that unless
+     the value of loop is altered, these instructions will execute one
+     time.  */
+  unsigned short loop;
+
+  /* Establishes the smallest possible value to which a distance will
+     be rounded.  */
+  sfnt_f26dot6 minimum_distance;
+
+  /* A unit vector whose direction establishes an axis along which
+     distances are measured.  */
+  struct sfnt_unit_vector projection_vector;
+
+  /* Determines the manner in which values are rounded. Can be set to
+     a number of predefined states or to a customized state with the
+     SROUND or S45ROUND instructions.  */
+  int round_state;
+
+  /* Reference points.  These reference point numbers, which together
+     with a zone designation, specify a point in either the glyph zone
+     or the twilight zone.  */
+  uint16_t rp0, rp1, rp2;
+
+  /* Flags which determine whether the interpreter will activate
+     dropout control for the current glyph.  */
+  int scan_control;
+
+  /* The distance difference below which the interpreter will replace
+     a CVT distance or an actual distance in favor of the single width
+     value.  */
+  sfnt_f26dot6 sw_cut_in;
+
+  /* The value used in place of the control value table distance or
+     the actual distance value when the difference between that
+     distance and the single width value is less than the single width
+     cut-in.  */
+  sfnt_f26dot6 single_width_value;
+
+  /* Zone pointers, which reference a zone.  */
+  int zp0, zp1, zp2;
+};
+
+struct sfnt_interpreter
+{
+  /* The number of elements in the stack.  */
+  uint16_t max_stack_elements;
+
+  /* The number of instructions in INSTRUCTIONS.  */
+  uint16_t num_instructions;
+
+  /* Size of the storage area.  */
+  uint16_t storage_size;
+
+  /* Size of the function definition area.  */
+  uint16_t function_defs_size;
+
+  /* Size of the instruction definition area.  */
+  uint16_t instruction_defs_size;
+
+  /* Size of the twilight zone.  */
+  uint16_t twilight_zone_size;
+
+  /* The instruction pointer.  This points to the instruction
+     currently being executed.  */
+  int IP;
+
+  /* The current scale.  */
+  sfnt_fixed scale;
+
+  /* The current ppem and point size.  */
+  int ppem, point_size;
+
+  /* The execution stack.  This has at most max_stack_elements
+     elements.  */
+  uint32_t *stack;
+
+  /* Pointer past the top of the stack.  */
+  uint32_t *SP;
+
+  /* The size of the control value table.  */
+  size_t cvt_size;
+
+  /* Pointer to instructions currently being executed.  */
+  unsigned char *restrict instructions;
+
+  /* The twilight zone.  May not be NULL.  */
+  sfnt_f26dot6 *restrict twilight_x, *restrict twilight_y;
+
+  /* The original X positions of points in the twilight zone.  */
+  sfnt_f26dot6 *restrict twilight_original_x;
+
+  /* The original Y positions of points in the twilight zone.
+
+     Apple does not directly say whether or not points in the twilight
+     zone can have their original positions changed.  But this is
+     implied by ``create points in the twilight zone''.  */
+  sfnt_f26dot6 *restrict twilight_original_y;
+
+  /* The scaled outlines being manipulated.  May be NULL.  */
+  struct sfnt_interpreter_zone *glyph_zone;
+
+  /* The glyph advance width.  Value is undefined unless GLYPH_ZONE is
+     set.  */
+  sfnt_f26dot6 advance_width;
+
+  /* The storage area.  */
+  uint32_t *storage;
+
+  /* Control value table values.  */
+  sfnt_f26dot6 *cvt;
+
+  /* Function definitions.  */
+  struct sfnt_interpreter_definition *function_defs;
+
+  /* Instruction definitions.  */
+  struct sfnt_interpreter_definition *instruction_defs;
+
+  /* Interpreter registers.  */
+  struct sfnt_graphics_state state;
+
+  /* Detailed rounding state used when state.round_state indicates
+     that fine grained rounding should be used.
+
+     PERIOD says how often a round value occurs, for numbers
+     increasing from PHASE to infinity.
+
+     THRESHOLD says when to round a value between two increasing
+     periods towards the larger period.  */
+  sfnt_f26dot6 period, phase, threshold;
+
+  /* The depth of any ongoing calls.  */
+  int call_depth;
+
+  /* Jump buffer for traps.  */
+  jmp_buf trap;
+
+  /* What was the trap.  */
+  const char *trap_reason;
+
+  /* Number of variation axes provided by this distortable font.  */
+  int n_axis;
+
+  /* Normalized axis coordinates set for this distortable font.  */
+  sfnt_fixed *norm_coords;
+
+#ifdef TEST
+  /* If non-NULL, function called before each instruction is
+     executed.  */
+  void (*run_hook) (struct sfnt_interpreter *);
+
+  /* If non-NULL, function called before each stack element is
+     pushed.  */
+  void (*push_hook) (struct sfnt_interpreter *, uint32_t);
+
+  /* If non-NULL, function called before each stack element is
+     popped.  */
+  void (*pop_hook) (struct sfnt_interpreter *, uint32_t);
+#endif
+};
+
+
+
+/* Glyph hinting.  */
+
+/* Structure describing a single scaled and fitted outline.  */
+
+struct sfnt_instructed_outline
+{
+  /* The number of points in this contour, including the two phantom
+     points at the end.  */
+  size_t num_points;
+
+  /* The number of contours in this outline.  */
+  size_t num_contours;
+
+  /* The end points of each contour.  */
+  size_t *contour_end_points;
+
+  /* The points of each contour, with two additional phantom points at
+     the end.  */
+  sfnt_f26dot6 *restrict x_points, *restrict y_points;
+
+  /* The flags of each point.  */
+  unsigned char *flags;
+};
+
+
+
+/* Functions used to read tables used by the TrueType interpreter.  */
+
+#ifndef TEST
+
+#define PROTOTYPE int, struct sfnt_offset_subtable *
+
+extern struct sfnt_cvt_table *sfnt_read_cvt_table (PROTOTYPE);
+extern struct sfnt_fpgm_table *sfnt_read_fpgm_table (PROTOTYPE);
+extern struct sfnt_prep_table *sfnt_read_prep_table (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE                              \
+  struct sfnt_maxp_table *,                    \
+  struct sfnt_cvt_table *,                     \
+  struct sfnt_head_table *,                    \
+  struct sfnt_fvar_table *,                    \
+  int, int
+
+extern struct sfnt_interpreter *sfnt_make_interpreter (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE                              \
+  struct sfnt_interpreter *,                   \
+  struct sfnt_fpgm_table *
+
+extern const char *sfnt_interpret_font_program (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE                              \
+  struct sfnt_interpreter *,                   \
+  struct sfnt_prep_table *,                    \
+  struct sfnt_graphics_state *
+
+extern const char *sfnt_interpret_control_value_program (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE struct sfnt_instructed_outline *
+
+extern struct sfnt_glyph_outline *sfnt_build_instructed_outline (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE                              \
+  struct sfnt_glyph *,                         \
+  struct sfnt_interpreter *,                   \
+  struct sfnt_glyph_metrics *,                 \
+  struct sfnt_instructed_outline **
+
+extern const char *sfnt_interpret_simple_glyph (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE                              \
+  struct sfnt_glyph *,                         \
+  struct sfnt_interpreter *,                   \
+  struct sfnt_graphics_state *,                        \
+  sfnt_get_glyph_proc,                         \
+  sfnt_free_glyph_proc,                                \
+  struct sfnt_hmtx_table *,                    \
+  struct sfnt_hhea_table *,                    \
+  struct sfnt_maxp_table *,                    \
+  struct sfnt_glyph_metrics *,                 \
+  void *,                                      \
+  struct sfnt_instructed_outline **
+
+extern const char *sfnt_interpret_compound_glyph (PROTOTYPE);
+
+#undef PROTOTYPE
+
+
+
+extern void sfnt_vary_interpreter (struct sfnt_interpreter *,
+                                  struct sfnt_blend *);
+
+#endif /* TEST */
+
+
+
+#endif /* _SFNT_H_ */
diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c
new file mode 100644
index 00000000000..de2a9253b57
--- /dev/null
+++ b/src/sfntfont-android.c
@@ -0,0 +1,793 @@
+/* sfnt format font driver for Android.
+
+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, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#include <config.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __aarch64__
+#include <arm_neon.h>
+#endif
+
+#include <android/api-level.h>
+#include <android/log.h>
+
+#include "androidterm.h"
+#include "sfntfont.h"
+#include "pdumper.h"
+#include "blockinput.h"
+#include "android.h"
+
+/* Structure describing a temporary buffer.  */
+
+struct sfntfont_android_scanline_buffer
+{
+  /* Size of this buffer.  */
+  size_t buffer_size;
+
+  /* Pointer to the buffer data.  */
+  void *buffer_data;
+};
+
+/* Array of directories to search for system fonts.  */
+static char *system_font_directories[] =
+  {
+    (char *) "/system/fonts",
+    (char *) "/product/fonts",
+    /* This should be filled in by init_sfntfont_android.  */
+    (char[PATH_MAX]) { },
+  };
+
+/* The font cache.  */
+static Lisp_Object font_cache;
+
+/* The scanline buffer.  */
+static struct sfntfont_android_scanline_buffer scanline_buffer;
+
+/* The largest size of the scanline buffer since the last window
+   update.  */
+static size_t max_scanline_buffer_size;
+
+
+
+/* Return a temporary buffer for storing scan lines.
+   Set BUFFER to the buffer upon success.  */
+
+#ifndef __aarch64__
+
+#define GET_SCANLINE_BUFFER(buffer, height, stride)            \
+  do                                                           \
+    {                                                          \
+      size_t _size;                                            \
+                                                               \
+      if (INT_MULTIPLY_WRAPV (height, stride, &_size))         \
+       memory_full (SIZE_MAX);                                 \
+                                                               \
+      if (_size < MAX_ALLOCA)                                  \
+       (buffer) = alloca (_size);                              \
+      else                                                     \
+       {                                                       \
+         if (_size > scanline_buffer.buffer_size)              \
+           {                                                   \
+             (buffer)                                          \
+               = scanline_buffer.buffer_data                   \
+               = xrealloc (scanline_buffer.buffer_data,        \
+                           _size);                             \
+             scanline_buffer.buffer_size = _size;              \
+           }                                                   \
+         else if (_size <= scanline_buffer.buffer_size)        \
+           (buffer) = scanline_buffer.buffer_data;             \
+         /* This is unreachable but clang says it is.  */      \
+         else                                                  \
+           emacs_abort ();                                     \
+                                                               \
+         max_scanline_buffer_size                              \
+           = max (_size, max_scanline_buffer_size);            \
+       }                                                       \
+    } while (false);
+
+#else
+
+#define GET_SCANLINE_BUFFER(buffer, height, stride)            \
+  do                                                           \
+    {                                                          \
+      size_t _size;                                            \
+      void *_temp;                                             \
+                                                               \
+      if (INT_MULTIPLY_WRAPV (height, stride, &_size))         \
+       memory_full (SIZE_MAX);                                 \
+                                                               \
+      if (_size > scanline_buffer.buffer_size)                 \
+       {                                                       \
+         if (posix_memalign (&_temp, 16, _size))               \
+           memory_full (_size);                                \
+         free (scanline_buffer.buffer_data);                   \
+         (buffer)                                              \
+           = scanline_buffer.buffer_data                       \
+           = _temp;                                            \
+         scanline_buffer.buffer_size = _size;                  \
+       }                                                       \
+      else if (_size <= scanline_buffer.buffer_size)           \
+       (buffer) = scanline_buffer.buffer_data;                 \
+      /* This is unreachable but clang says it is.  */         \
+      else                                                     \
+       emacs_abort ();                                         \
+                                                               \
+      max_scanline_buffer_size                                 \
+      = max (_size, max_scanline_buffer_size);                 \
+    } while (false);
+
+#endif
+
+
+
+/* Scale each of the four packed bytes in P in the low 16 bits of P by
+   SCALE.  Return the result.
+
+   SCALE is an integer between 0 and 256.  */
+
+static unsigned int
+sfntfont_android_scale32 (unsigned int scale, unsigned int p)
+{
+  uint32_t ag, rb;
+  uint32_t scaled_ag, scaled_rb;
+
+  ag = (p & 0xFF00FF00) >> 8;
+  rb = (p & 0x00FF00FF);
+
+  scaled_ag = (scale * ag) & 0xFF00FF00;
+  scaled_rb = (scale * rb) >> 8 & 0x00FF00FF;
+
+  return scaled_ag | scaled_rb;
+}
+
+static unsigned int
+sfntfont_android_mul8x2 (unsigned int a8, unsigned int b32)
+{
+  unsigned int i;
+
+  b32 &= 0xff00ff;
+  i = a8 * b32 + 0x800080;
+
+  return (i + ((i >> 8) & 0xff00ff)) >> 8 & 0xff00ff;
+}
+
+#define U255TO256(x) ((unsigned short) (x) + ((x) >> 7))
+
+/* Blend two pixels SRC and DST without utilizing any control flow.
+   Both SRC and DST are expected to be in premultiplied ABGB8888
+   format.  Value is returned in premultiplied ARGB8888 format.  */
+
+static unsigned int
+sfntfont_android_blend (unsigned int src, unsigned int dst)
+{
+  unsigned int a, br_part, ag_part, both;
+
+  a = (src >> 24);
+  br_part = sfntfont_android_mul8x2 (255 - a, dst);
+  ag_part = sfntfont_android_mul8x2 (255 - a, dst >> 8) << 8;
+
+  both = ag_part | br_part;
+
+  /* This addition need not be saturating because both has already
+     been multiplied by 255 - a.  */
+  return both + src;
+}
+
+#ifdef __aarch64__
+
+/* Like U255TO256, but operates on vectors.  */
+
+static uint16x8_t
+sfntfont_android_u255to256 (uint8x8_t in)
+{
+  return vaddl_u8 (vshr_n_u8 (in, 7), in);
+}
+
+/* Use processor features to efficiently composite four pixels at SRC
+   to DST.  */
+
+static void
+sfntfont_android_over_8888_1 (unsigned int *src, unsigned int *dst)
+{
+  uint8x8_t alpha;
+  uint16x8_t alpha_c16, v1, v3, v4;
+  uint8x8_t b, g, r, a, v2, v5;
+  uint8x8x4_t _src, _dst;
+
+  /* Pull in src and dst.
+
+     This loads bytes, not words, so little endian ABGR becomes
+     RGBA.  */
+  _src = vld4_u8 ((const uint8_t *) src);
+  _dst = vld4_u8 ((const uint8_t *) dst);
+
+  /* Load constants.  */
+  v4 = vdupq_n_u16 (256);
+  v5 = vdup_n_u8 (0);
+
+  /* Load src alpha.  */
+  alpha = _src.val[3];
+
+  /* alpha_c16 = 256 - 255TO256 (alpha).  */
+  alpha_c16 = sfntfont_android_u255to256 (alpha);
+  alpha_c16 = vsubq_u16 (v4, alpha_c16);
+
+  /* Cout = Csrc + Cdst * alpha_c.  */
+  v1 = vaddl_u8 (_dst.val[2], v5);
+  v2 = _src.val[2];
+  v3 = vmulq_u16 (v1, alpha_c16);
+  b = vqadd_u8 (v2, vshrn_n_u16 (v3, 8));
+
+  v1 = vaddl_u8 (_dst.val[1], v5);
+  v2 = _src.val[1];
+  v3 = vmulq_u16 (v1, alpha_c16);
+  g = vqadd_u8 (v2, vshrn_n_u16 (v3, 8));
+
+  v1 = vaddl_u8 (_dst.val[0], v5);
+  v2 = _src.val[0];
+  v3 = vmulq_u16 (v1, alpha_c16);
+  r = vqadd_u8 (v2, vshrn_n_u16 (v3, 8));
+
+#if 0
+  /* Aout = Asrc + Adst * alpha_c.  */
+  v1 = vaddl_u8 (_dst.val[3], v5);
+  v2 = _src.val[3];
+  v3 = vmulq_u16 (v1, alpha_c16);
+  a = vqadd_u8 (v2, vshrn_n_u16 (v3, 8));
+#else
+  /* We know that Adst is always 1, so Asrc + Adst * (1 - Asrc) is
+     always 1.  */
+  a = vdup_n_u8 (255);
+#endif
+
+  /* Store back in dst.  */
+  _dst.val[0] = r;
+  _dst.val[1] = g;
+  _dst.val[2] = b;
+  _dst.val[3] = a;
+  vst4_u8 ((uint8_t *) dst, _dst);
+}
+
+/* Use processor features to efficiently composite the buffer at SRC
+   to DST.  Composite at most MAX - SRC words.
+
+   If either SRC or DST are not yet properly aligned, value is 1.
+   Otherwise, value is 0, and *X is incremented to the start of any
+   trailing data which could not be composited due to data alignment
+   constraints.  */
+
+static int
+sfntfont_android_over_8888 (unsigned int *src, unsigned int *dst,
+                           unsigned int *max, unsigned int *x)
+{
+  size_t i;
+  ptrdiff_t how_much;
+  void *s, *d;
+
+  /* Figure out how much can be composited by this loop.  */
+  how_much = (max - src) & ~7;
+
+  /* Return if there is not enough to vectorize.  */
+  if (!how_much)
+    return 1;
+
+  /* Now increment *X by that much so the containing loop can process
+     the remaining pixels one-by-one.  */
+
+  *x += how_much;
+
+  for (i = 0; i < how_much; i += 8)
+    {
+      s = (src + i);
+      d = (dst + i);
+
+      sfntfont_android_over_8888_1 (s, d);
+    }
+
+  return 0;
+}
+
+#endif
+
+/* Composite the bitmap described by BUFFER, STRIDE and TEXT_RECTANGLE
+   onto the native-endian ABGR8888 bitmap described by DEST and
+   BITMAP_INFO.  RECT is the subset of the bitmap to composite.  */
+
+static void
+sfntfont_android_composite_bitmap (unsigned char *restrict buffer,
+                                  size_t stride,
+                                  unsigned char *restrict dest,
+                                  AndroidBitmapInfo *bitmap_info,
+                                  struct android_rectangle *text_rectangle,
+                                  struct android_rectangle *rect)
+{
+  unsigned int *src_row;
+  unsigned int *dst_row;
+  unsigned int i, src_y, x, src_x, max_x, dst_x;
+#ifdef __aarch64__
+  unsigned int lim_x;
+#endif
+
+  if ((intptr_t) dest & 3 || bitmap_info->stride & 3)
+    /* This shouldn't be possible as Android is supposed to align the
+       bitmap to at least a 4 byte boundary.  */
+    emacs_abort ();
+  else
+    {
+      for (i = 0; i < rect->height; ++i)
+       {
+         if (i + rect->y >= bitmap_info->height)
+           /* Done.  */
+           return;
+
+         src_y = i + (rect->y - text_rectangle->y);
+
+         if (src_y > text_rectangle->height)
+           /* Huh? */
+           return;
+
+         src_row = (unsigned int *) ((buffer + src_y * stride));
+         dst_row = (unsigned int *) (dest + ((i + rect->y)
+                                             * bitmap_info->stride));
+
+         /* Figure out where the loop below should end.  */
+         max_x = min (rect->width, bitmap_info->width - rect->x);
+
+         /* Keep this loop simple! */
+         for (x = 0; x < max_x; ++x)
+           {
+             src_x = x + (rect->x - text_rectangle->x);
+             dst_x = x + rect->x;
+
+#ifdef __aarch64__
+             /* This is the largest value of src_x.  */
+             lim_x = max_x + (rect->x - text_rectangle->x);
+
+             if (!sfntfont_android_over_8888 (src_row + src_x,
+                                              dst_row + dst_x,
+                                              src_row + lim_x,
+                                              &x))
+               {
+                 /* Decrement X by one so the for loop can increment
+                    it again.  */
+                 x--;
+                 continue;
+               }
+#endif
+               dst_row[dst_x]
+                 = sfntfont_android_blend (src_row[src_x],
+                                           dst_row[dst_x]);
+           }
+       }
+    }
+}
+
+/* Calculate the union containing both A and B, both boxes.  Place the
+   result in RESULT.  */
+
+static void
+sfntfont_android_union_boxes (struct gui_box a, struct gui_box b,
+                             struct gui_box *result)
+{
+  result->x1 = min (a.x1, b.x1);
+  result->y1 = min (a.y1, b.y1);
+  result->x2 = max (a.x2, b.x2);
+  result->y2 = max (a.y2, b.y2);
+}
+
+/* Draw the specified glyph rasters from FROM to TO on behalf of S,
+   using S->gc.  Fill the background if WITH_BACKGROUND is true.
+
+   See init_sfntfont_vendor and sfntfont_draw for more details.  */
+
+static void
+sfntfont_android_put_glyphs (struct glyph_string *s, int from,
+                            int to, int x, int y, bool with_background,
+                            struct sfnt_raster **rasters,
+                            int *x_coords)
+{
+  struct android_rectangle background, text_rectangle, rect;
+  struct gui_box text, character;
+  unsigned int *buffer, *row;
+  unsigned char *restrict raster_row;
+  size_t stride, i;
+  AndroidBitmapInfo bitmap_info;
+  unsigned char *bitmap_data;
+  jobject bitmap;
+  int left, top, temp_y;
+  unsigned int prod, raster_y;
+  unsigned long foreground, back_pixel, rb;
+
+  if (!s->gc->num_clip_rects)
+    /* Clip region is empty.  */
+    return;
+
+  if (from == to)
+    /* Nothing to draw.  */
+    return;
+
+  /* Swizzle the foreground and background in s->gc into BGR, then add
+     an alpha channel.  */
+  foreground = s->gc->foreground;
+  back_pixel = s->gc->background;
+  rb = foreground & 0x00ff00ff;
+  foreground &= ~0x00ff00ff;
+  foreground |= rb >> 16 | rb << 16 | 0xff000000;
+  rb = back_pixel & 0x00ff00ff;
+  back_pixel &= ~0x00ff00ff;
+  back_pixel |= rb >> 16 | rb << 16 | 0xff000000;
+
+  prepare_face_for_display (s->f, s->face);
+
+  /* Build the scanline buffer.  Figure out the bounds of the
+     background.  */
+  memset (&background, 0, sizeof background);
+
+  if (with_background)
+    {
+      background.x = x;
+      background.y = y - FONT_BASE (s->font);
+      background.width = s->width;
+      background.height = FONT_HEIGHT (s->font);
+    }
+
+  /* Now figure out the bounds of the text.  */
+
+  if (rasters[0])
+    {
+      text.x1 = x_coords[0] + rasters[0]->offx;
+      text.x2 = text.x1 + rasters[0]->width;
+      text.y1 = y - rasters[0]->height - rasters[0]->offy;
+      text.y2 = y - rasters[0]->offy;
+    }
+  else
+    memset (&text, 0, sizeof text);
+
+  for (i = 1; i < to - from; ++i)
+    {
+      /* See if text has to be extended.  */
+
+      if (!rasters[i])
+       continue;
+
+      character.x1 = x_coords[i] + rasters[i]->offx;
+      character.x2 = character.x1 + rasters[i]->width;
+      character.y1 = y - rasters[i]->height - rasters[i]->offy;
+      character.y2 = y - rasters[i]->offy;
+
+      sfntfont_android_union_boxes (text, character, &text);
+    }
+
+  /* Union the background rect with the text rectangle.  */
+  text_rectangle.x = text.x1;
+  text_rectangle.y = text.y1;
+  text_rectangle.width = text.x2 - text.x1;
+  text_rectangle.height = text.y2 - text.y1;
+  gui_union_rectangles (&background, &text_rectangle,
+                       &text_rectangle);
+
+  /* Allocate enough to hold text_rectangle.height, aligned to 8 (or
+     16) bytes.  Then fill it with the background.  */
+#ifndef __aarch64__
+  stride = ((text_rectangle.width * sizeof *buffer) + 7) & ~7;
+#else
+  stride = ((text_rectangle.width * sizeof *buffer) + 15) & ~15;
+#endif
+  GET_SCANLINE_BUFFER (buffer, text_rectangle.height, stride);
+
+  /* Try to optimize out this memset if the background rectangle
+     contains the whole text rectangle.  */
+
+  if (!with_background || memcmp (&background, &text_rectangle,
+                                 sizeof text_rectangle))
+    memset (buffer, 0, text_rectangle.height * stride);
+
+  if (with_background)
+    {
+      /* Fill the background.  First, offset the background rectangle
+        to become relative from text_rectangle.x,
+        text_rectangle.y.  */
+      background.x = background.x - text_rectangle.x;
+      background.y = background.y - text_rectangle.y;
+      eassert (background.x >= 0 && background.y >= 0);
+
+      for (temp_y = background.y; (temp_y
+                                  < (background.y
+                                     + background.height));
+          ++temp_y)
+       {
+         row = (unsigned int *) ((unsigned char *) buffer
+                                 + stride * temp_y);
+
+         for (x = background.x; x < background.x + background.width; ++x)
+           row[x] = back_pixel;
+       }
+    }
+
+  /* Draw all the rasters onto the buffer.  */
+  for (i = 0; i < to - from; ++i)
+    {
+      if (!rasters[i])
+       continue;
+
+      /* Figure out the top and left of the raster relative to
+        text_rectangle.  */
+      left = x_coords[i] + rasters[i]->offx - text_rectangle.x;
+
+      /* Note that negative offy represents the part of the text that
+        lies below the baseline.  */
+      top = (y - (rasters[i]->height + rasters[i]->offy)
+            - text_rectangle.y);
+      eassert (left >= 0 && top >= 0);
+
+      /* Draw the raster onto the temporary bitmap using the
+        foreground color scaled by the alpha map.  */
+
+      for (raster_y = 0; raster_y < rasters[i]->height; ++raster_y)
+       {
+         row = (unsigned int *) ((unsigned char *) buffer
+                                 + stride * (raster_y + top));
+         raster_row = &rasters[i]->cells[raster_y * rasters[i]->stride];
+
+         for (x = 0; x < rasters[i]->width; ++x)
+           {
+             prod
+               = sfntfont_android_scale32 (U255TO256 (raster_row[x]),
+                                           foreground);
+             row[left + x]
+               = sfntfont_android_blend (prod, row[left + x]);
+           }
+       }
+    }
+
+  /* Lock the bitmap.  It must be unlocked later.  */
+  bitmap_data = android_lock_bitmap (FRAME_ANDROID_DRAWABLE (s->f),
+                                    &bitmap_info, &bitmap);
+
+  /* If locking the bitmap fails, just discard the data that was
+     allocated.  */
+  if (!bitmap_data)
+    return;
+
+  /* Loop over each clip rect in the GC.  */
+  eassert (bitmap_info.format == ANDROID_BITMAP_FORMAT_RGBA_8888);
+
+  if (s->gc->num_clip_rects > 0)
+    {
+      for (i = 0; i < s->gc->num_clip_rects; ++i)
+       {
+         if (!gui_intersect_rectangles (&s->gc->clip_rects[i],
+                                        &text_rectangle, &rect))
+           /* Outside the clip region.  */
+           continue;
+
+         /* Composite the intersection onto the buffer.  */
+         sfntfont_android_composite_bitmap ((unsigned char *) buffer,
+                                            stride, bitmap_data,
+                                            &bitmap_info,
+                                            &text_rectangle, &rect);
+       }
+    }
+  else /* gc->num_clip_rects < 0 */
+    sfntfont_android_composite_bitmap ((unsigned char *) buffer,
+                                      stride, bitmap_data,
+                                      &bitmap_info,
+                                      &text_rectangle,
+                                      &text_rectangle);
+
+  /* Release the bitmap.  */
+  AndroidBitmap_unlockPixels (android_java_env, bitmap);
+  ANDROID_DELETE_LOCAL_REF (bitmap);
+
+  /* Damage the window by the text rectangle.  */
+  android_damage_window (FRAME_ANDROID_DRAWABLE (s->f),
+                        &text_rectangle);
+
+#undef MAX_ALLOCA
+}
+
+
+
+/* Shrink the scanline buffer after a window update.  If
+   max_scanline_buffer_size is not zero, and is less than
+   scanline_buffer.buffer_size / 2, then resize the scanline buffer to
+   max_scanline_buffer_size.  */
+
+void
+sfntfont_android_shrink_scanline_buffer (void)
+{
+  if (!max_scanline_buffer_size)
+    return;
+
+  if (max_scanline_buffer_size
+      < scanline_buffer.buffer_size / 2)
+    {
+      scanline_buffer.buffer_size
+       = max_scanline_buffer_size;
+      scanline_buffer.buffer_data
+       = xrealloc (scanline_buffer.buffer_data,
+                   max_scanline_buffer_size);
+    }
+
+  max_scanline_buffer_size = 0;
+}
+
+
+
+/* Font driver definition.  */
+
+/* Return the font cache for this font driver.  F is ignored.  */
+
+static Lisp_Object
+sfntfont_android_get_cache (struct frame *f)
+{
+  return font_cache;
+}
+
+/* The Android sfntfont driver.  */
+const struct font_driver android_sfntfont_driver =
+  {
+    .type = LISPSYM_INITIALLY (Qsfnt_android),
+    .case_sensitive = true,
+    .get_cache = sfntfont_android_get_cache,
+    .list = sfntfont_list,
+    .match = sfntfont_match,
+    .draw = sfntfont_draw,
+    .open_font = sfntfont_open,
+    .close_font = sfntfont_close,
+    .encode_char = sfntfont_encode_char,
+    .text_extents = sfntfont_text_extents,
+    .list_family = sfntfont_list_family,
+    .get_variation_glyphs = sfntfont_get_variation_glyphs,
+
+#ifdef HAVE_HARFBUZZ
+    /* HarfBuzz support is enabled transparently on Android without
+       using a separate font driver.  */
+    .begin_hb_font = sfntfont_begin_hb_font,
+    .combining_capability = hbfont_combining_capability,
+    .shape = hbfont_shape,
+    .otf_capability = hbfont_otf_capability,
+#endif /* HAVE_HARFBUZZ */
+  };
+
+
+
+/* This is an ugly hack that should go away, but I can't think of
+   how.  */
+
+DEFUN ("android-enumerate-fonts", Fandroid_enumerate_fonts,
+       Sandroid_enumerate_fonts, 0, 0, 0,
+       doc: /* Enumerate fonts present on the system.
+
+Signal an error if fonts have already been enumerated.  This would
+normally have been done in C, but reading fonts require Lisp to be
+loaded before character sets are made available.  */)
+  (void)
+{
+  DIR *dir;
+  int i;
+  struct dirent *dirent;
+  char name[PATH_MAX * 2];
+  static bool enumerated;
+
+  if (enumerated)
+    error ("Fonts have already been enumerated");
+  enumerated = true;
+
+  block_input ();
+
+  /* Scan through each of the system font directories.  Enumerate each
+     font that looks like a TrueType font.  */
+  for (i = 0; i < ARRAYELTS (system_font_directories); ++i)
+    {
+      dir = opendir (system_font_directories[i]);
+
+      __android_log_print (ANDROID_LOG_VERBOSE, __func__,
+                          "Loading fonts from: %s",
+                          system_font_directories[i]);
+
+      if (!dir)
+       continue;
+
+      while ((dirent = readdir (dir)))
+       {
+         /* If it contains (not ends with!) with .ttf or .ttc, then
+            enumerate it.  */
+
+         if ((strstr (dirent->d_name, ".ttf")
+              || strstr (dirent->d_name, ".ttc"))
+             /* Ignore the non-variable Roboto font.  */
+             && (i != 0 || strcmp (dirent->d_name,
+                                   "RobotoStatic-Regular.ttf")))
+           {
+             sprintf (name, "%s/%s", system_font_directories[i],
+                      dirent->d_name);
+             sfnt_enum_font (name);
+           }
+       }
+
+      closedir (dir);
+    }
+
+  unblock_input ();
+
+  return Qnil;
+}
+
+
+
+static void
+syms_of_sfntfont_android_for_pdumper (void)
+{
+  init_sfntfont_vendor (Qsfnt_android, &android_sfntfont_driver,
+                       sfntfont_android_put_glyphs);
+  register_font_driver (&android_sfntfont_driver, NULL);
+}
+
+void
+init_sfntfont_android (void)
+{
+  if (!android_init_gui)
+    return;
+
+  /* Make sure to pick the right Sans Serif font depending on what
+     version of Android the device is running.  */
+  if (android_get_current_api_level () >= 15)
+    Vsfnt_default_family_alist
+      = list3 (Fcons (build_string ("Monospace"),
+                     build_string ("Droid Sans Mono")),
+              /* Android doesn't come with a Monospace Serif font, so
+                 this will have to do.  */
+              Fcons (build_string ("Monospace Serif"),
+                     build_string ("Droid Sans Mono")),
+              Fcons (build_string ("Sans Serif"),
+                     build_string ("Roboto")));
+  else
+    Vsfnt_default_family_alist
+      = list3 (Fcons (build_string ("Monospace"),
+                     build_string ("Droid Sans Mono")),
+              Fcons (build_string ("Monospace Serif"),
+                     build_string ("Droid Sans Mono")),
+              Fcons (build_string ("Sans Serif"),
+                     build_string ("Droid Sans")));
+
+  /* Set up the user fonts directory.  This directory is ``fonts'' in
+     the Emacs files directory.  */
+  snprintf (system_font_directories[2], PATH_MAX, "%s/fonts",
+           android_get_home_directory ());
+}
+
+void
+syms_of_sfntfont_android (void)
+{
+  DEFSYM (Qsfnt_android, "sfnt-android");
+  DEFSYM (Qandroid_enumerate_fonts, "android-enumerate-fonts");
+  Fput (Qandroid, Qfont_driver_superseded_by, Qsfnt_android);
+
+  font_cache = list (Qnil);
+  staticpro (&font_cache);
+
+  defsubr (&Sandroid_enumerate_fonts);
+
+  pdumper_do_now_and_after_load (syms_of_sfntfont_android_for_pdumper);
+}
diff --git a/src/sfntfont.c b/src/sfntfont.c
new file mode 100644
index 00000000000..6927b185721
--- /dev/null
+++ b/src/sfntfont.c
@@ -0,0 +1,3878 @@
+/* sfnt format font driver for GNU Emacs.
+
+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, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#include <config.h>
+
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "lisp.h"
+
+#include "blockinput.h"
+#include "charset.h"
+#include "coding.h"
+#include "font.h"
+#include "frame.h"
+#include "math.h"
+#include "sfnt.h"
+#include "sfntfont.h"
+
+#ifdef HAVE_HARFBUZZ
+#include <hb.h>
+#include <hb-ot.h>
+#endif /* HAVE_HARFBUZZ */
+
+/* For FRAME_FONT.  */
+#include TERM_HEADER
+
+/* Generic font driver for sfnt-based fonts (currently TrueType, but
+   it would be easy to add CFF support in the future with a PostScript
+   renderer.)
+
+   This is not a complete font driver.  Hooks must be supplied by the
+   platform implementer to draw glyphs.  */
+
+
+
+/* Tables associated with each font, be it distortable or not.  This
+   allows different font objects sharing the same underlying font file
+   to share tables.  */
+
+struct sfnt_font_tables
+{
+  /* Various tables required to use the font.  */
+  struct sfnt_cmap_table *cmap;
+  struct sfnt_hhea_table *hhea;
+  struct sfnt_maxp_table *maxp;
+  struct sfnt_head_table *head;
+  struct sfnt_hmtx_table *hmtx;
+  struct sfnt_glyf_table *glyf;
+  struct sfnt_loca_table_short *loca_short;
+  struct sfnt_loca_table_long *loca_long;
+  struct sfnt_prep_table *prep;
+  struct sfnt_fpgm_table *fpgm;
+  struct sfnt_cvt_table *cvt;
+  struct sfnt_fvar_table *fvar;
+  struct sfnt_avar_table *avar;
+  struct sfnt_gvar_table *gvar;
+  struct sfnt_cvar_table *cvar;
+
+  /* The selected character map.  */
+  struct sfnt_cmap_encoding_subtable_data *cmap_data;
+
+  /* Data identifying that character map.  */
+  struct sfnt_cmap_encoding_subtable cmap_subtable;
+
+  /* The UVS context.  */
+  struct sfnt_uvs_context *uvs;
+
+#ifdef HAVE_MMAP
+  /* Whether or not the glyph table has been mmapped.  */
+  bool glyf_table_mapped;
+#endif /* HAVE_MMAP */
+
+#ifdef HAVE_HARFBUZZ
+  /* File descriptor associated with this font.  */
+  int fd;
+
+  /* The table directory of the font file.  */
+  struct sfnt_offset_subtable *directory;
+#endif /* HAVE_HARFBUZZ */
+};
+
+/* Description of a font that hasn't been opened.  */
+
+struct sfnt_font_desc
+{
+  /* Next font in this list.  */
+  struct sfnt_font_desc *next;
+
+  /* Family name of the font.  */
+  Lisp_Object family;
+
+  /* Style name of the font.  */
+  Lisp_Object style;
+
+  /* Designer (foundry) of the font.  */
+  Lisp_Object designer;
+
+  /* Style tokens that could not be parsed.  */
+  Lisp_Object adstyle;
+
+  /* List of design languages.  */
+  Lisp_Object languages;
+
+  /* Font registry that this font supports.  */
+  Lisp_Object registry;
+
+  /* Vector of instances.  Each element is another of the instance's
+     `style', `adstyle', and numeric width, weight, and slant.  May be
+     nil.  */
+  Lisp_Object instances;
+
+  /* Numeric width, weight, slant and spacing.  */
+  int width, weight, slant, spacing;
+
+  /* Path to the font file.  */
+  char *path;
+
+  /* char table consisting of characters already known to be
+     present in the font.  */
+  Lisp_Object char_cache;
+
+  /* Whether or not the character map can't be used by Emacs.  */
+  bool cmap_invalid;
+
+  /* The header of the cmap being used.  May be invalid, in which case
+     platform_id will be 500.  */
+  struct sfnt_cmap_encoding_subtable subtable;
+
+  /* The offset of the table directory within PATH.  */
+  off_t offset;
+
+  /* The number of glyphs in this font.  Used to catch invalid cmap
+     tables.  This is actually the number of glyphs - 1.  */
+  int num_glyphs;
+
+  /* The number of references to the font tables below.  */
+  int refcount;
+
+  /* List of font tables.  */
+  struct sfnt_font_tables *tables;
+};
+
+/* List of fonts.  */
+
+static struct sfnt_font_desc *system_fonts;
+
+/* Font enumeration and matching.  The sfnt driver assumes it can read
+   data from each font at startup.  It then reads the head, meta and
+   name tables to determine font data, and records the font in a list
+   of system fonts that is then matched against.  */
+
+/* Set up the coding system CODING to decode string data from the
+   given platform id ID and platform specific id PLATFORM_SPECIFIC_ID.
+   Value is 0 upon success, 1 upon failure.  */
+
+static int
+sfnt_setup_coding_system (enum sfnt_platform_id id, int platform_specific_id,
+                         struct coding_system *coding)
+{
+  Lisp_Object system;
+
+  system = Qnil;
+
+  /* Figure out what coding system to use.  */
+
+  switch (id)
+    {
+    case SFNT_PLATFORM_UNICODE:
+      system = Qutf_16be;
+      break;
+
+    case SFNT_PLATFORM_MACINTOSH:
+
+      if (platform_specific_id == SFNT_MACINTOSH_ROMAN)
+       system = Qmac_roman;
+      else
+       /* MULE doesn't support the rest... */
+       system = Qnil;
+
+      break;
+
+    case SFNT_PLATFORM_MICROSOFT:
+      system = Qutf_16be;
+
+      /* Not sure if this is right.  */
+      if (platform_specific_id == SFNT_MICROSOFT_BIG_FIVE)
+       system = Qchinese_big5;
+
+      break;
+
+    default:
+      system = Qnil;
+    }
+
+  if (NILP (system))
+    return 1;
+
+  setup_coding_system (system, coding);
+  return 0;
+}
+
+/* Globals used to communicate inside the condition-case wrapper.  */
+static struct coding_system *sfnt_font_coding;
+
+/* The src_object being encoded from.  This should be on the stack as
+   well, or it will get garbage collected.  */
+static Lisp_Object sfnt_font_src_object;
+
+/* From-position.  */
+static ptrdiff_t sfnt_font_from, sfnt_font_from_byte;
+
+/* To-position.  */
+static ptrdiff_t sfnt_font_to, sfnt_font_to_byte;
+
+/* Destination object.  Once again, this should also be on the
+   stack.  */
+static Lisp_Object sfnt_font_dst_object;
+
+/* Error flag.  Set to true if a signal was caught.  */
+static bool sfnt_font_signal;
+
+static Lisp_Object
+sfnt_safe_decode_coding_object_1 (void)
+{
+  decode_coding_object (sfnt_font_coding,
+                       sfnt_font_src_object,
+                       sfnt_font_from,
+                       sfnt_font_from_byte,
+                       sfnt_font_to,
+                       sfnt_font_to_byte,
+                       sfnt_font_dst_object);
+  return Qnil;
+}
+
+static Lisp_Object
+sfnt_safe_decode_coding_object_2 (Lisp_Object error)
+{
+  sfnt_font_signal = true;
+
+  return Qnil;
+}
+
+/* Like decode_coding_object, but return 1 if a signal happens.  Value
+   is otherwise 0.  */
+
+static int
+sfnt_safe_decode_coding_object (struct coding_system *coding,
+                               Lisp_Object src_object,
+                               ptrdiff_t from, ptrdiff_t from_byte,
+                               ptrdiff_t to, ptrdiff_t to_byte,
+                               Lisp_Object dst_object)
+{
+  sfnt_font_coding = coding;
+  sfnt_font_src_object = src_object;
+  sfnt_font_from = from;
+  sfnt_font_from_byte = from_byte;
+  sfnt_font_to = to;
+  sfnt_font_to_byte = to_byte;
+  sfnt_font_dst_object = dst_object;
+  sfnt_font_signal = false;
+
+  internal_condition_case (sfnt_safe_decode_coding_object_1,
+                          Qt,
+                          sfnt_safe_decode_coding_object_2);
+
+  return (int) sfnt_font_signal;
+}
+
+/* Decode the specified string DATA.  The encoding is determined based
+   on PLATFORM_ID, PLATFORM_SPECIFIC_ID and LANGUAGE_ID.  Consult
+   sfnt.h and the TrueType Reference Manual for more details.  LENGTH
+   is the length of DATA in bytes.
+
+   Value is nil upon failure, else the decoded string.  */
+
+static Lisp_Object
+sfnt_decode_font_string (unsigned char *data, enum sfnt_platform_id id,
+                        int platform_specific_id, int language_id,
+                        size_t length)
+{
+  struct coding_system coding;
+
+  memset (&coding, 0, sizeof coding);
+  sfnt_setup_coding_system (id, platform_specific_id, &coding);
+  coding.mode |= CODING_MODE_SAFE_ENCODING;
+  coding.mode |= CODING_MODE_LAST_BLOCK;
+  /* Suppress producing escape sequences for composition.  */
+  coding.common_flags &= ~CODING_ANNOTATION_MASK;
+  coding.source = data;
+
+  if (sfnt_safe_decode_coding_object (&coding, Qnil, 0, 0,
+                                     length, length, Qt))
+    return Qnil;
+
+  return coding.dst_object;
+}
+
+/* Decode the family and style names from the name table NAME.  Return
+   0 and the family and style names upon success, else 1.  */
+
+static int
+sfnt_decode_family_style (struct sfnt_name_table *name,
+                         Lisp_Object *family, Lisp_Object *style)
+{
+  struct sfnt_name_record family_rec, style_rec;
+  unsigned char *family_data, *style_data;
+
+  family_data = sfnt_find_name (name, SFNT_NAME_FONT_FAMILY,
+                               &family_rec);
+  style_data = sfnt_find_name (name, SFNT_NAME_FONT_SUBFAMILY,
+                              &style_rec);
+
+  if (!family_data || !style_data)
+    return 1;
+
+  /* Now decode the data.  */
+  *family = sfnt_decode_font_string (family_data,
+                                    family_rec.platform_id,
+                                    family_rec.platform_specific_id,
+                                    family_rec.language_id,
+                                    family_rec.length);
+  *style = sfnt_decode_font_string (style_data,
+                                   style_rec.platform_id,
+                                   style_rec.platform_specific_id,
+                                   style_rec.language_id,
+                                   style_rec.length);
+
+  /* Return whether or not it was successful.  */
+  return (!NILP (*family) && !NILP (*style)) ? 0 : 1;
+}
+
+/* Decode the foundry names from the name table NAME.  Return the
+   foundry name, or nil upon failure.  */
+
+static Lisp_Object
+sfnt_decode_foundry_name (struct sfnt_name_table *name)
+{
+  struct sfnt_name_record designer_rec;
+  unsigned char *designer_data;
+
+  designer_data = sfnt_find_name (name, SFNT_NAME_DESIGNER,
+                                 &designer_rec);
+
+  if (!designer_data)
+    return Qnil;
+
+  return sfnt_decode_font_string (designer_data,
+                                 designer_rec.platform_id,
+                                 designer_rec.platform_specific_id,
+                                 designer_rec.language_id,
+                                 designer_rec.length);
+}
+
+/* Decode the name of the specified font INSTANCE using the given NAME
+   table.  Return the name of that instance, or nil upon failure.  */
+
+static Lisp_Object
+sfnt_decode_instance_name (struct sfnt_instance *instance,
+                          struct sfnt_name_table *name)
+{
+  struct sfnt_name_record name_rec;
+  unsigned char *name_data;
+
+  name_data = sfnt_find_name (name, instance->name_id,
+                             &name_rec);
+
+  if (!name_data)
+    return Qnil;
+
+  return sfnt_decode_font_string (name_data,
+                                 name_rec.platform_id,
+                                 name_rec.platform_specific_id,
+                                 name_rec.language_id,
+                                 name_rec.length);
+}
+
+struct sfnt_style_desc
+{
+  /* The C string to match against.  */
+  const char *c_string;
+
+  /* The value of the style field.  */
+  int value;
+};
+
+/* Array of style descriptions describing weight.  */
+static struct sfnt_style_desc sfnt_weight_descriptions[] =
+  {
+    { "thin", 0,               },
+    { "extralight", 40,                },
+    { "ultralight", 40,                },
+    { "light", 50,             },
+    { "demilight", 55,         },
+    { "semilight", 55,         },
+    { "book", 75,              },
+    { "medium", 100,           },
+    { "demibold", 180,         },
+    { "semibold", 180,         },
+    { "bold", 200,             },
+    { "extrabold", 205,                },
+    { "ultrabold", 205,                },
+    { "black", 210,            },
+    { "heavy", 210,            },
+    { "extrablack", 215,       },
+    { "ultrablack", 215,       },
+  };
+
+/* Array of style descriptions describing slant.  */
+static struct sfnt_style_desc sfnt_slant_descriptions[] =
+  {
+    { "italic", 200,   },
+    { "oblique", 210,  },
+  };
+
+/* Array of style descriptions describing width.  */
+static struct sfnt_style_desc sfnt_width_descriptions[] =
+  {
+    { "ultracondensed", 50,    },
+    { "extracondensed", 63,    },
+    { "condensed", 75,         },
+    { "semicondensed", 87,     },
+    { "semiexpanded", 113,     },
+    { "expanded", 125,         },
+    { "extraexpanded", 150,    },
+    { "ultraexpanded", 200,    },
+  };
+
+/* Figure out DESC->width, DESC->weight, DESC->slant and DESC->spacing
+   based on the style name passed as STYLE_NAME.
+
+   Also append any unknown tokens to DESC->adstyle.  */
+
+static void
+sfnt_parse_style (Lisp_Object style_name, struct sfnt_font_desc *desc)
+{
+  char *style, *single, *saveptr;
+  int i;
+  USE_SAFE_ALLOCA;
+
+  /* Fill in default values.  slant seems to not be consistent with
+     Fontconfig.  */
+  desc->weight = 80;
+  desc->slant = 100;
+  desc->width = 100;
+
+  /* Split the style into tokens delimited by spaces.  Attempt to find
+     a token specifying each of the weight, slant, or width attributes
+     using their respective descriptions arrays as a reference.  */
+
+  SAFE_ALLOCA_STRING (style, Fdowncase (style_name));
+  saveptr = NULL;
+
+  while ((single = strtok_r (style, " ", &saveptr)))
+    {
+      style = NULL;
+
+      if (desc->weight == 80)
+       {
+         /* Weight hasn't been found yet.  Scan through the weight
+            table.  */
+         for (i = 0; i < ARRAYELTS (sfnt_weight_descriptions); ++i)
+           {
+             if (!strcmp (sfnt_weight_descriptions[i].c_string,
+                          single))
+               {
+                 /* Weight found.  Continue on reading the slant and
+                    width.  */
+                 desc->weight = sfnt_weight_descriptions[i].value;
+                 goto next;
+               }
+           }
+       }
+
+      if (desc->slant == 100)
+       {
+         /* Slant hasn't been found yet.  Scan through the slant
+            table.  */
+         for (i = 0; i < ARRAYELTS (sfnt_slant_descriptions); ++i)
+           {
+             if (!strcmp (sfnt_slant_descriptions[i].c_string,
+                          single))
+               {
+                 /* Slant found.  Continue on reading the weight and
+                    width.  */
+                 desc->slant = sfnt_slant_descriptions[i].value;
+                 goto next;
+               }
+           }
+       }
+
+      if (desc->width == 100)
+       {
+         /* Width hasn't been found yet.  Scan through the width
+            table.  */
+         for (i = 0; i < ARRAYELTS (sfnt_width_descriptions); ++i)
+           {
+             if (!strcmp (sfnt_width_descriptions[i].c_string,
+                          single))
+               {
+                 /* Width found.  Continue on reading the slant and
+                    weight.  */
+                 desc->width = sfnt_width_descriptions[i].value;
+                 goto next;
+               }
+           }
+       }
+
+      /* This token is extraneous or was not recognized.  Capitalize
+        the first letter and set it as the adstyle.  */
+
+      if (strlen (single))
+       {
+         if (islower (single[0]))
+           single[0] = toupper (single[0]);
+
+         if (NILP (desc->adstyle))
+           desc->adstyle = build_string (single);
+         else
+           desc->adstyle = CALLN (Fconcat, desc->adstyle,
+                                  build_string (" "),
+                                  build_string (single));
+       }
+
+    next:
+      continue;
+    }
+
+  SAFE_FREE ();
+}
+
+/* Parse the list of design languages in META, a font metadata table,
+   and place the results in DESC->languages.  Do nothing if there is
+   no such metadata.  */
+
+static void
+sfnt_parse_languages (struct sfnt_meta_table *meta,
+                     struct sfnt_font_desc *desc)
+{
+  char *data, *metadata, *tag;
+  struct sfnt_meta_data_map map;
+  char *saveptr;
+
+  /* Look up the ``design languages'' metadata.  This is a comma (and
+     possibly space) separated list of scripts that the font was
+     designed for.  Here is an example of one such tag:
+
+       zh-Hans,Jpan,Kore
+
+     for a font that covers Simplified Chinese, along with Japanese
+     and Korean text.  */
+
+  saveptr = NULL;
+  data = sfnt_find_metadata (meta, SFNT_META_DATA_TAG_DLNG,
+                            &map);
+
+  if (!data)
+    {
+      /* Fall back to the supported languages metadata.  */
+      data = sfnt_find_metadata (meta, SFNT_META_DATA_TAG_SLNG,
+                                &map);
+
+      if (!data)
+       return;
+    }
+
+  USE_SAFE_ALLOCA;
+
+  /* Now copy metadata and add a trailing NULL byte.  */
+
+  if (map.data_length >= SIZE_MAX)
+    memory_full (SIZE_MAX);
+
+  metadata = SAFE_ALLOCA ((size_t) map.data_length + 1);
+  memcpy (metadata, data, map.data_length);
+  metadata[map.data_length] = '\0';
+
+  /* Loop through each script-language tag.  Note that there may be
+     extra leading spaces.  */
+  while ((tag = strtok_r (metadata, ",", &saveptr)))
+    {
+      metadata = NULL;
+
+      if (strstr (tag, "Hans") || strstr (tag, "Hant"))
+       desc->languages = Fcons (Qzh, desc->languages);
+
+      if (strstr (tag, "Japn"))
+       desc->languages = Fcons (Qja, desc->languages);
+
+      if (strstr (tag, "Kore"))
+       desc->languages = Fcons (Qko, desc->languages);
+    }
+
+  SAFE_FREE ();
+}
+
+/* Return the font registry corresponding to the encoding subtable
+   SUBTABLE.
+
+   Under X, the font registry is an atom registered with the Open
+   Group uniquely identifying the organization which defines the
+   font's character set.
+
+   In practice, the registry overlaps with the character set itself.
+   So Emacs just uses the ``registry'' field of each font object and
+   entity to represent both instead.  */
+
+static Lisp_Object
+sfnt_registry_for_subtable (struct sfnt_cmap_encoding_subtable *subtable)
+{
+  switch (subtable->platform_id)
+    {
+    case SFNT_PLATFORM_UNICODE:
+      /* Reject variation selector and last resort tables.  */
+      if ((subtable->platform_specific_id
+          == SFNT_UNICODE_VARIATION_SEQUENCES)
+         || (subtable->platform_specific_id
+             == SFNT_UNICODE_LAST_RESORT))
+       return Qnil;
+
+      return Qiso10646_1;
+
+    case SFNT_PLATFORM_MACINTOSH:
+
+      switch (subtable->platform_specific_id)
+       {
+       case SFNT_MACINTOSH_ROMAN:
+         /* X calls mac-roman ``apple-roman''.  */
+         return Qapple_roman;
+
+       default:
+         /* Some other Macintosh charset not supported by Emacs.  */
+         return Qnil;
+       }
+
+    case SFNT_PLATFORM_MICROSOFT:
+
+      /* Microsoft specific encodings.  */
+
+      switch (subtable->platform_specific_id)
+       {
+       case SFNT_MICROSOFT_SYMBOL:
+       case SFNT_MICROSOFT_UNICODE_BMP:
+         /* Symbols in the Unicode PUA are still Unicode.  */
+         return Qiso10646_1;
+
+       case SFNT_MICROSOFT_SHIFT_JIS:
+         return Qjisx0208_1983_0;
+
+       case SFNT_MICROSOFT_PRC:
+         return Qgbk;
+
+       case SFNT_MICROSOFT_JOHAB:
+         return Qksc5601_1987_0;
+
+       case SFNT_MICROSOFT_UNICODE_UCS_4:
+         return Qiso10646_1;
+       }
+
+    default:
+      return Qnil;
+    }
+}
+
+/* Return the type of characters that the cmap subtable SUBTABLE maps
+   from.  Value is:
+
+   2 if SUBTABLE maps from Unicode characters, including those outside
+   the Unicode Basic Multilingual Plane (BMP).
+
+   1 if SUBTABLE maps from Unicode characters within the BMP.
+
+   0 if SUBTABLE maps from some other character set that Emacs knows
+   about.
+
+   3 if SUBTABLE cannot be used by Emacs.  */
+
+static int
+sfntfont_identify_cmap (struct sfnt_cmap_encoding_subtable subtable)
+{
+  switch (subtable.platform_id)
+    {
+    case SFNT_PLATFORM_UNICODE:
+
+      /* Reject variation selector and last resort tables.  */
+      if ((subtable.platform_specific_id
+          == SFNT_UNICODE_VARIATION_SEQUENCES)
+         || (subtable.platform_specific_id
+             == SFNT_UNICODE_LAST_RESORT))
+       return 3;
+
+      /* 1.0, 1.1, ISO-10646-1993, and 2.0_BMP tables are all within
+        the BMP.  */
+      if (subtable.platform_specific_id < SFNT_UNICODE_2_0)
+       return 1;
+
+      return 2;
+
+    case SFNT_PLATFORM_MACINTOSH:
+
+      switch (subtable.platform_specific_id)
+       {
+       case SFNT_MACINTOSH_ROMAN:
+         /* mac-roman */
+         return 0;
+
+       default:
+         /* Some other Macintosh charset not supported by Emacs.  */
+         return 3;
+       }
+
+    case SFNT_PLATFORM_MICROSOFT:
+
+      /* Microsoft specific encodings.  */
+
+      switch (subtable.platform_specific_id)
+       {
+       case SFNT_MICROSOFT_SYMBOL:
+         /* Symbols in the Unicode PUA are still Unicode.  */
+         return 1;
+
+       case SFNT_MICROSOFT_UNICODE_BMP:
+         return 1;
+
+       case SFNT_MICROSOFT_SHIFT_JIS:
+         /* PCK aka japanese-jisx0208.  */
+         return 0;
+
+       case SFNT_MICROSOFT_PRC:
+         /* GBK, GB2312 or GB18030.  */
+         return 0;
+
+       case SFNT_MICROSOFT_JOHAB:
+         /* KS C 5601-1992, aka korean-ksc5601.  */
+         return 0;
+
+       case SFNT_MICROSOFT_UNICODE_UCS_4:
+         /* Unicode past the BMP.  */
+         return 2;
+       }
+
+    default:
+      return 3;
+    }
+}
+
+/* Figure out which registry DESC, backed by FD, whose table directory
+   is SUBTABLE, is likely to support.
+
+   Read the header of each subtable in the character map and compute
+   the registry to use; then, set DESC->registry to that value.  */
+
+static void
+sfnt_grok_registry (int fd, struct sfnt_font_desc *desc,
+                   struct sfnt_offset_subtable *subtable)
+{
+  struct sfnt_cmap_table *cmap;
+  struct sfnt_cmap_encoding_subtable *subtables;
+  int i;
+
+  cmap = sfnt_read_cmap_table (fd, subtable, &subtables, NULL);
+
+  if (!cmap)
+    return;
+
+  /* Now pick the ``best'' character map the same way as sfntfont_open
+     does.  The caveat is that since the subtable data has not been
+     read, Emacs cannot determine whether or not the encoding subtable
+     is valid.
+
+     Once platform_id is set, that value becomes much more
+     reliable.  */
+
+  /* First look for a non-BMP Unicode cmap.  */
+
+  for (i = 0; i < cmap->num_subtables; ++i)
+    {
+      if (sfntfont_identify_cmap (subtables[i]) == 2)
+       {
+         desc->registry
+           = sfnt_registry_for_subtable (&subtables[i]);
+         goto done;
+       }
+    }
+
+  /* Next, look for a BMP only Unicode cmap.  */
+
+  for (i = 0; i < cmap->num_subtables; ++i)
+    {
+      if (sfntfont_identify_cmap (subtables[i]) == 1)
+        {
+         desc->registry
+           = sfnt_registry_for_subtable (&subtables[i]);
+         goto done;
+       }
+    }
+
+  /* Finally, use the first cmap that appears and can be
+     identified.  */
+
+  for (i = 0; i < cmap->num_subtables; ++i)
+    {
+      if (sfntfont_identify_cmap (subtables[i]) == 0)
+        {
+         desc->registry
+           = sfnt_registry_for_subtable (&subtables[i]);
+         goto done;
+       }
+    }
+
+  /* There are no cmaps available to Emacs.  */
+ done:
+  xfree (cmap);
+  xfree (subtables);
+}
+
+/* Return whether or not the font description PREV conflicts with the
+   newer font description DESC, and should be removed from the list of
+   system fonts.
+
+   If PREV is a variable font, potentially adjust its list of
+   instances.  */
+
+static bool
+sfnt_replace_fonts_p (struct sfnt_font_desc *prev,
+                     struct sfnt_font_desc *desc)
+{
+  int i, width, weight, slant, count_instance;
+  Lisp_Object tem;
+  bool family_equal_p;
+
+  family_equal_p = !NILP (Fstring_equal (prev->family,
+                                        desc->family));
+
+  if ((!NILP (desc->instances)
+       || !NILP (Fstring_equal (prev->style, desc->style)))
+      && family_equal_p)
+    return true;
+
+  if (NILP (prev->instances) || !family_equal_p)
+    return false;
+
+  /* Look through instances in PREV to see if DESC provides the same
+     thing.  */
+
+  count_instance = 0;
+  for (i = 0; i < ASIZE (prev->instances); ++i)
+    {
+      tem = AREF (prev->instances, i);
+
+      if (NILP (tem))
+       continue;
+
+      width = XFIXNUM (AREF (tem, 2));
+      weight = XFIXNUM (AREF (tem, 3));
+      slant = XFIXNUM (AREF (tem, 4));
+
+      if (desc->width == width
+         && desc->weight == weight
+         && desc->slant == slant)
+       {
+         /* Remove this instance.  */
+         ASET (prev->instances, i, Qnil);
+         continue;
+       }
+
+      count_instance++;
+    }
+
+  /* Remove this desc if there are no more instances.  */
+  return count_instance < 1;
+}
+
+/* Enumerate the offset subtable SUBTABLES in the file FD, whose file
+   name is FILE.  OFFSET should be the offset of the subtable within
+   the font file, and is recorded for future use.  Value is 1 upon
+   failure, else 0.  */
+
+static int
+sfnt_enum_font_1 (int fd, const char *file,
+                 struct sfnt_offset_subtable *subtables,
+                 off_t offset)
+{
+  struct sfnt_font_desc *desc, **next, *prev;
+  struct sfnt_head_table *head;
+  struct sfnt_name_table *name;
+  struct sfnt_meta_table *meta;
+  struct sfnt_maxp_table *maxp;
+  struct sfnt_fvar_table *fvar;
+  struct sfnt_font_desc temp;
+  Lisp_Object family, style, instance, style1;
+  int i;
+
+  /* Create the font desc and copy in the file name.  */
+  desc = xzalloc (sizeof *desc + strlen (file) + 1);
+  desc->path = (char *) (desc + 1);
+  memcpy (desc->path, file, strlen (file) + 1);
+  desc->offset = offset;
+
+  /* Check that this is a TrueType font.  */
+  if (subtables->scaler_type != SFNT_SCALER_TRUE
+      && subtables->scaler_type != SFNT_SCALER_VER1)
+    goto bail1;
+
+  /* Read required tables.  */
+  head = sfnt_read_head_table (fd, subtables);
+  if (!head)
+    goto bail1;
+
+  name = sfnt_read_name_table (fd, subtables);
+  if (!name)
+    goto bail2;
+
+  maxp = sfnt_read_maxp_table (fd, subtables);
+  if (!maxp)
+    goto bail3;
+
+  /* meta is not required, nor present on many non-Apple fonts.  */
+  meta = sfnt_read_meta_table (fd, subtables);
+
+  /* Decode the family and style from the name table.  */
+  if (sfnt_decode_family_style (name, &family, &style))
+    goto bail4;
+
+  /* See if this is a distortable/variable/multiple master font (all
+     three terms mean the same time.)  */
+  fvar = sfnt_read_fvar_table (fd, subtables);
+
+  /* Set the family.  */
+  desc->family = family;
+  desc->designer = sfnt_decode_foundry_name (name);
+  desc->char_cache = Qnil;
+  desc->subtable.platform_id = 500;
+
+  /* Set the largest glyph identifier.  */
+  desc->num_glyphs = maxp->num_glyphs;
+
+  /* Parse the style.  */
+  sfnt_parse_style (style, desc);
+
+  /* If the meta table exists, parse the list of design languages.  */
+  if (meta)
+    sfnt_parse_languages (meta, desc);
+
+  /* Figure out the spacing.  Some fancy test like what Fontconfig
+     does is probably in order but not really necessary.  */
+  if (!NILP (Fstring_search (Fdowncase (family),
+                            build_string ("mono"),
+                            Qnil)))
+    desc->spacing = 100; /* FC_MONO */
+
+  /* Finally add mac-style flags.  Allow them to override styles that
+     have not been found.  */
+
+  if (head->mac_style & 01 && desc->weight == 80) /* Bold */
+    desc->weight = 200;
+
+  if (head->mac_style & 02 && desc->slant == 0) /* Italic */
+    desc->slant = 100;
+
+  /* Figure out what registry this font is likely to support.  */
+  sfnt_grok_registry (fd, desc, subtables);
+
+  if (fvar && fvar->instance_count)
+    {
+      /* If there is an fvar table with instances, then this is a font
+        which defines different axes along which the points in each
+        glyph can be changed.
+
+         Instead of enumerating the font itself, enumerate each
+         instance within, which specifies how to configure each axis
+         to achieve a specified style.  */
+
+      desc->instances = make_vector (fvar->instance_count, Qnil);
+
+      for (i = 0; i < fvar->instance_count; ++i)
+       {
+         style1 = sfnt_decode_instance_name (&fvar->instance[i],
+                                             name);
+
+         if (NILP (style1))
+           continue;
+
+         /* Now parse the style.  */
+         temp.adstyle = Qnil;
+         sfnt_parse_style (style1, &temp);
+
+         /* Set each field of the vector.  */
+         instance = make_vector (5, Qnil);
+         ASET (instance, 0, style1);
+         ASET (instance, 1, temp.adstyle);
+         ASET (instance, 2, make_fixnum (temp.width));
+         ASET (instance, 3, make_fixnum (temp.weight));
+         ASET (instance, 4, make_fixnum (temp.slant));
+
+         /* Place the vector in desc->instances.  */
+         ASET (desc->instances, i, instance);
+       }
+    }
+
+  /* Set the style, link the desc onto system_fonts and return.  */
+  desc->style = style;
+  desc->next = system_fonts;
+  system_fonts = desc;
+
+  /* Remove any fonts which have the same style as this one.  For
+     distortable fonts, only remove overlapping styles, unless this is
+     also a distortable font.  */
+
+  next = &system_fonts->next;
+  prev = *next;
+  for (; *next; prev = *next)
+    {
+      if (sfnt_replace_fonts_p (prev, desc))
+       {
+         *next = prev->next;
+         xfree (prev);
+       }
+      else
+       next = &prev->next;
+    }
+
+  xfree (fvar);
+  xfree (meta);
+  xfree (maxp);
+  xfree (name);
+  xfree (head);
+  return 0;
+
+ bail4:
+  xfree (meta);
+  xfree (maxp);
+ bail3:
+  xfree (name);
+ bail2:
+  xfree (head);
+ bail1:
+  xfree (desc);
+  return 1;
+}
+
+/* Enumerate the font FILE into the list of system fonts.  Return 1 if
+   it could not be enumerated, 0 otherwise.
+
+   Remove any font whose family and style is a duplicate of this one.
+
+   FILE can either be a TrueType collection file containing TrueType
+   fonts, or a TrueType font itself.  */
+
+int
+sfnt_enum_font (const char *file)
+{
+  int fd, rc;
+  struct sfnt_offset_subtable *subtables;
+  struct sfnt_ttc_header *ttc;
+  size_t i;
+
+  /* Now open the font for reading.  */
+  fd = emacs_open (file, O_RDONLY, 0);
+
+  if (fd == -1)
+    goto bail;
+
+  /* Read the table directory.  */
+  subtables = sfnt_read_table_directory (fd);
+
+  if (subtables == (struct sfnt_offset_subtable *) -1)
+    {
+      /* This is actually a TrueType container file.  Go back to the
+        beginning and read the TTC header.  */
+
+      if (lseek (fd, 0, SEEK_SET))
+       goto bail0;
+
+      ttc = sfnt_read_ttc_header (fd);
+
+      if (!ttc)
+       goto bail0;
+
+      /* Enumerate each of the fonts in the collection.  */
+
+      for (i = 0; i < ttc->num_fonts; ++i)
+       {
+         if (lseek (fd, ttc->offset_table[i], SEEK_SET)
+             != ttc->offset_table[i])
+           continue;
+
+         subtables = sfnt_read_table_directory (fd);
+
+         if (!subtables)
+           continue;
+
+         sfnt_enum_font_1 (fd, file, subtables,
+                           ttc->offset_table[i]);
+         xfree (subtables);
+       }
+
+      /* Always treat reading containers as having been
+        successful.  */
+
+      emacs_close (fd);
+      xfree (ttc);
+      return 0;
+    }
+
+  if (!subtables)
+    goto bail0;
+
+  /* Now actually enumerate this font.  */
+  rc = sfnt_enum_font_1 (fd, file, subtables, 0);
+  xfree (subtables);
+  emacs_close (fd);
+  return rc;
+
+ bail0:
+  emacs_close (fd);
+ bail:
+  return 1;
+}
+
+
+
+/* Font discovery and matching.  */
+
+static struct charset *
+sfntfont_charset_for_name (Lisp_Object symbol)
+{
+  ptrdiff_t idx;
+  int id;
+
+  idx = CHARSET_SYMBOL_HASH_INDEX (symbol);
+
+  if (idx == -1)
+    return NULL;
+
+  /* Vcharset_hash_table is not a real variable, so Lisp programs
+     can't clobber it.  */
+  id = XFIXNUM (AREF (HASH_VALUE (XHASH_TABLE (Vcharset_hash_table),
+                                 idx),
+                     charset_id));
+
+  return CHARSET_FROM_ID (id);
+}
+
+/* Return the character set corresponding to a cmap subtable SUBTABLE.
+   Value is NULL if the subtable is not supported.  */
+
+static struct charset *
+sfntfont_charset_for_cmap (struct sfnt_cmap_encoding_subtable subtable)
+{
+  switch (subtable.platform_id)
+    {
+    case SFNT_PLATFORM_UNICODE:
+      /* Reject variation selector and last resort tables.  */
+      if ((subtable.platform_specific_id
+          == SFNT_UNICODE_VARIATION_SEQUENCES)
+         || (subtable.platform_specific_id
+             == SFNT_UNICODE_LAST_RESORT))
+       return NULL;
+
+      /* 1.0, 1.1, ISO-10646-1993, and 2.0_BMP tables are all within
+        the BMP.  */
+      if (subtable.platform_specific_id < SFNT_UNICODE_2_0)
+       return sfntfont_charset_for_name (Qunicode_bmp);
+
+      return sfntfont_charset_for_name (Qunicode);
+
+    case SFNT_PLATFORM_MACINTOSH:
+
+      switch (subtable.platform_specific_id)
+       {
+       case SFNT_MACINTOSH_ROMAN:
+         return sfntfont_charset_for_name (Qmac_roman);
+
+       default:
+         /* Some other Macintosh charset not supported by Emacs.  */
+         return NULL;
+       }
+
+    case SFNT_PLATFORM_MICROSOFT:
+
+      /* Microsoft specific encodings.  */
+
+      switch (subtable.platform_specific_id)
+       {
+       case SFNT_MICROSOFT_SYMBOL:
+         /* Symbols in the Unicode PUA are still Unicode.  */
+         return sfntfont_charset_for_name (Qunicode);
+
+       case SFNT_MICROSOFT_UNICODE_BMP:
+         return sfntfont_charset_for_name (Qunicode_bmp);
+
+       case SFNT_MICROSOFT_SHIFT_JIS:
+         /* PCK aka japanese-jisx0208.  */
+         return sfntfont_charset_for_name (Qjapanese_jisx0208);
+
+       case SFNT_MICROSOFT_PRC:
+         /* GBK, GB2312 or GB18030.  */
+         return sfntfont_charset_for_name (Qgbk);
+
+       case SFNT_MICROSOFT_JOHAB:
+         /* KS C 5601-1992, aka korean-ksc5601.  */
+         return sfntfont_charset_for_name (Qkorean_ksc5601);
+
+       case SFNT_MICROSOFT_UNICODE_UCS_4:
+         /* Unicode past the BMP.  */
+         return sfntfont_charset_for_name (Qucs);
+       }
+
+    default:
+      return NULL;
+    }
+}
+
+/* Pick the best character map in the cmap table CMAP.  Use the
+   subtables in SUBTABLES and DATA.  Return the subtable data and the
+   subtable in *SUBTABLE upon success, NULL otherwise.
+
+   If FORMAT14 is non-NULL, return any associated format 14 variation
+   selection context in *FORMAT14 should the selected charcter map be
+   a Unicode character map.  */
+
+static struct sfnt_cmap_encoding_subtable_data *
+sfntfont_select_cmap (struct sfnt_cmap_table *cmap,
+                     struct sfnt_cmap_encoding_subtable *subtables,
+                     struct sfnt_cmap_encoding_subtable_data **data,
+                     struct sfnt_cmap_encoding_subtable *subtable,
+                     struct sfnt_cmap_format_14 **format14)
+{
+  int i, j;
+
+  /* First look for a non-BMP Unicode cmap.  */
+
+  for (i = 0; i < cmap->num_subtables; ++i)
+    {
+      if (data[i] && sfntfont_identify_cmap (subtables[i]) == 2)
+       {
+         *subtable = subtables[i];
+
+         if (!format14)
+           return data[i];
+
+         /* Search for a correspoinding format 14 character map.
+            This is used in conjunction with the selected character
+            map to map variation sequences.  */
+
+         for (j = 0; j < cmap->num_subtables; ++j)
+           {
+             if (data[j]
+                 && subtables[j].platform_id == SFNT_PLATFORM_UNICODE
+                 && (subtables[j].platform_specific_id
+                     == SFNT_UNICODE_VARIATION_SEQUENCES)
+                 && data[j]->format == 14)
+               *format14 = (struct sfnt_cmap_format_14 *) data[j];
+           }
+
+         return data[i];
+       }
+    }
+
+  /* Next, look for a BMP only Unicode cmap.  */
+
+  for (i = 0; i < cmap->num_subtables; ++i)
+    {
+      if (data[i] && sfntfont_identify_cmap (subtables[i]) == 1)
+       {
+         *subtable = subtables[i];
+
+         if (!format14)
+           return data[i];
+
+         /* Search for a correspoinding format 14 character map.
+            This is used in conjunction with the selected character
+            map to map variation sequences.  */
+
+         for (j = 0; j < cmap->num_subtables; ++j)
+           {
+             if (data[j]
+                 && subtables[j].platform_id == SFNT_PLATFORM_UNICODE
+                 && (subtables[j].platform_specific_id
+                     == SFNT_UNICODE_VARIATION_SEQUENCES)
+                 && data[j]->format == 14)
+               *format14 = (struct sfnt_cmap_format_14 *) data[j];
+           }
+
+         return data[i];
+       }
+    }
+
+  /* Finally, use the first cmap that appears and can be
+     identified.  */
+
+  for (i = 0; i < cmap->num_subtables; ++i)
+    {
+      if (data[i] && sfntfont_identify_cmap (subtables[i]) == 0)
+       {
+         *subtable = subtables[i];
+         return data[i];
+       }
+    }
+
+  /* There are no cmaps available to Emacs.  */
+  return NULL;
+}
+
+/* Read the cmap from the font descriptor DESC, and place it in CMAP.
+   Keep *CMAP untouched if opening the cmap fails.  Set SUBTABLE to
+   the cmap's header upon success.  */
+
+static void
+sfntfont_read_cmap (struct sfnt_font_desc *desc,
+                   struct sfnt_cmap_encoding_subtable_data **cmap,
+                   struct sfnt_cmap_encoding_subtable *subtable)
+{
+  struct sfnt_offset_subtable *font;
+  struct sfnt_cmap_encoding_subtable *subtables;
+  struct sfnt_cmap_encoding_subtable_data **data;
+  struct sfnt_cmap_table *table;
+  int fd, i;
+
+  /* Pick a character map and place it in *CMAP.  */
+  fd = emacs_open (desc->path, O_RDONLY, 0);
+
+  if (fd < 0)
+    return;
+
+  font = sfnt_read_table_directory (fd);
+
+  if (!font)
+    {
+      emacs_close (fd);
+      return;
+    }
+
+  table = sfnt_read_cmap_table (fd, font, &subtables,
+                               &data);
+  xfree (font);
+
+  if (!table)
+    {
+      emacs_close (fd);
+      return;
+    }
+
+  /* Now pick the best character map.  */
+
+  *cmap = sfntfont_select_cmap (table, subtables, data,
+                               subtable, NULL);
+
+  /* Free the cmap data.  */
+
+  for (i = 0; i < table->num_subtables; ++i)
+    {
+      if (data[i] != *cmap)
+       xfree (data[i]);
+    }
+
+  xfree (data);
+  xfree (subtables);
+  xfree (table);
+  emacs_close (fd);
+}
+
+/* Return whether or not CHARACTER has an associated mapping in CMAP,
+   and the mapping points to a valid glyph.  DESC is the font
+   descriptor associated with the font.  */
+
+static bool
+sfntfont_glyph_valid (struct sfnt_font_desc *desc,
+                     sfnt_char font_character,
+                     struct sfnt_cmap_encoding_subtable_data *cmap)
+{
+  sfnt_glyph glyph;
+
+  glyph = sfnt_lookup_glyph (font_character, cmap);
+
+  if (!glyph)
+    return false;
+
+  return glyph <= desc->num_glyphs;
+}
+
+/* Look up a character CHARACTER in the font description DESC.  Cache
+   the results.  Return true if the character exists, false otherwise.
+
+   If *CMAP is NULL, select a character map for the font and save it
+   there.  Otherwise, use the character map in *CMAP.  Save data
+   associated with the character map in *SUBTABLE.  */
+
+static bool
+sfntfont_lookup_char (struct sfnt_font_desc *desc, Lisp_Object character,
+                     struct sfnt_cmap_encoding_subtable_data **cmap,
+                     struct sfnt_cmap_encoding_subtable *subtable)
+{
+  Lisp_Object cached;
+  sfnt_char font_character;
+  struct charset *charset;
+  bool present;
+
+  /* Return false for characters that don't fit in a char table.  */
+  if (XFIXNUM (character) > INT_MAX || XFIXNUM (character) < 0)
+    return false;
+
+  if (!NILP (desc->char_cache))
+    {
+      cached = char_table_ref (desc->char_cache,
+                              XFIXNUM (character));
+      if (!NILP (cached))
+       return (EQ (cached, Qlambda) ? false : true);
+    }
+
+  if (!*cmap && !desc->cmap_invalid)
+    sfntfont_read_cmap (desc, cmap, subtable);
+
+  /* Check that a cmap is now present.  */
+  if (!*cmap)
+    {
+      /* Opening the cmap failed.  Set desc->cmap_invalid to avoid
+        opening it again.  */
+      desc->cmap_invalid = true;
+      return false;
+    }
+
+  /* Otherwise, encode the character.  */
+
+  charset = sfntfont_charset_for_cmap (*subtable);
+  if (!charset)
+    /* Emacs missing charsets? */
+    return false;
+
+  font_character = ENCODE_CHAR (charset, (int) XFIXNUM (character));
+
+  if (font_character == CHARSET_INVALID_CODE (charset))
+    return false;
+
+  /* Now return whether or not the glyph is present.  Noto Sans
+     Georgian comes with a corrupt format 4 cmap table that somehow
+     tries to express glyphs greater than 65565.  */
+  present = sfntfont_glyph_valid (desc, font_character, *cmap);
+
+  /* Cache the result.  Store Qlambda when not present, Qt
+     otherwise.  */
+
+  if (NILP (desc->char_cache))
+    desc->char_cache = Fmake_char_table (Qfont_lookup_cache,
+                                        Qnil);
+
+  Fset_char_table_range (desc->char_cache, character,
+                        present ? Qt : Qlambda);
+  return present;
+}
+
+/* Return whether or not the specified registry A is ``compatible''
+   with registry B.
+
+   Compatibility does not refer to whether or not the font registries
+   have an identical character set or repertory of characters.
+
+   Instead, it refers to whether or not Emacs expects looking for A to
+   result in fonts used with B.  */
+
+static bool
+sfntfont_registries_compatible_p (Lisp_Object a, Lisp_Object b)
+{
+  if (EQ (a, Qiso8859_1) && EQ (b, Qiso10646_1))
+    return true;
+
+  return EQ (a, b);
+}
+
+/* Return whether or not the font description DESC satisfactorily
+   matches the font specification FONT_SPEC.
+
+   Value is 0 if there is no match, -1 if there is a match against
+   DESC itself, and the number of matching instances if the style
+   matches one or more instances defined in in DESC.  Return the index
+   of each matching instance in INSTANCES; it should be SIZE big.  */
+
+static int
+sfntfont_list_1 (struct sfnt_font_desc *desc, Lisp_Object spec,
+                int *instances, int size)
+{
+  Lisp_Object tem, extra, tail;
+  struct sfnt_cmap_encoding_subtable_data *cmap;
+  size_t i;
+  struct sfnt_cmap_encoding_subtable subtable;
+  int instance, num_instance;
+  Lisp_Object item;
+
+  /* cmap and subtable are caches for sfntfont_lookup_char.  */
+
+  /* Check that the family name in SPEC matches DESC->family if it is
+     specified.  */
+
+  tem = AREF (spec, FONT_FAMILY_INDEX);
+
+  /* If TEM is a family listed in Vsfnt_default_family_alist,
+     then use that instead.  */
+
+  if (SYMBOLP (tem) && CONSP (Vsfnt_default_family_alist))
+    {
+      tail = Vsfnt_default_family_alist;
+      FOR_EACH_TAIL_SAFE (tail)
+       {
+         if (!CONSP (XCAR (tail)))
+           continue;
+
+         if (STRINGP (XCAR (XCAR (tail)))
+             && STRINGP (XCDR (XCAR (tail)))
+             && !NILP (Fstring_equal (SYMBOL_NAME (tem),
+                                      XCAR (XCAR (tail)))))
+           {
+             /* Special family found.  */
+             tem = Fintern (XCDR (XCAR (tail)), Qnil);
+             break;
+           }
+       }
+    }
+
+  if (!NILP (tem) && NILP (Fstring_equal (SYMBOL_NAME (tem),
+                                         desc->family)))
+    return 0;
+
+  instance = -1;
+
+  /* If a registry is set and wrong, then reject the font desc
+     immediately.  This detects 50% of mismatches from fontset.c.
+
+     If DESC->registry is nil, then the registry couldn't be
+     determined beforehand.  */
+
+  tem = AREF (spec, FONT_REGISTRY_INDEX);
+  if (!NILP (tem) && !NILP (desc->registry)
+      && !sfntfont_registries_compatible_p (tem, desc->registry))
+    return 0;
+
+  /* Check the style.  If DESC is a fixed font, just check once.
+     Otherwise, check each instance.  */
+
+  if (NILP (desc->instances))
+    {
+      tem = AREF (spec, FONT_ADSTYLE_INDEX);
+      if (!NILP (tem) && NILP (Fequal (tem, desc->adstyle)))
+       return 0;
+
+      if (FONT_WIDTH_NUMERIC (spec) != -1
+         && FONT_WIDTH_NUMERIC (spec) != desc->width)
+       return 0;
+
+      if (FONT_WEIGHT_NUMERIC (spec) != -1
+         && FONT_WEIGHT_NUMERIC (spec) != desc->weight)
+       return 0;
+
+      if (FONT_SLANT_NUMERIC (spec) != -1
+         && FONT_SLANT_NUMERIC (spec) != desc->slant)
+       return 0;
+    }
+  else
+    {
+      num_instance = 0;
+
+      /* Find the indices of instances in this distortable font which
+        match the given font spec.  */
+
+      for (i = 0; i < ASIZE (desc->instances); ++i)
+       {
+         item = AREF (desc->instances, i);
+
+         if (NILP (item))
+           continue;
+
+         /* Check that the adstyle specified matches.  */
+
+         tem = AREF (spec, FONT_ADSTYLE_INDEX);
+         if (!NILP (tem) && NILP (Fequal (tem, AREF (item, 1))))
+           continue;
+
+         /* Check the style.  */
+
+         if (FONT_WIDTH_NUMERIC (spec) != -1
+             && (FONT_WIDTH_NUMERIC (spec)
+                 != XFIXNUM (AREF (item, 2))))
+           continue;
+
+         if (FONT_WEIGHT_NUMERIC (spec) != -1
+             && (FONT_WEIGHT_NUMERIC (spec)
+                 != XFIXNUM (AREF (item, 3))))
+           continue;
+
+         if (FONT_SLANT_NUMERIC (spec) != -1
+             && (FONT_SLANT_NUMERIC (spec)
+                 != XFIXNUM (AREF (item, 4))))
+           continue;
+
+         if (num_instance == size)
+           break;
+
+         /* A matching instance has been found.  Set its index, then
+            go back to the rest of the font matching.  */
+         instances[num_instance++] = i;
+       }
+
+      instance = num_instance;
+    }
+
+  /* Handle extras.  */
+  extra = AREF (spec, FONT_EXTRA_INDEX);
+
+  if (NILP (extra))
+    return instance;
+
+  tem = assq_no_quit (QCscript, extra);
+  cmap = NULL;
+
+  if (!NILP (tem))
+    {
+      /* If a script has been specified, look up its representative
+        characters and see if they are present in the font.  This
+        requires reading the cmap.  */
+      tem = assq_no_quit (XCDR (tem), Vscript_representative_chars);
+
+      if (CONSP (tem) && VECTORP (XCDR (tem)))
+       {
+         tem = XCDR (tem);
+
+         /* The vector contains characters, of which one must be
+            present in the font.  */
+         for (i = 0; i < ASIZE (tem); ++i)
+           {
+             if (FIXNUMP (AREF (tem, i)))
+               {
+                 if (!sfntfont_lookup_char (desc, AREF (tem, i),
+                                            &cmap, &subtable))
+                   goto fail;
+
+                 /* One character is enough to pass a font.  Don't
+                    look at too many.  */
+                 break;
+               }
+           }
+       }
+      else if (CONSP (tem) && CONSP (XCDR (tem)))
+       {
+         tem = XCDR (tem);
+
+         /* tem is a list of each characters, all of which must be
+            present in the font.  */
+         FOR_EACH_TAIL_SAFE (tem)
+           {
+             if (FIXNUMP (XCAR (tem))
+                 && !sfntfont_lookup_char (desc, XCAR (tem), &cmap,
+                                           &subtable))
+               goto fail;
+           }
+
+         /* One or more characters are missing.  */
+         if (!NILP (tem))
+           goto fail;
+       }
+      /* Fail if there are no matching fonts at all.  */
+      else if (NILP (tem))
+       goto fail;
+    }
+
+  /* Now check that the language is supported.  */
+  tem = assq_no_quit (QClang, extra);
+  if (!NILP (tem) && NILP (Fmemq (tem, desc->languages)))
+    goto fail;
+
+  /* Set desc->subtable if cmap was specified.  */
+  if (cmap)
+    desc->subtable = subtable;
+
+  xfree (cmap);
+  return instance;
+
+ fail:
+  /* The cmap might've been read in and require deallocation.  */
+  xfree (cmap);
+  return 0;
+}
+
+/* Type of font entities and font objects created.  */
+static Lisp_Object sfnt_vendor_name;
+
+/* Font driver used in font objects created.  */
+static const struct font_driver *sfnt_font_driver;
+
+/* Return the font registry corresponding to the font descriptor DESC.
+   Under X, the font registry is an atom registered with the Open
+   Group uniquely identifying the organization which defines the
+   font's character set.
+
+   In practice, the registry overlaps with the character set itself.
+   So Emacs just uses the ``registry'' field to represent both
+   instead.  */
+
+static Lisp_Object
+sfntfont_registry_for_desc (struct sfnt_font_desc *desc)
+{
+  struct sfnt_cmap_encoding_subtable_data *cmap;
+
+  cmap = NULL;
+
+  if (desc->cmap_invalid)
+    return Qnil;
+
+  if (desc->subtable.platform_id == 500)
+    {
+      /* Read in the cmap to determine the registry.  */
+      sfntfont_read_cmap (desc, &cmap, &desc->subtable);
+
+      if (!cmap)
+       {
+         desc->cmap_invalid = true;
+         return Qnil;
+       }
+    }
+
+  xfree (cmap);
+
+  if (desc->subtable.platform_id != 500)
+    /* desc->subtable.platform_id is now set.  CMAP is already free,
+       because it is not actually used.  */
+    return sfnt_registry_for_subtable (&desc->subtable);
+
+  return Qnil;
+}
+
+/* Return a font-entity that represents the font descriptor (unopened
+   font) DESC.  If INSTANCE is more than or equal to 1, then it is the
+   index of the instance in DESC that should be opened plus 1; in that
+   case, DESC must be a distortable font.  */
+
+static Lisp_Object
+sfntfont_desc_to_entity (struct sfnt_font_desc *desc, int instance)
+{
+  Lisp_Object entity, vector;
+
+  entity = font_make_entity ();
+
+  ASET (entity, FONT_TYPE_INDEX, sfnt_vendor_name);
+
+  if (!NILP (desc->designer))
+    ASET (entity, FONT_FOUNDRY_INDEX,
+         Fintern (desc->designer, Qnil));
+
+  ASET (entity, FONT_FAMILY_INDEX, Fintern (desc->family, Qnil));
+  ASET (entity, FONT_ADSTYLE_INDEX, Qnil);
+  ASET (entity, FONT_REGISTRY_INDEX,
+       sfntfont_registry_for_desc (desc));
+
+  /* Size of 0 means the font is scalable.  */
+  ASET (entity, FONT_SIZE_INDEX, make_fixnum (0));
+  ASET (entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
+  ASET (entity, FONT_SPACING_INDEX,
+       make_fixnum (desc->spacing));
+
+  if (instance >= 1)
+    {
+      if (NILP (desc->instances)
+         || instance > ASIZE (desc->instances))
+       emacs_abort ();
+
+      vector = AREF (desc->instances, instance - 1);
+      FONT_SET_STYLE (entity, FONT_WIDTH_INDEX,
+                     AREF (vector, 2));
+      FONT_SET_STYLE (entity, FONT_WEIGHT_INDEX,
+                     AREF (vector, 3));
+      FONT_SET_STYLE (entity, FONT_SLANT_INDEX,
+                     AREF (vector, 4));
+      ASET (entity, FONT_ADSTYLE_INDEX, AREF (vector, 1));      
+    }
+  else
+    {
+      FONT_SET_STYLE (entity, FONT_WIDTH_INDEX,
+                     make_fixnum (desc->width));
+      FONT_SET_STYLE (entity, FONT_WEIGHT_INDEX,
+                     make_fixnum (desc->weight));
+      FONT_SET_STYLE (entity, FONT_SLANT_INDEX,
+                     make_fixnum (desc->slant));
+      ASET (entity, FONT_ADSTYLE_INDEX, desc->adstyle);
+    }
+
+  /* Set FONT_EXTRA_INDEX to a pointer to the font description.  Font
+     descriptions are never supposed to be freed.  */
+
+  ASET (entity, FONT_EXTRA_INDEX,
+       (instance >= 1
+        ? list2 (Fcons (Qfont_entity, make_mint_ptr (desc)),
+                 Fcons (Qfont_instance, make_fixnum (instance - 1)))
+        : list1 (Fcons (Qfont_entity, make_mint_ptr (desc)))));
+
+  return entity;
+}
+
+/* Return a list of font-entities matching the specified
+   FONT_SPEC.  */
+
+Lisp_Object
+sfntfont_list (struct frame *f, Lisp_Object font_spec)
+{
+  Lisp_Object matching, tem;
+  struct sfnt_font_desc *desc;
+  int i, rc, instances[100];
+
+  matching = Qnil;
+
+  block_input ();
+  /* Returning irrelevant results on receiving an OTF form will cause
+     fontset.c to loop over and over, making displaying some
+     characters very slow.  */
+  tem = assq_no_quit (QCotf, AREF (font_spec, FONT_EXTRA_INDEX));
+  if (CONSP (tem) && !NILP (XCDR (tem)))
+    {
+      unblock_input ();
+      return Qnil;
+    }
+
+  /* Loop through known system fonts and add them one-by-one.  */
+
+  for (desc = system_fonts; desc; desc = desc->next)
+    {
+      rc = sfntfont_list_1 (desc, font_spec, instances,
+                           ARRAYELTS (instances));
+
+      if (rc < 0)
+       matching = Fcons (sfntfont_desc_to_entity (desc, 0),
+                         matching);
+      else if (rc)
+       {
+         /* Add each matching instance.  */
+
+         for (i = 0; i < rc; ++i)
+           matching = Fcons (sfntfont_desc_to_entity (desc,
+                                                      instances[i] + 1),
+                             matching);
+       }
+    }
+
+  unblock_input ();
+
+  return matching;
+}
+
+/* Return the first font-entity matching the specified FONT_SPEC.  */
+
+Lisp_Object
+sfntfont_match (struct frame *f, Lisp_Object font_spec)
+{
+  Lisp_Object matches;
+
+  matches = sfntfont_list (f, font_spec);
+
+  if (!NILP (matches))
+    return XCAR (matches);
+
+  return Qnil;
+}
+
+
+
+enum
+  {
+    SFNT_OUTLINE_CACHE_SIZE = 256,
+    SFNT_RASTER_CACHE_SIZE  = 128,
+  };
+
+/* Caching subsystem.  Generating outlines from glyphs is expensive,
+   and so is rasterizing them, so two caches are maintained for both
+   glyph outlines and rasters.
+
+   Computing metrics also requires some expensive processing if the
+   glyph has instructions or distortions.  */
+
+struct sfnt_outline_cache
+{
+  /* Next and last cache buckets.  */
+  struct sfnt_outline_cache *next, *last;
+
+  /* Pointer to outline.  */
+  struct sfnt_glyph_outline *outline;
+
+  /* Reference to glyph metrics.  */
+  struct sfnt_glyph_metrics metrics;
+
+  /* What glyph this caches.  */
+  sfnt_glyph glyph;
+};
+
+struct sfnt_raster_cache
+{
+  /* Next and last cache buckets.  */
+  struct sfnt_raster_cache *next, *last;
+
+  /* Pointer to raster.  */
+  struct sfnt_raster *raster;
+
+  /* What glyph this caches.  */
+  sfnt_glyph glyph;
+};
+
+struct sfntfont_get_glyph_outline_dcontext
+{
+  /* Long and short loca tables.  */
+  struct sfnt_loca_table_long *loca_long;
+  struct sfnt_loca_table_short *loca_short;
+
+  /* glyf table.  */
+  struct sfnt_glyf_table *glyf;
+
+  /* Variation settings, or NULL.  */
+  struct sfnt_blend *blend;
+};
+
+/* Return the glyph identified by GLYPH_ID from the glyf and loca
+   table specified in DCONTEXT.  Set *NEED_FREE to true.  */
+
+static struct sfnt_glyph *
+sfntfont_get_glyph (sfnt_glyph glyph_id, void *dcontext,
+                   bool *need_free)
+{
+  struct sfntfont_get_glyph_outline_dcontext *tables;
+  struct sfnt_glyph *glyph;
+  struct sfnt_metrics_distortion distortion;
+
+  tables = dcontext;
+  *need_free = true;
+
+  glyph = sfnt_read_glyph (glyph_id, tables->glyf,
+                          tables->loca_short,
+                          tables->loca_long);
+
+  if (tables->blend && glyph)
+    {
+      if (glyph->simple)
+       sfnt_vary_simple_glyph (tables->blend, glyph_id, glyph,
+                               &distortion);
+      else
+       sfnt_vary_compound_glyph (tables->blend, glyph_id, glyph,
+                                 &distortion);
+    }
+
+  /* Note that the distortion is not relevant for compound glyphs.  */
+  return glyph;
+}
+
+/* Free the glyph identified by GLYPH.  */
+
+static void
+sfntfont_free_glyph (struct sfnt_glyph *glyph, void *dcontext)
+{
+  sfnt_free_glyph (glyph);
+}
+
+/* Dereference the outline OUTLINE.  Free it once refcount reaches
+   0.  */
+
+static void
+sfntfont_dereference_outline (struct sfnt_glyph_outline *outline)
+{
+  eassert (outline->refcount > 0);
+
+  if (--outline->refcount)
+    return;
+
+  xfree (outline);
+}
+
+/* Get the outline corresponding to the specified GLYPH_CODE in CACHE.
+   Use the scale factor SCALE, the glyf table GLYF, and the head table
+   HEAD.  Keep *CACHE_SIZE updated with the number of elements in the
+   cache.
+
+   Distort the glyph using BLEND if INDEX is not -1.
+
+   Use the offset information in the long or short loca tables
+   LOCA_LONG and LOCA_SHORT, whichever is set.
+
+   Use the specified HMTX, HEAD, HHEA and MAXP tables when instructing
+   compound glyphs.
+
+   If INTERPRETER is non-NULL, then possibly use it and the
+   interpreter graphics STATE to instruct the glyph.
+
+   If METRICS is non-NULL, return the scaled glyph metrics after
+   variation and instructing.
+
+   Return the outline with an incremented reference count and enter
+   the generated outline into CACHE upon success, possibly discarding
+   any older outlines, or NULL on failure.  */
+
+static struct sfnt_glyph_outline *
+sfntfont_get_glyph_outline (sfnt_glyph glyph_code,
+                           struct sfnt_outline_cache *cache,
+                           sfnt_fixed scale, int *cache_size,
+                           struct sfnt_blend *blend,
+                           int index,
+                           struct sfnt_glyf_table *glyf,
+                           struct sfnt_head_table *head,
+                           struct sfnt_hmtx_table *hmtx,
+                           struct sfnt_hhea_table *hhea,
+                           struct sfnt_maxp_table *maxp,
+                           struct sfnt_loca_table_short *loca_short,
+                           struct sfnt_loca_table_long *loca_long,
+                           struct sfnt_interpreter *interpreter,
+                           struct sfnt_glyph_metrics *metrics,
+                           struct sfnt_graphics_state *state)
+{
+  struct sfnt_outline_cache *start;
+  struct sfnt_glyph_outline *outline;
+  struct sfnt_glyph *glyph;
+  struct sfntfont_get_glyph_outline_dcontext dcontext;
+  struct sfnt_instructed_outline *value;
+  const char *error;
+  struct sfnt_glyph_metrics temp;
+  struct sfnt_metrics_distortion distortion;
+
+  start = cache->next;
+  distortion.advance = 0;
+
+  /* See if the outline is already cached.  */
+  for (; start != cache; start = start->next)
+    {
+      if (start->glyph == glyph_code)
+       {
+         /* Move start to the start of the ring.  Then increase
+            start->outline->refcount and return it.  */
+
+         start->last->next = start->next;
+         start->next->last = start->last;
+
+         start->next = cache->next;
+         start->last = cache;
+         start->next->last = start;
+         start->last->next = start;
+         start->outline->refcount++;
+
+         if (metrics)
+           *metrics = start->metrics;
+
+         return start->outline;
+       }
+    }
+
+  /* Not already cached.  Get the glyph.  */
+  glyph = sfnt_read_glyph (glyph_code, glyf,
+                          loca_short, loca_long);
+
+  if (!glyph)
+    return NULL;
+
+  /* Distort the glyph if necessary.  */
+
+  if (index != -1)
+    {
+      if (glyph->simple)
+       {
+         if (sfnt_vary_simple_glyph (blend, glyph_code,
+                                     glyph, &distortion))
+           {
+             sfnt_free_glyph (glyph);
+             return NULL;
+           }
+       }
+      else if (sfnt_vary_compound_glyph (blend, glyph_code,
+                                        glyph, &distortion))
+       {
+         sfnt_free_glyph (glyph);
+         return NULL;
+       }
+    }
+
+  /* Try to instruct the glyph if INTERPRETER is specified.  */
+
+  outline = NULL;
+
+  dcontext.loca_long = loca_long;
+  dcontext.loca_short = loca_short;
+  dcontext.glyf = glyf;
+  dcontext.blend = (index != -1 ? blend : NULL);
+
+  /* Now load the glyph's unscaled metrics into TEMP.  */
+
+  if (sfnt_lookup_glyph_metrics (glyph_code, -1, &temp, hmtx, hhea,
+                                head, maxp))
+    goto fail;
+
+  /* Add the advance width distortion.  */
+  temp.advance += distortion.advance;
+
+  if (interpreter)
+    {
+      if (glyph->simple)
+       {
+         /* Restore the interpreter state from the snapshot taken
+            after loading the preprogram.  */
+         interpreter->state = *state;
+
+         error = sfnt_interpret_simple_glyph (glyph, interpreter,
+                                              &temp, &value);
+       }
+      else
+       /* Restoring the interpreter state is done by
+          sfnt_interpret_compound_glyph; all that must be done here
+          is to give the graphics state to that function.  */
+       error = sfnt_interpret_compound_glyph (glyph, interpreter,
+                                              state,
+                                              sfntfont_get_glyph,
+                                              sfntfont_free_glyph,
+                                              hmtx, hhea, maxp,
+                                              &temp, &dcontext,
+                                              &value);
+
+      if (!error)
+       {
+         outline = sfnt_build_instructed_outline (value);
+         xfree (value);
+       }
+    }
+
+  if (!outline)
+    {
+      if (!interpreter)
+       outline = sfnt_build_glyph_outline (glyph, scale,
+                                           &temp,
+                                           sfntfont_get_glyph,
+                                           sfntfont_free_glyph,
+                                           &dcontext);
+      else
+       outline = sfnt_build_glyph_outline (glyph, scale,
+                                           &temp,
+                                           sfntfont_get_glyph,
+                                           sfntfont_free_glyph,
+                                           &dcontext);
+    }
+
+  /* At this point, the glyph metrics are unscaled.  Scale them up.
+     If INTERPRETER is set, use the scale placed within.  */
+
+  sfnt_scale_metrics (&temp, scale);
+
+ fail:
+
+  xfree (glyph);
+
+  if (!outline)
+    return NULL;
+
+  if (index != -1)
+    /* Finally, adjust the left side bearing of the glyph metrics by
+       the origin point of the outline, should a distortion have been
+       applied.  The left side bearing is the distance from the origin
+       point to the left most point on the X axis.  */
+    temp.lbearing = outline->xmin - outline->origin;
+
+  start = xmalloc (sizeof *start);
+  start->glyph = glyph_code;
+  start->outline = outline;
+  start->metrics = temp;
+
+  /* One reference goes to the cache.  The second reference goes to
+     the caller.  */
+  outline->refcount = 2;
+
+  /* Link start onto the cache.  */
+  start->next = cache->next;
+  start->last = cache;
+  start->next->last = start;
+  start->last->next = start;
+
+  /* Update the cache size.  */
+  (*cache_size)++;
+
+  /* Figure out if the least recently used element has to be
+     evicted.  */
+  if (*cache_size > SFNT_OUTLINE_CACHE_SIZE)
+    {
+      start = cache->last;
+      eassert (start != cache);
+
+      /* Free the least recently used entry in the cache.  */
+      start->last->next = start->next;
+      start->next->last = start->last;
+      sfntfont_dereference_outline (start->outline);
+      xfree (start);
+
+      (*cache_size)--;
+    }
+
+  /* Return the cached outline and metrics.  */
+
+  if (metrics)
+    *metrics = temp;
+
+  return outline;
+}
+
+/* Free the outline cache referred to by CACHE.  Dereference each
+   outline contained therein.  */
+
+static void
+sfntfont_free_outline_cache (struct sfnt_outline_cache *cache)
+{
+  struct sfnt_outline_cache *next, *last;
+
+  /* Handle partly initialized fonts.  */
+  if (!cache->next)
+    return;
+
+  for (next = cache->next; next != cache;)
+    {
+      last = next;
+      next = next->next;
+
+      sfntfont_dereference_outline (last->outline);
+      xfree (last);
+    }
+
+  cache->next = cache;
+  cache->last = cache;
+}
+
+/* Dereference the raster RASTER.  Free it once refcount reaches
+   0.  */
+
+static void
+sfntfont_dereference_raster (struct sfnt_raster *raster)
+{
+  eassert (raster->refcount > 0);
+
+  if (--raster->refcount)
+    return;
+
+  xfree (raster);
+}
+
+/* Get the raster corresponding to the specified GLYPH_CODE in CACHE.
+   Use the outline named OUTLINE.  Keep *CACHE_SIZE updated with the
+   number of elements in the cache.  */
+
+static struct sfnt_raster *
+sfntfont_get_glyph_raster (sfnt_glyph glyph_code,
+                          struct sfnt_raster_cache *cache,
+                          struct sfnt_glyph_outline *outline,
+                          int *cache_size)
+{
+  struct sfnt_raster_cache *start;
+  struct sfnt_raster *raster;
+
+  /* See if the raster is already cached.  */
+  start = cache->next;
+
+  for (; start != cache; start = start->next)
+    {
+      if (start->glyph == glyph_code)
+       {
+         /* Move start to the start of the ring.  Them, increase
+            start->raster->refcount and return it.  */
+
+         start->last->next = start->next;
+         start->next->last = start->last;
+
+         start->next = cache->next;
+         start->last = cache;
+         start->next->last = start;
+         start->last->next = start;
+         start->raster->refcount++;
+
+         return start->raster;
+       }
+    }
+
+  /* Not already cached.  Raster the outline.  */
+  raster = sfnt_raster_glyph_outline (outline);
+
+  if (!raster)
+    return NULL;
+
+  start = xmalloc (sizeof *start);
+  start->glyph = glyph_code;
+  start->raster = raster;
+
+  /* One reference goes to the cache.  The second reference goes to
+     the caller.  */
+  raster->refcount = 2;
+
+  /* Link start onto the cache.  */
+  start->next = cache->next;
+  start->last = cache;
+  start->next->last = start;
+  start->last->next = start;
+
+  /* Update the cache size.  */
+  (*cache_size)++;
+
+  /* Figure out if the least recently used element has to be
+     evicted.  */
+  if (*cache_size > SFNT_OUTLINE_CACHE_SIZE)
+    {
+      start = cache->last;
+      eassert (start != cache);
+
+      /* Free the least recently used entry in the cache.  */
+      start->last->next = start->next;
+      start->next->last = start->last;
+      sfntfont_dereference_raster (start->raster);
+      xfree (start);
+
+      (*cache_size)--;
+    }
+
+  /* Return the cached raster.  */
+  return raster;
+}
+
+/* Free the raster cache referred to by CACHE.  Dereference each
+   raster contained therein.  */
+
+static void
+sfntfont_free_raster_cache (struct sfnt_raster_cache *cache)
+{
+  struct sfnt_raster_cache *next, *last;
+
+  /* Handle partly initialized fonts.  */
+  if (!cache->next)
+    return;
+
+  for (next = cache->next; next != cache;)
+    {
+      last = next;
+      next = next->next;
+
+      sfntfont_dereference_raster (last->raster);
+      xfree (last);
+    }
+
+  cache->next = cache;
+  cache->last = cache;
+}
+
+
+
+/* Opening fonts.  */
+
+struct sfnt_font_info
+{
+  /* Parent font structure.  */
+  struct font font;
+
+#ifdef HAVE_MMAP
+  /* The next font in this chain.  */
+  struct sfnt_font_info *next;
+#endif /* HAVE_MMAP */
+
+  /* The font description used to create this font.  Used to
+     dereference tables associated with this font.  */
+  struct sfnt_font_desc *desc;
+
+  /* Various tables required to use the font.  */
+  struct sfnt_cmap_table *cmap;
+  struct sfnt_hhea_table *hhea;
+  struct sfnt_maxp_table *maxp;
+  struct sfnt_head_table *head;
+  struct sfnt_hmtx_table *hmtx;
+  struct sfnt_glyf_table *glyf;
+  struct sfnt_loca_table_short *loca_short;
+  struct sfnt_loca_table_long *loca_long;
+  struct sfnt_prep_table *prep;
+  struct sfnt_fpgm_table *fpgm;
+  struct sfnt_cvt_table *cvt;
+
+  /* The selected character map.  */
+  struct sfnt_cmap_encoding_subtable_data *cmap_data;
+
+  /* Data identifying that character map.  */
+  struct sfnt_cmap_encoding_subtable cmap_subtable;
+
+  /* The UVS context.  */
+  struct sfnt_uvs_context *uvs;
+
+  /* Outline cache.  */
+  struct sfnt_outline_cache outline_cache;
+
+  /* Number of elements in the outline cache.  */
+  int outline_cache_size;
+
+  /* Raster cache.  */
+  struct sfnt_raster_cache raster_cache;
+
+  /* Number of elements in the raster cache.  */
+  int raster_cache_size;
+
+  /* Interpreter for grid fitting (if enabled).  */
+  struct sfnt_interpreter *interpreter;
+
+  /* Graphics state after the execution of the font and control value
+     programs.  */
+  struct sfnt_graphics_state state;
+
+  /* Factor used to convert from em space to pixel space.  */
+  sfnt_fixed scale;
+
+  /* The blend (configuration of this multiple master font).  */
+  struct sfnt_blend blend;
+
+  /* The index of the named instance used to initialize BLEND.
+     -1 if BLEND is not initialized.  */
+  int instance;
+
+#ifdef HAVE_MMAP
+  /* Whether or not the glyph table has been mmapped.  */
+  bool glyf_table_mapped;
+#endif /* HAVE_MMAP */
+
+#ifdef HAVE_HARFBUZZ
+  /* HarfBuzz font object.  */
+  hb_font_t *hb_font;
+
+  /* File descriptor associated with this font.  */
+  int fd;
+
+  /* The table directory of the font file.  */
+  struct sfnt_offset_subtable *directory;
+#endif /* HAVE_HARFBUZZ */
+};
+
+#ifdef HAVE_MMAP
+
+/* List of all open fonts.  */
+
+static struct sfnt_font_info *open_fonts;
+
+#endif /* HAVE_MMAP */
+
+/* Look up the glyph corresponding to the character C in FONT.  Return
+   0 upon failure, and the glyph otherwise.  */
+
+static sfnt_glyph
+sfntfont_lookup_glyph (struct sfnt_font_info *font_info, int c)
+{
+  struct charset *charset;
+  sfnt_char character;
+  sfnt_glyph glyph;
+
+  charset = CHARSET_FROM_ID (font_info->font.encoding_charset);
+
+  if (!charset)
+    return 0;
+
+  character = ENCODE_CHAR (charset, c);
+
+  if (character == CHARSET_INVALID_CODE (charset))
+    return 0;
+
+  /* Do the actual lookup with the encoded character.  */
+  glyph = sfnt_lookup_glyph (character, font_info->cmap_data);
+
+  return glyph;
+}
+
+/* Probe and set FONT_INFO->font.average_width,
+   FONT_INFO->font.space_width, and FONT_INFO->font.min_width
+   according to the tables contained therein.  */
+
+static void
+sfntfont_probe_widths (struct sfnt_font_info *font_info)
+{
+  int i, num_characters, total_width;
+  sfnt_glyph glyph;
+  struct sfnt_glyph_metrics metrics;
+
+  num_characters = 0;
+  total_width = 0;
+
+  /* First set some reasonable default values.  */
+  font_info->font.average_width = font_info->font.pixel_size;
+  font_info->font.space_width = font_info->font.pixel_size;
+  font_info->font.min_width = 1;
+
+  /* Next, loop through the common ASCII characters.  Tally up their
+     advance widths and set space_width if necessary.  */
+  for (i = 0; i < 127; ++i)
+    {
+      glyph = sfntfont_lookup_glyph (font_info, i);
+
+      if (!glyph)
+       continue;
+
+      /* Now look up the metrics of this glyph.  */
+      if (sfnt_lookup_glyph_metrics (glyph, font_info->font.pixel_size,
+                                    &metrics, font_info->hmtx,
+                                    font_info->hhea, font_info->head,
+                                    font_info->maxp))
+       continue;
+
+      /* Increase the number of characters.  */
+      num_characters++;
+
+      /* Add the advance to total_width.  */
+      total_width += SFNT_CEIL_FIXED (metrics.advance) / 65536;
+
+      /* Update min_width if it hasn't been set yet or is wider.  */
+      if (font_info->font.min_width == 1
+         || font_info->font.min_width > metrics.advance / 65536)
+       font_info->font.min_width = metrics.advance / 65536;
+
+      /* If i is the space character, set the space width.  Make sure
+        to round this up.  */
+      if (i == 32)
+       font_info->font.space_width
+         = SFNT_CEIL_FIXED (metrics.advance) / 65536;
+    }
+
+  /* Now, if characters were found, set average_width.  */
+  if (num_characters)
+    font_info->font.average_width = total_width / num_characters;
+}
+
+/* Initialize the instruction interpreter for INFO.  Load the font and
+   preprogram for the pixel size in INFO and its corresponding point
+   size POINT_SIZE.  Use the FVAR table in DESC.
+
+   The font tables in INFO must already have been initialized.
+
+   Set INFO->interpreter upon success, and leave that field intact
+   otherwise.  */
+
+static void
+sfntfont_setup_interpreter (struct sfnt_font_info *info,
+                           struct sfnt_font_desc *desc,
+                           int point_size)
+{
+  struct sfnt_cvt_table *cvt;
+  struct sfnt_fpgm_table *fpgm;
+  struct sfnt_prep_table *prep;
+  struct sfnt_interpreter *interpreter;
+  const char *error;
+  struct sfnt_graphics_state state;
+
+  /* Load the cvt, fpgm and prep already read.  */
+
+  cvt  = info->cvt ;
+  fpgm = info->fpgm;
+  prep = info->prep;
+
+  /* If both fpgm and prep are NULL, this font likely has no
+     instructions, so don't bother setting up the interpreter.  */
+
+  if (!fpgm && !prep)
+    goto bail;
+
+  /* If the interpreter does not use the operand stack at all, it is
+     useless.  In addition, some broken fonts specify some unnecessary
+     instructions in prep and set head->max_stack_elements to 0.
+
+     Don't create the interpreter in that case.  */
+
+  if (!info->maxp->max_stack_elements)
+    goto bail;
+
+  /* Now, create the interpreter using the limits in info->maxp and
+     info->head.  CVT can be NULL.  */
+
+  interpreter = sfnt_make_interpreter (info->maxp, cvt, info->head,
+                                      desc->tables->fvar,
+                                      info->font.pixel_size,
+                                      point_size);
+
+  /* Bail if the interpreter couldn't be created.  */
+  if (!interpreter)
+    goto bail;
+
+  if (fpgm)
+    {
+      /* Otherwise, evaluate the font and cvt programs.
+
+        FIXME: make sure infinite loops inside these programs
+        cannot lock up Emacs.  */
+
+      error = sfnt_interpret_font_program (interpreter, fpgm);
+
+      if (error)
+       {
+         /* If an error occurs, log it to the *Messages* buffer.  */
+         message_with_string ("While interpreting font program: %s",
+                              build_string (error), true);
+         goto bail1;
+       }
+
+      /* Save the graphics state.  */
+      state = interpreter->state;
+    }
+
+  if (prep)
+    {
+      /* This will overwrite state if the instruction control is set
+        appropriately.  */
+      error = sfnt_interpret_control_value_program (interpreter, prep,
+                                                   &state);
+
+      if (error)
+       {
+         /* If an error occurs, log it to the *Messages* buffer.  */
+         message_with_string ("While interpreting preprogram: %s",
+                              build_string (error), true);
+         goto bail1;
+       }
+    }
+
+  /* The interpreter has been properly set up.  */
+  info->fpgm = fpgm;
+  info->prep = prep;
+  info->cvt = cvt;
+  info->state = state;
+  info->interpreter = interpreter;
+
+  return;
+
+ bail1:
+  xfree (interpreter);
+ bail:
+  return;
+}
+
+/* Free each of the tables opened by `sfnt_open_tables', and possibly
+   file descriptors as well.  Then, free TABLES itself.  */
+
+static void
+sfnt_close_tables (struct sfnt_font_tables *tables)
+{
+  int rc;
+
+  xfree (tables->cmap);
+  xfree (tables->hhea);
+  xfree (tables->maxp);
+  xfree (tables->head);
+  xfree (tables->hmtx);
+#ifdef HAVE_MMAP
+  if (tables->glyf_table_mapped)
+    {
+      rc = sfnt_unmap_glyf_table (tables->glyf);
+
+      if (rc)
+       emacs_abort ();
+    }
+  else
+#endif /* HAVE_MMAP */
+    xfree (tables->glyf);
+  xfree (tables->loca_short);
+  xfree (tables->loca_long);
+  xfree (tables->prep);
+  xfree (tables->fpgm);
+  xfree (tables->cvt);
+  xfree (tables->fvar);
+  xfree (tables->avar);
+  xfree (tables->gvar);
+  xfree (tables->cvar);
+  xfree (tables->cmap_data);
+
+  if (tables->uvs)
+    sfnt_free_uvs_context (tables->uvs);
+
+#ifdef HAVE_HARFBUZZ
+  /* Close the font file.  */
+
+  if (tables->fd != -1)
+    {
+      emacs_close (tables->fd);
+      tables->fd = -1;
+    }
+
+  /* Free its table directory.  */
+  xfree (tables->directory);
+  tables->directory = NULL;
+#endif
+}
+
+/* Open font tables associated with the specified font description
+   DESC.  Return the font tables, or NULL upon failure.  */
+
+static struct sfnt_font_tables *
+sfnt_open_tables (struct sfnt_font_desc *desc)
+{
+  struct sfnt_font_tables *tables;
+  struct sfnt_offset_subtable *subtable;
+  int fd, i, rc;
+  struct sfnt_cmap_encoding_subtable *subtables;
+  struct sfnt_cmap_encoding_subtable_data **data;
+  struct sfnt_cmap_format_14 *format14;
+
+  tables = xzalloc (sizeof *tables);
+
+  /* Open the font.  */
+  fd = emacs_open (desc->path, O_RDONLY, 0);
+
+  if (fd == -1)
+    goto bail;
+
+  /* Seek to the offset specified to the table directory.  */
+
+  if (desc->offset
+      && lseek (fd, desc->offset, SEEK_SET) != desc->offset)
+    goto bail;
+
+  /* Read the offset subtable.  */
+  subtable = sfnt_read_table_directory (fd);
+
+  if (!subtable)
+    goto bail1;
+
+  /* Read required tables.  This font backend is supposed to be used
+     mostly on devices with flash memory, so the order in which they
+     are read is insignificant.  */
+
+  tables->cmap = sfnt_read_cmap_table (fd, subtable, &subtables,
+                                      &data);
+  if (!tables->cmap)
+    goto bail2;
+
+  format14 = NULL;
+  tables->cmap_data
+    = sfntfont_select_cmap (tables->cmap,
+                           subtables, data,
+                           &tables->cmap_subtable,
+                           &format14);
+
+  if (format14)
+    {
+      /* Build a UVS context from this format 14 mapping table.  A UVS
+         context contains each variation selector supported by the
+         font, and a list of ``non-default'' mappings between base
+         characters and variation glyph IDs.  */
+
+      tables->uvs = sfnt_create_uvs_context (format14, fd);
+      xfree (format14);
+    }
+
+  for (i = 0; i < tables->cmap->num_subtables; ++i)
+    {
+      if (data[i] != tables->cmap_data
+         /* format14 has already been freed.  */
+         && data[i] != (struct sfnt_cmap_encoding_subtable_data *) format14)
+       xfree (data[i]);
+    }
+
+  xfree (subtables);
+  xfree (data);
+
+  if (!tables->cmap_data)
+    goto bail3;
+
+  /* Read the hhea, maxp, glyf, and head tables.  */
+  tables->hhea = sfnt_read_hhea_table (fd, subtable);
+  tables->maxp = sfnt_read_maxp_table (fd, subtable);
+
+#ifdef HAVE_MMAP
+
+  /* First try to map the glyf table.  If that fails, then read the
+     glyf table.  */
+
+  tables->glyf = sfnt_map_glyf_table (fd, subtable);
+
+  /* Next, if this fails, read the glyf table.  */
+
+  if (!tables->glyf)
+#endif /* HAVE_MMAP */
+    tables->glyf = sfnt_read_glyf_table (fd, subtable);
+#ifdef HAVE_MMAP
+  else
+    tables->glyf_table_mapped = true;
+#endif /* HAVE_MMAP */
+
+  tables->head = sfnt_read_head_table (fd, subtable);
+
+  /* If any of those tables couldn't be read, bail.  */
+  if (!tables->hhea || !tables->maxp || !tables->glyf
+      || !tables->head)
+    goto bail4;
+
+  /* Now figure out which kind of loca table must be read based on
+     head->index_to_loc_format.  */
+
+  if (tables->head->index_to_loc_format)
+    {
+      tables->loca_long
+       = sfnt_read_loca_table_long (fd, subtable);
+
+      if (!tables->loca_long)
+       goto bail4;
+    }
+  else
+    {
+      tables->loca_short
+       = sfnt_read_loca_table_short (fd, subtable);
+
+      if (!tables->loca_short)
+       goto bail4;
+    }
+
+  /* Read the horizontal metrics table.  */
+  tables->hmtx = sfnt_read_hmtx_table (fd, subtable,
+                                      tables->hhea,
+                                      tables->maxp);
+  if (!tables->hmtx)
+    goto bail5;
+
+  /* Read instruction related font tables.  These might not be
+     present, which is OK, since instructing fonts is optional.  */
+  tables->prep = sfnt_read_prep_table (fd, subtable);
+  tables->fpgm = sfnt_read_fpgm_table (fd, subtable);
+  tables->cvt  = sfnt_read_cvt_table (fd, subtable);
+
+  /* Read distortion related tables.  These might not be present.  */
+  tables->fvar = sfnt_read_fvar_table (fd, subtable);
+  tables->avar = sfnt_read_avar_table (fd, subtable);
+  tables->gvar = sfnt_read_gvar_table (fd, subtable);
+
+  if (tables->cvt && tables->fvar)
+    tables->cvar = sfnt_read_cvar_table (fd, subtable, tables->fvar,
+                                        tables->cvt);
+
+#ifdef HAVE_HARFBUZZ
+  /* Now copy over the subtable if necessary, as it is needed to read
+     extra font tables required by HarfBuzz.  */
+  tables->directory = subtable;
+  tables->fd = fd;
+#else /* !HAVE_HARFBUZZ */
+  /* Otherwise, close the fd and free the table directory.  */
+  xfree (subtable);
+  emacs_close (fd);
+#endif /* HAVE_HARFBUZZ */
+
+  return tables;
+
+ bail5:
+  xfree (tables->loca_long);
+  xfree (tables->loca_short);
+ bail4:
+  xfree (tables->hhea);
+  xfree (tables->maxp);
+
+#ifdef HAVE_MMAP
+  if (tables->glyf_table_mapped)
+    {
+      rc = sfnt_unmap_glyf_table (tables->glyf);
+
+      if (rc)
+       emacs_abort ();
+    }
+  else
+#endif /* HAVE_MMAP */
+    xfree (tables->glyf);
+
+  xfree (tables->head);
+
+  /* This comes under bail4 due to a peculiarity of how the four
+     tables above are validated.  */
+  xfree (tables->cmap_data);
+ bail3:
+  if (tables->uvs)
+    sfnt_free_uvs_context (tables->uvs);
+
+  xfree (tables->cmap);
+ bail2:
+  xfree (subtable);
+ bail1:
+  emacs_close (fd);
+ bail:
+  xfree (tables);
+  return NULL;
+}
+
+/* Open or reference font tables corresponding to the specified font
+   DESC.  Return NULL upon failure.  */
+
+static struct sfnt_font_tables *
+sfnt_reference_font_tables (struct sfnt_font_desc *desc)
+{
+  if (desc->refcount)
+    {
+      desc->refcount++;
+      return desc->tables;
+    }
+
+  desc->tables = sfnt_open_tables (desc);
+
+  if (!desc->tables)
+    return NULL;
+
+  desc->refcount++;
+  return desc->tables;
+}
+
+/* Dereference font tables corresponding to the specified font
+   DESC.  */
+
+static void
+sfnt_dereference_font_tables (struct sfnt_font_desc *desc)
+{
+  if (!desc->refcount)
+    emacs_abort ();
+
+  if (--desc->refcount)
+    return;
+
+  sfnt_close_tables (desc->tables);
+  desc->tables = NULL;
+  return;
+}
+
+/* Open the font corresponding to the font-entity FONT_ENTITY.  Return
+   nil upon failure, else the opened font-object.  */
+
+Lisp_Object
+sfntfont_open (struct frame *f, Lisp_Object font_entity,
+              int pixel_size)
+{
+  struct sfnt_font_info *font_info;
+  struct font *font;
+  struct sfnt_font_desc *desc;
+  Lisp_Object font_object;
+  struct charset *charset;
+  int point_size, instance, i;
+  Display_Info *dpyinfo;
+  struct sfnt_font_tables *tables;
+  Lisp_Object tem;
+
+  if (XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)) != 0)
+    pixel_size = XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX));
+  else if (pixel_size == 0)
+    {
+      /* This bit was copied from xfont.c.  The values might need
+        adjustment.  */
+
+      if (FRAME_FONT (f))
+       pixel_size = FRAME_FONT (f)->pixel_size;
+      else
+       pixel_size = 12;
+    }
+
+  /* Now find the font description corresponding to FONT_ENTITY.  */
+
+  tem = AREF (font_entity, FONT_EXTRA_INDEX);
+  if (NILP (tem))
+    return Qnil;
+
+  desc = xmint_pointer (XCDR (XCAR (tem)));
+
+  /* Finally, see if a specific instance is associated with
+     FONT_ENTITY.  */
+
+  instance = -1;
+  if (!NILP (XCDR (tem)))
+    instance = XFIXNUM (XCDR (XCAR (XCDR (tem))));
+
+  /* Build the font object.  */
+  font_object = font_make_object (VECSIZE (struct sfnt_font_info),
+                                 font_entity, pixel_size);
+  font_info = (struct sfnt_font_info *) XFONT_OBJECT (font_object);
+
+  block_input ();
+
+  /* Initialize all the font driver specific data.  */
+
+  font_info->cmap = NULL;
+  font_info->hhea = NULL;
+  font_info->maxp = NULL;
+  font_info->head = NULL;
+  font_info->glyf = NULL;
+  font_info->hmtx = NULL;
+  font_info->loca_short = NULL;
+  font_info->loca_long = NULL;
+  font_info->cmap_data = NULL;
+  font_info->prep = NULL;
+  font_info->fpgm = NULL;
+  font_info->cvt = NULL;
+  font_info->uvs = NULL;
+
+  font_info->outline_cache.next = &font_info->outline_cache;
+  font_info->outline_cache.last = &font_info->outline_cache;
+  font_info->outline_cache_size = 0;
+  font_info->raster_cache.next = &font_info->raster_cache;
+  font_info->raster_cache.last = &font_info->raster_cache;
+  font_info->raster_cache_size = 0;
+  font_info->interpreter = NULL;
+  font_info->scale = 0;
+  font_info->instance = -1;
+  font_info->blend.coords = NULL;
+#ifdef HAVE_MMAP
+  font_info->glyf_table_mapped = false;
+#endif /* HAVE_MMAP */
+#ifdef HAVE_HARFBUZZ
+  font_info->hb_font = NULL;
+  font_info->fd = -1;
+  font_info->directory = NULL;
+#endif /* HAVE_HARFBUZZ */
+
+  /* Read required tables.  This font backend is supposed to be used
+     mostly on devices with flash memory, so the order in which they
+     are read is insignificant.  */
+
+  tables = sfnt_reference_font_tables (desc);
+
+  if (!tables)
+    goto bail;
+
+  /* Copy fields from the table structure to the font for fast
+     access.  */
+  font_info->cmap = tables->cmap;
+  font_info->hhea = tables->hhea;
+  font_info->maxp = tables->maxp;
+  font_info->head = tables->head;
+  font_info->hmtx = tables->hmtx;
+  font_info->glyf = tables->glyf;
+  font_info->loca_short = tables->loca_short;
+  font_info->loca_long = tables->loca_long;
+  font_info->prep = tables->prep;
+  font_info->fpgm = tables->fpgm;
+  font_info->cvt  = tables->cvt ;
+  font_info->cmap_data = tables->cmap_data;
+  font_info->cmap_subtable = tables->cmap_subtable;
+  font_info->uvs = tables->uvs;
+
+  /* Calculate the font's scaling factor.  */
+  font_info->scale = sfnt_get_scale (font_info->head, pixel_size);
+
+  /* Fill in font data.  */
+  font = &font_info->font;
+  font->pixel_size = pixel_size;
+  font->driver = sfnt_font_driver;
+  font->encoding_charset = font->repertory_charset = -1;
+
+  /* Figure out which character set to use.  */
+  charset = sfntfont_charset_for_cmap (font_info->cmap_subtable);
+
+  if (!charset)
+    goto bail6;
+
+  /* Set the character set IDs.  */
+  font->encoding_charset = charset->id;
+  font->repertory_charset = charset->id;
+
+  /* Figure out the font ascent and descent.  */
+  font->ascent
+    = ceil (font_info->hhea->ascent
+           * pixel_size * 1.0 / font_info->head->units_per_em);
+  font->descent
+    = -floor (font_info->hhea->descent
+             * pixel_size * 1.0 / font_info->head->units_per_em);
+  font->height = font->ascent + font->descent;
+
+  /* Set font->max_width to the maximum advance width.  */
+  font->max_width = (font_info->hhea->advance_width_max
+                    * pixel_size * 1.0 / font_info->head->units_per_em);
+
+  /* Set generic attributes such as type and style.  */
+  ASET (font_object, FONT_TYPE_INDEX, sfnt_vendor_name);
+
+  if (!NILP (desc->designer))
+    ASET (font_object, FONT_FOUNDRY_INDEX,
+         Fintern (desc->designer, Qnil));
+
+  ASET (font_object, FONT_FAMILY_INDEX, Fintern (desc->family, Qnil));
+  ASET (font_object, FONT_ADSTYLE_INDEX, Qnil);
+  ASET (font_object, FONT_REGISTRY_INDEX,
+       sfntfont_registry_for_desc (desc));
+
+  /* Size of 0 means the font is scalable.  */
+  ASET (font_object, FONT_SIZE_INDEX, make_fixnum (0));
+  ASET (font_object, FONT_AVGWIDTH_INDEX, make_fixnum (0));
+  ASET (font_object, FONT_SPACING_INDEX,
+       make_fixnum (desc->spacing));
+
+  /* Set the font style.  */
+
+  FONT_SET_STYLE (font_object, FONT_WIDTH_INDEX,
+                 make_fixnum (desc->width));
+  FONT_SET_STYLE (font_object, FONT_WEIGHT_INDEX,
+                 make_fixnum (desc->weight));
+  FONT_SET_STYLE (font_object, FONT_SLANT_INDEX,
+                 make_fixnum (desc->slant));
+
+  ASET (font_object, FONT_ADSTYLE_INDEX, Qnil);
+
+  /* Find out the minimum, maximum and average widths.  */
+  sfntfont_probe_widths (font_info);
+
+  /* Clear various offsets.  */
+  font_info->font.baseline_offset = 0;
+  font_info->font.relative_compose = 0;
+  font_info->font.default_ascent = 0;
+  font_info->font.vertical_centering = 0;
+  font_info->font.underline_position = -1;
+  font_info->font.underline_thickness = 0;
+
+  /* Now try to set up grid fitting for this font.  */
+  dpyinfo = FRAME_DISPLAY_INFO (f);
+  point_size = PIXEL_TO_POINT (pixel_size, (dpyinfo->resx
+                                           * dpyinfo->resy
+                                           / 2));
+  sfntfont_setup_interpreter (font_info, desc, point_size);
+
+  /* If an instance was specified and the font is distortable, set up
+     the blend.  */
+
+  if (instance != -1
+      && desc->tables->fvar && desc->tables->gvar
+      /* Make sure the instance is within range.  */
+      && instance < desc->tables->fvar->instance_count)
+    {
+      tem = AREF (desc->instances, instance);
+
+      if (!NILP (tem))
+       {
+         sfnt_init_blend (&font_info->blend, desc->tables->fvar,
+                          desc->tables->gvar, desc->tables->avar,
+                          desc->tables->cvar);
+
+         /* Copy over the coordinates.  */
+         for (i = 0; i < desc->tables->fvar->axis_count; ++i)
+           font_info->blend.coords[i]
+             = desc->tables->fvar->instance[instance].coords[i];
+
+         sfnt_normalize_blend (&font_info->blend);
+
+         /* Test whether or not the instance is actually redundant,
+            as all of its axis are at their default values.  If so,
+            free the instance.  */
+
+         for (i = 0; i < desc->tables->fvar->axis_count; ++i)
+           {
+             if (font_info->blend.norm_coords[i])
+               break;
+           }
+
+         if (i == desc->tables->fvar->axis_count)
+           {
+             sfnt_free_blend (&font_info->blend);
+             goto cancel_blend;
+           }
+
+         /* If an interpreter was specified, distort it now.  */
+
+         if (font_info->interpreter)
+           sfnt_vary_interpreter (font_info->interpreter,
+                                  &font_info->blend);
+
+         font_info->instance = instance;
+
+         /* Replace the style information with that of the
+            instance.  */
+
+         FONT_SET_STYLE (font_object, FONT_WIDTH_INDEX,
+                         AREF (tem, 2));
+         FONT_SET_STYLE (font_object, FONT_WEIGHT_INDEX,
+                         AREF (tem, 3));
+         FONT_SET_STYLE (font_object, FONT_SLANT_INDEX,
+                         AREF (tem, 4));
+         ASET (font_object, FONT_ADSTYLE_INDEX, Qnil);   
+       }
+    }
+
+ cancel_blend:
+  /* Calculate the xfld name.  */
+  font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil);
+
+#ifdef HAVE_HARFBUZZ
+  /* HarfBuzz will potentially read font tables after the font has
+     been opened by Emacs.  Keep the font open, and record its offset
+     subtable.  */
+  font_info->fd = tables->fd;
+  font_info->directory = tables->directory;
+#endif /* HAVE_HARFBUZZ */
+
+  /* Set font->desc so that font tables can be dereferenced if
+     anything goes wrong.  */
+  font_info->desc = desc;
+
+#ifdef HAVE_MMAP
+  /* Link the font onto the font table.  */
+  font_info->next = open_fonts;
+  open_fonts = font_info;
+#endif /* HAVE_MMAP */
+
+  /* All done.  */
+  unblock_input ();
+  return font_object;
+
+ bail6:
+  sfnt_dereference_font_tables (desc);
+  font_info->desc = NULL;
+ bail:
+  unblock_input ();
+  return Qnil;
+}
+
+
+
+/* Metrics computation and other similar font backend functions.  */
+
+/* Return the glyph code corresponding to C inside the font-object
+   FONT.  Value is the glyph code upon success, else
+   FONT_INVALID_CODE.  */
+
+unsigned int
+sfntfont_encode_char (struct font *font, int c)
+{
+  sfnt_glyph glyph;
+
+  /* Now look up the glyph.  */
+  glyph = sfntfont_lookup_glyph ((struct sfnt_font_info *) font, c);
+
+  if (!glyph)
+    return FONT_INVALID_CODE;
+
+  return glyph;
+}
+
+/* Measure the single glyph GLYPH in the font FONT and return its
+   metrics in *PCM.
+
+   Instruct the glyph if possible.
+
+   Value is 0 upon success, 1 otherwise.  */
+
+static int
+sfntfont_measure_pcm (struct sfnt_font_info *font, sfnt_glyph glyph,
+                     struct font_metrics *pcm)
+{
+  struct sfnt_glyph_metrics metrics;
+  struct sfnt_glyph_outline *outline;
+
+  /* Now get the glyph outline, which is required to obtain the rsb,
+     ascent and descent.  */
+  outline = sfntfont_get_glyph_outline (glyph, &font->outline_cache,
+                                       font->scale,
+                                       &font->outline_cache_size,
+                                       &font->blend,
+                                       font->instance,
+                                       font->glyf, font->head,
+                                       font->hmtx, font->hhea,
+                                       font->maxp,
+                                       font->loca_short,
+                                       font->loca_long,
+                                       font->interpreter, &metrics,
+                                       &font->state);
+
+  if (!outline)
+    return 1;
+
+  /* Round the left side bearing downwards.  */
+  pcm->lbearing = SFNT_FLOOR_FIXED (metrics.lbearing) / 65536;
+  pcm->rbearing = SFNT_CEIL_FIXED (outline->xmax) / 65536;
+
+  /* Round the advance, ascent and descent upwards.  */
+  pcm->width = SFNT_CEIL_FIXED (metrics.advance) / 65536;
+  pcm->ascent = SFNT_CEIL_FIXED (outline->ymax) / 65536;
+  pcm->descent = SFNT_CEIL_FIXED (-outline->ymin) / 65536;
+
+  sfntfont_dereference_outline (outline);
+  return 0;
+}
+
+/* Return the total text extents of NGLYPHS glyphs given as CODE in
+   the single font metrics array METRICS.  */
+
+void
+sfntfont_text_extents (struct font *font, const unsigned int *code,
+                      int nglyphs, struct font_metrics *metrics)
+{
+  int i, total_width;
+  struct font_metrics pcm;
+
+  total_width = 0;
+
+  /* First clear the metrics array.  */
+  memset (metrics, 0, sizeof *metrics);
+
+  /* Get the metrcs one by one, then sum them up.  */
+  for (i = 0; i < nglyphs; ++i)
+    {
+      if (!sfntfont_measure_pcm ((struct sfnt_font_info *) font,
+                                code[i], &pcm))
+       {
+         /* Add the per-char metric (PCM) to the metrics in
+            METRICS.  */
+
+         if (total_width + pcm.lbearing < metrics->lbearing)
+           metrics->lbearing = total_width + pcm.lbearing;
+
+         if (total_width + pcm.rbearing > metrics->rbearing)
+           metrics->rbearing = total_width + pcm.rbearing;
+
+         if (pcm.ascent > metrics->ascent)
+           metrics->ascent = pcm.ascent;
+
+         if (pcm.descent > metrics->descent)
+           metrics->descent = pcm.descent;
+
+         total_width += pcm.width;
+       }
+    }
+
+  metrics->width = total_width;
+}
+
+/* Close the font FONT, discarding all tables inside it and
+   dereferencing all cached outlines and rasters.  */
+
+void
+sfntfont_close (struct font *font)
+{
+  struct sfnt_font_info *info;
+#ifdef HAVE_MMAP
+  struct sfnt_font_info **next;
+#endif /* HAVE_MMAP */
+
+  info = (struct sfnt_font_info *) font;
+
+  /* If info->desc is still set, dereference the font tables.  */
+  if (info->desc)
+    sfnt_dereference_font_tables (info->desc);
+  info->desc = NULL;
+
+  /* Free the interpreter, which is created on a per font basis.  */
+  xfree (info->interpreter);
+
+  /* Clear these fields.  It seems that close can be called twice,
+     once during font driver destruction, and once during GC.  */
+
+  info->cmap = NULL;
+  info->hhea = NULL;
+  info->maxp = NULL;
+  info->head = NULL;
+  info->hhea = NULL;
+  info->glyf = NULL;
+  info->loca_short = NULL;
+  info->loca_long = NULL;
+  info->cmap_data = NULL;
+  info->prep = NULL;
+  info->fpgm = NULL;
+  info->cvt = NULL;
+  info->interpreter = NULL;
+  info->uvs = NULL;
+
+  /* Deinitialize the blend.  */
+  if (info->instance != -1 && info->blend.coords)
+    sfnt_free_blend (&info->blend);
+  info->instance = -1;
+
+#ifdef HAVE_MMAP
+
+  /* Unlink INFO.  */
+
+  next = &open_fonts;
+  while (*next && (*next) != info)
+    next = &(*next)->next;
+
+  if (*next)
+    *next = info->next;
+  info->next = NULL;
+
+#endif /* HAVE_MMAP */
+
+#ifdef HAVE_HARFBUZZ
+  /* These fields will be freed or closed by
+     sfnt_dereference_font_tables, but clear them here for good
+     measure.  */
+  info->directory = NULL;
+  info->fd = -1;
+
+  /* Free any hb_font created.  */
+
+  if (info->hb_font)
+    {
+      hb_font_destroy (info->hb_font);
+      info->hb_font = NULL;
+    }
+#endif
+
+  sfntfont_free_outline_cache (&info->outline_cache);
+  sfntfont_free_raster_cache (&info->raster_cache);
+}
+
+
+
+/* Glyph display.  */
+
+/* Function called to actually draw rasters to the glass.  */
+static sfntfont_put_glyph_proc sfnt_put_glyphs;
+
+/* Draw glyphs in S->char2b starting from FROM to TO, with the origin
+   at X and baseline at Y.  Fill the background from X, Y +
+   FONT_DESCENT to X + S->background_width, Y - FONT_ASCENT with the
+   background color if necessary.  Use the foreground and background
+   colors in S->gc.  */
+
+int
+sfntfont_draw (struct glyph_string *s, int from, int to,
+              int x, int y, bool with_background)
+{
+  int length;
+  struct sfnt_raster **rasters;
+  int *x_coords, current_x, i;
+  struct sfnt_glyph_outline *outline;
+  struct font *font;
+  struct sfnt_font_info *info;
+  struct sfnt_glyph_metrics metrics;
+
+  length = to - from;
+  font = s->font;
+  info = (struct sfnt_font_info *) font;
+
+  rasters = alloca (length * sizeof *rasters);
+  x_coords = alloca (length * sizeof *x_coords);
+  current_x = x;
+
+  /* Get rasters and outlines for them.  */
+  for (i = from; i < to; ++i)
+    {
+      /* Look up the outline.  */
+      outline = sfntfont_get_glyph_outline (s->char2b[i],
+                                           &info->outline_cache,
+                                           info->scale,
+                                           &info->outline_cache_size,
+                                           &info->blend,
+                                           info->instance,
+                                           info->glyf, info->head,
+                                           info->hmtx, info->hhea,
+                                           info->maxp,
+                                           info->loca_short,
+                                           info->loca_long,
+                                           info->interpreter,
+                                           &metrics,
+                                           &info->state);
+      x_coords[i - from] = 0;
+
+      if (!outline)
+       {
+         rasters[i - from] = NULL;
+         continue;
+       }
+
+      /* Rasterize the outline.  */
+      rasters[i - from] = sfntfont_get_glyph_raster (s->char2b[i],
+                                                    &info->raster_cache,
+                                                    outline,
+                                                    &info->raster_cache_size);
+      sfntfont_dereference_outline (outline);
+
+      if (!rasters[i - from])
+       continue;
+
+      /* Now work out where to put the outline.  */
+      x_coords[i - from] = current_x;
+
+      if (s->padding_p)
+       current_x += 1;
+      else
+       current_x += SFNT_CEIL_FIXED (metrics.advance) / 65536;
+    }
+
+  /* Call the window system function to put the glyphs to the
+     frame.  */
+  sfnt_put_glyphs (s, from, to, x, y, with_background,
+                  rasters, x_coords);
+
+  /* Dereference all the rasters.  */
+  for (i = 0; i < from - to; ++i)
+    {
+      if (rasters[i])
+       sfntfont_dereference_raster (rasters[i]);
+    }
+
+  return 1;
+}
+
+
+
+/* Other callbacks.  */
+
+/* Return a list of each font family known to Emacs.  F is supposed to
+   be a frame but is ignored.  */
+
+Lisp_Object
+sfntfont_list_family (struct frame *f)
+{
+  Lisp_Object families;
+  struct sfnt_font_desc *desc;
+
+  families = Qnil;
+
+  for (desc = system_fonts; desc; desc = desc->next)
+    /* Add desc->family to the list.  */
+    families = Fcons (desc->family, families);
+
+  /* Not sure if deleting duplicates is worth it.  Is this ever
+     called? */
+  return families;
+}
+
+
+
+/* Unicode Variation Selector (UVS) support.  This is typically
+   required for Harfbuzz.  */
+
+/* Given a FONT object, a character C, and VARIATIONS, return the
+   number of non-default variation glyphs, and their glyph ids in
+   VARIATIONS.
+
+   For each variation selector character K with a non-default glyph in
+   the variation selector range 0xFE00 to 0xFE0F, set variations[K -
+   0xFE0] to its ID.
+
+   For each variation selector character K with a non-default glyph in
+   the variation selector range 0xE0100 to 0xE01EF, set variations[K -
+   0xE0100 + 16] to its ID.
+
+   If value is more than 0, set all other members of VARIATIONS to 0.
+   Else, the contents of VARIATIONS are undefined.  */
+
+int
+sfntfont_get_variation_glyphs (struct font *font, int c,
+                              unsigned variations[256])
+{
+  struct sfnt_font_info *info;
+  size_t i;
+  int n;
+  struct sfnt_mapped_variation_selector_record *record;
+
+  info = (struct sfnt_font_info *) font;
+  n = 0;
+
+  /* Return 0 if there is no UVS mapping table.  */
+
+  if (!info->uvs)
+    return 0;
+
+  /* Clear the variations array.  */
+
+  memset (variations, 0, sizeof *variations * 256);
+
+  /* Find the first 0xFExx selector.  */
+
+  i = 0;
+  while (i < info->uvs->num_records
+        && info->uvs->records[i].selector < 0xfe00)
+    ++i;
+
+  /* Fill in selectors 0 to 15.  */
+
+  while (i < info->uvs->num_records
+        && info->uvs->records[i].selector <= 0xfe0f)
+    {
+      record = &info->uvs->records[i];
+
+      /* If record has no non-default mappings, continue on to the
+        next selector.  */
+
+      if (!record->nondefault_uvs)
+       goto next_selector;
+
+      /* Handle invalid unsorted tables.  */
+
+      if (record->selector < 0xfe00)
+       return 0;
+
+      /* Find the glyph ID associated with C and put it in
+        VARIATIONS.  */
+
+      variations[info->uvs->records[i].selector - 0xfe00]
+       = sfnt_variation_glyph_for_char (record->nondefault_uvs, c);
+
+      if (variations[info->uvs->records[i].selector - 0xfe00])
+       ++n;
+
+    next_selector:
+      ++i;
+    }
+
+  /* Find the first 0xE0100 selector.  */
+
+  i = 0;
+  while (i < info->uvs->num_records
+        && info->uvs->records[i].selector < 0xe0100)
+    ++i;
+
+  /* Fill in selectors 16 to 255.  */
+
+  while (i < info->uvs->num_records
+        && info->uvs->records[i].selector <= 0xe01ef)
+    {
+      record = &info->uvs->records[i];
+
+      /* If record has no non-default mappings, continue on to the
+        next selector.  */
+
+      if (!record->nondefault_uvs)
+       goto next_selector_1;
+
+      /* Handle invalid unsorted tables.  */
+
+      if (record->selector < 0xe0100)
+       return 0;
+
+      /* Find the glyph ID associated with C and put it in
+        VARIATIONS.  */
+
+      variations[info->uvs->records[i].selector - 0xe0100 + 16]
+       = sfnt_variation_glyph_for_char (record->nondefault_uvs, c);
+
+      if (variations[info->uvs->records[i].selector - 0xe0100 + 16])
+       ++n;
+
+    next_selector_1:
+      ++i;
+    }
+
+  return n;
+}
+
+
+
+/* mmap specific stuff.  */
+
+#ifdef HAVE_MMAP
+
+/* Return whether or not ADDR lies in a mapped glyph, and bus faults
+   should be ignored.  */
+
+bool
+sfntfont_detect_sigbus (void *addr)
+{
+  struct sfnt_font_info *info;
+
+  for (info = open_fonts; info; info = info->next)
+    {
+      if (info->glyf_table_mapped
+         && (unsigned char *) addr >= info->glyf->glyphs
+         && (unsigned char *) addr < (info->glyf->glyphs
+                                      + info->glyf->size))
+       return true;
+    }
+
+  return false;
+}
+
+#endif
+
+
+
+/* Harfbuzz font support.  */
+
+#ifdef HAVE_HARFBUZZ
+
+#ifdef HAVE_MMAP
+
+/* Unmap the specified table.  */
+
+static void
+sfntfont_unmap_blob (void *ptr)
+{
+  if (sfnt_unmap_table (ptr))
+    emacs_abort ();
+
+  xfree (ptr);
+}
+
+#endif /* HAVE_MMAP */
+
+/* Given a font DATA and a tag TAG, return the data of the
+   corresponding font table as a HarfBuzz blob.  */
+
+static hb_blob_t *
+sfntfont_get_font_table (hb_face_t *face, hb_tag_t tag, void *data)
+{
+  size_t size;
+  struct sfnt_font_info *info;
+#ifdef HAVE_MMAP
+  struct sfnt_mapped_table *table;
+  hb_blob_t *blob;
+
+  info = data;
+  table = xmalloc (sizeof *table);
+
+  if (!sfnt_map_table (info->fd, info->directory, tag,
+                      table))
+    {
+      /* Create an hb_blob_t and return it.
+         TODO: record this mapping properly so that SIGBUS can
+        be handled.  */
+
+      blob = hb_blob_create (table->data, table->length,
+                            HB_MEMORY_MODE_READONLY,
+                            table, sfntfont_unmap_blob);
+
+      /* Note that sfntfont_unmap_blob will be called if the empty
+        blob is returned.  */
+      return blob;
+    }
+
+  xfree (table);
+#else /* !HAVE_MMAP */
+
+  /* Try to read the table conventionally.  */
+  info = data;
+#endif /* HAVE_MMAP */
+
+  data = sfnt_read_table (info->fd, info->directory, tag,
+                         &size);
+
+  if (!data)
+    return NULL;
+
+  return hb_blob_create (data, size, HB_MEMORY_MODE_WRITABLE,
+                        data, xfree);
+}
+
+/* Create or return a HarfBuzz font object corresponding to the
+   specified FONT.  Return the scale to convert between fwords and
+   pixels in POSITION_UNIT.  */
+
+hb_font_t *
+sfntfont_begin_hb_font (struct font *font, double *position_unit)
+{
+  struct sfnt_font_info *info;
+  hb_face_t *face;
+  int factor;
+
+  info = (struct sfnt_font_info *) font;
+
+  if (info->hb_font)
+    {
+      /* Calculate the scale factor.  */
+      *position_unit = 1.0 / 64.0;
+      return info->hb_font;
+    }
+
+  /* Create a face and then a font.  */
+  face = hb_face_create_for_tables (sfntfont_get_font_table, font,
+                                   NULL);
+
+  if (hb_face_get_glyph_count (face) > 0)
+    {
+      info->hb_font = hb_font_create (face);
+      if (!info->hb_font)
+       goto bail;
+
+      factor = font->pixel_size;
+
+      /* Set the scale and PPEM values.  */
+      hb_font_set_scale (info->hb_font, factor * 64, factor * 64);
+      hb_font_set_ppem (info->hb_font, factor, factor);
+
+#ifdef HAVE_HB_FONT_SET_VAR_NAMED_INSTANCE
+      /* Set the instance if this is a distortable font.  */
+      if (info->instance != -1)
+       hb_font_set_var_named_instance (info->hb_font,
+                                       info->instance);
+#endif /* HAVE_HB_FONT_SET_VAR_NAMED_INSTANCE */
+
+      /* This is needed for HarfBuzz before 2.0.0; it is the default
+        in later versions.  */
+      hb_ot_font_set_funcs (info->hb_font);
+    }
+
+ bail:
+  hb_face_destroy (face);
+
+  /* Calculate the scale factor.  */
+  *position_unit = 1.0 / 64.0;
+  return info->hb_font;
+}
+
+#endif /* HAVE_HARFBUZZ */
+
+
+
+void
+syms_of_sfntfont (void)
+{
+  DEFSYM (Qutf_16be, "utf-16be");
+  DEFSYM (Qmac_roman, "mac-roman");
+  DEFSYM (Qchinese_big5, "chinese-big5");
+  DEFSYM (Qunicode_bmp, "unicode-bmp");
+  DEFSYM (Qucs, "ucs");
+  DEFSYM (Qjapanese_jisx0208, "japanese-jisx0208");
+  DEFSYM (Qgbk, "gbk");
+  DEFSYM (Qkorean_ksc5601, "korean-ksc5601");
+  DEFSYM (Qapple_roman, "apple-roman");
+  DEFSYM (Qjisx0208_1983_0, "jisx0208.1983-0");
+  DEFSYM (Qksc5601_1987_0, "ksc5601.1987-0");
+  DEFSYM (Qzh, "zh");
+  DEFSYM (Qja, "ja");
+  DEFSYM (Qko, "ko");
+  DEFSYM (Qfont_instance, "font-instance");
+
+  /* Char-table purpose.  */
+  DEFSYM (Qfont_lookup_cache, "font-lookup-cache");
+
+  /* Set up staticpros.  */
+  sfnt_vendor_name = Qnil;
+  staticpro (&sfnt_vendor_name);
+
+  /* This variable is supposed to be set by the platform specific part
+     of the font backend.  */
+  DEFVAR_LISP ("sfnt-default-family-alist", Vsfnt_default_family_alist,
+    doc: /* Alist between "emulated" and actual font family names.
+
+Much Emacs code assumes that font families named "Monospace" and "Sans
+Serif" exist, and map to the default monospace and Sans Serif fonts on
+a system.  When the `sfnt' font driver is asked to look for a font
+with one of the families in this alist, it uses its value instead.  */);
+  Vsfnt_default_family_alist = Qnil;
+}
+
+void
+mark_sfntfont (void)
+{
+  struct sfnt_font_desc *desc;
+
+  /* Mark each font desc.  */
+  for (desc = system_fonts; desc; desc = desc->next)
+    {
+      mark_object (desc->family);
+      mark_object (desc->style);
+      mark_object (desc->adstyle);
+      mark_object (desc->instances);
+      mark_object (desc->languages);
+      mark_object (desc->registry);
+      mark_object (desc->char_cache);
+      mark_object (desc->designer);
+    }
+}
+
+void
+init_sfntfont (void)
+{
+
+}
+
+
+
+/* Initialize the sfntfont font driver.  VENDOR_TYPE is the type of
+   all font entities created.  DRIVER is the font driver that is saved
+   in font objects.  PUT_GLYPHS is a function that is called with 8
+   arguments, S, FROM, TO, X, Y, WITH_BACKGROUND, RASTERS, and
+   X_COORDS, and should draw all the rasters in RASTERS to S->f,
+   originating at X_COORDS[i], Y, along with filling the background if
+   WITH_BACKGROUND is specified.  */
+
+void
+init_sfntfont_vendor (Lisp_Object vendor_name,
+                     const struct font_driver *driver,
+                     sfntfont_put_glyph_proc put_glyphs)
+{
+  sfnt_vendor_name = vendor_name;
+  sfnt_font_driver = driver;
+  sfnt_put_glyphs = put_glyphs;
+}
diff --git a/src/sfntfont.h b/src/sfntfont.h
new file mode 100644
index 00000000000..df387512d0d
--- /dev/null
+++ b/src/sfntfont.h
@@ -0,0 +1,79 @@
+/* sfnt format font driver for GNU Emacs.
+
+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, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef _SFNTFONT_H_
+#define _SFNTFONT_H_
+
+#include "lisp.h"
+#include "frame.h"
+#include "font.h"
+#include "sfnt.h"
+
+extern int sfnt_enum_font (const char *);
+
+
+/* Font driver callbacks.  */
+
+extern Lisp_Object sfntfont_list (struct frame *, Lisp_Object);
+extern Lisp_Object sfntfont_match (struct frame *, Lisp_Object);
+extern Lisp_Object sfntfont_open (struct frame *, Lisp_Object, int);
+
+extern unsigned int sfntfont_encode_char (struct font *, int);
+extern void sfntfont_text_extents (struct font *, const unsigned int *,
+                                  int, struct font_metrics *);
+extern void sfntfont_close (struct font *);
+extern int sfntfont_draw (struct glyph_string *, int, int,
+                         int, int, bool);
+extern Lisp_Object sfntfont_list_family (struct frame *);
+extern int sfntfont_get_variation_glyphs (struct font *, int, unsigned[256]);
+
+
+/* Initialization functions.  */
+
+typedef void (*sfntfont_put_glyph_proc) (struct glyph_string *, int, int,
+                                        int, int, bool, struct sfnt_raster **,
+                                        int *);
+
+extern void syms_of_sfntfont (void);
+extern void init_sfntfont (void);
+extern void mark_sfntfont (void);
+extern void init_sfntfont_vendor (Lisp_Object, const struct font_driver *,
+                                 sfntfont_put_glyph_proc);
+
+
+/* mmap specific functions.  */
+
+#ifdef HAVE_MMAP
+
+extern bool sfntfont_detect_sigbus (void *);
+
+#endif /* HAVE_MMAP */
+
+
+
+/* HarfBuzz specific functions.  */
+
+#ifdef HAVE_HARFBUZZ
+
+extern hb_font_t *sfntfont_begin_hb_font (struct font *, double *);
+
+#endif /* HAVE_HARFBUZZ */
+
+#endif /* _SFNTFONT_H_ */
diff --git a/src/sound.c b/src/sound.c
index ac29fc1aa1e..9c023725da0 100644
--- a/src/sound.c
+++ b/src/sound.c
@@ -1386,7 +1386,7 @@ Internal use only, use `play-sound' instead.  */)
       /* Open the sound file.  */
       current_sound->fd =
        openp (list1 (Vdata_directory), attrs[SOUND_FILE], Qnil, &file, Qnil,
-              false, false);
+              false, false, NULL);
       if (current_sound->fd < 0)
        sound_perror ("Could not open sound file");
 
diff --git a/src/sysdep.c b/src/sysdep.c
index 443602a2d6d..a995bc66741 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -134,6 +134,14 @@ int _cdecl _spawnlp (int, const char *, const char *, ...);
 # include <sys/socket.h>
 #endif
 
+#ifdef HAVE_ANDROID
+#include "android.h"
+#endif
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#include "sfntfont.h"
+#endif
+
 /* Declare here, including term.h is problematic on some systems.  */
 extern void tputs (const char *, int, int (*)(int));
 
@@ -252,7 +260,7 @@ init_standard_fds (void)
   /* Set buferr if possible on platforms defining _PC_PIPE_BUF, as
      they support the notion of atomic writes to pipes.  */
   #ifdef _PC_PIPE_BUF
-    buferr = fdopen (STDERR_FILENO, "w");
+    buferr = emacs_fdopen (STDERR_FILENO, "w");
     if (buferr)
       setvbuf (buferr, NULL, _IOLBF, 0);
   #endif
@@ -790,6 +798,7 @@ init_sigio (int fd)
 #endif
 }
 
+#ifndef HAVE_ANDROID
 #ifndef DOS_NT
 #ifdef F_SETOWN
 static void
@@ -801,6 +810,7 @@ reset_sigio (int fd)
 }
 #endif /* F_SETOWN */
 #endif
+#endif
 
 void
 request_sigio (void)
@@ -972,6 +982,8 @@ narrow_foreground_group (int fd)
     tcsetpgrp_without_stopping (fd, getpid ());
 }
 
+#ifndef HAVE_ANDROID
+
 /* Set the tty to our original foreground group.  */
 static void
 widen_foreground_group (int fd)
@@ -979,6 +991,9 @@ widen_foreground_group (int fd)
   if (inherited_pgroup && setpgid (0, inherited_pgroup) == 0)
     tcsetpgrp_without_stopping (fd, inherited_pgroup);
 }
+
+#endif
+
 
 /* Getting and setting emacs_tty structures.  */
 
@@ -1496,6 +1511,8 @@ reset_sys_modes (struct tty_display_info *tty_out)
       fflush (stdout);
       return;
     }
+
+#ifndef HAVE_ANDROID
   if (!tty_out->term_initted)
     return;
 
@@ -1552,6 +1569,7 @@ reset_sys_modes (struct tty_display_info *tty_out)
 #endif
 
   widen_foreground_group (fileno (tty_out->input));
+#endif
 }
 
 #ifdef HAVE_PTYS
@@ -1802,7 +1820,45 @@ handle_arith_signal (int sig)
   xsignal0 (Qarith_error);
 }
 
-#if defined HAVE_STACK_OVERFLOW_HANDLING && !defined WINDOWSNT
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY && defined HAVE_MMAP
+
+static void
+handle_sigbus (int sig, siginfo_t *siginfo, void *arg)
+{
+  /* If this arrives during sfntfont_open, then Emacs may be
+     screwed.  */
+
+  if (sfntfont_detect_sigbus (siginfo->si_addr))
+    return;
+
+  handle_fatal_signal (sig);
+}
+
+/* Try to set up SIGBUS handling for the sfnt font driver.
+   Value is 1 upon failure, 0 otherwise.  */
+
+static int
+init_sigbus (void)
+{
+  struct sigaction sa;
+
+  sigfillset (&sa.sa_mask);
+  sa.sa_sigaction = handle_sigbus;
+  sa.sa_flags = SA_SIGINFO;
+
+  if (sigaction (SIGBUS, &sa, NULL))
+    return 1;
+
+  return 0;
+}
+
+#endif
+
+/* This does not work on Android and interferes with the system
+   tombstone generation.  */
+
+#if defined HAVE_STACK_OVERFLOW_HANDLING && !defined WINDOWSNT \
+  && (!defined HAVE_ANDROID || defined ANDROID_STUBIFY)
 
 /* Alternate stack used by SIGSEGV handler below.  */
 
@@ -1914,12 +1970,16 @@ init_sigsegv (void)
 
 #else /* not HAVE_STACK_OVERFLOW_HANDLING or WINDOWSNT */
 
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+
 static bool
 init_sigsegv (void)
 {
   return 0;
 }
 
+#endif
+
 #endif /* HAVE_STACK_OVERFLOW_HANDLING && !WINDOWSNT */
 
 static void
@@ -2027,12 +2087,17 @@ init_signals (void)
 #endif /* __vax__ */
     }
 
+  /* SIGUSR1 and SIGUSR2 are used internally by the android_select
+     function.  */
+#if !defined HAVE_ANDROID
 #ifdef SIGUSR1
   add_user_signal (SIGUSR1, "sigusr1");
 #endif
 #ifdef SIGUSR2
   add_user_signal (SIGUSR2, "sigusr2");
 #endif
+#endif
+
   sigaction (SIGABRT, &thread_fatal_action, 0);
 #ifdef SIGPRE
   sigaction (SIGPRE, &thread_fatal_action, 0);
@@ -2056,10 +2121,15 @@ init_signals (void)
   sigaction (SIGEMT, &thread_fatal_action, 0);
 #endif
 #ifdef SIGBUS
-  sigaction (SIGBUS, &thread_fatal_action, 0);
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY && defined HAVE_MMAP
+  if (init_sigbus ())
 #endif
+    sigaction (SIGBUS, &thread_fatal_action, 0);
+#endif
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
   if (!init_sigsegv ())
     sigaction (SIGSEGV, &thread_fatal_action, 0);
+#endif
 #ifdef SIGSYS
   sigaction (SIGSYS, &thread_fatal_action, 0);
 #endif
@@ -2313,7 +2383,8 @@ emacs_backtrace (int backtrace_limit)
     }
 }
 
-#ifndef HAVE_NTGUI
+#if !defined HAVE_NTGUI && !(defined HAVE_ANDROID              \
+                            && !defined ANDROID_STUBIFY)
 void
 emacs_abort (void)
 {
@@ -2335,11 +2406,20 @@ int
 emacs_fstatat (int dirfd, char const *filename, void *st, int flags)
 {
   int r;
-  while ((r = fstatat (dirfd, filename, st, flags)) != 0 && errno == EINTR)
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+  while ((r = fstatat (dirfd, filename, st, flags)) != 0
+        && errno == EINTR)
     maybe_quit ();
+#else
+  while ((r = android_fstatat (dirfd, filename, st, flags)) != 0
+        && errno == EINTR)
+    maybe_quit ();
+#endif
   return r;
 }
 
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+
 static int
 sys_openat (int dirfd, char const *file, int oflags, int mode)
 {
@@ -2354,6 +2434,28 @@ sys_openat (int dirfd, char const *file, int oflags, int 
mode)
 #endif
 }
 
+#endif
+
+int
+sys_fstat (int fd, struct stat *statb)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+  return fstat (fd, statb);
+#else
+  return android_fstat (fd, statb);
+#endif
+}
+
+int
+sys_faccessat (int fd, const char *pathname, int mode, int flags)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+  return faccessat (fd, pathname, mode, flags);
+#else
+  return android_faccessat (fd, pathname, mode, flags);
+#endif
+}
+
 /* Assuming the directory DIRFD, open FILE for Emacs use,
    using open flags OFLAGS and mode MODE.
    Use binary I/O on systems that care about text vs binary I/O.
@@ -2362,6 +2464,8 @@ sys_openat (int dirfd, char const *file, int oflags, int 
mode)
    Do not fail merely because the open was interrupted by a signal.
    Allow the user to quit.  */
 
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+
 int
 emacs_openat (int dirfd, char const *file, int oflags, int mode)
 {
@@ -2374,10 +2478,23 @@ emacs_openat (int dirfd, char const *file, int oflags, 
int mode)
   return fd;
 }
 
+#endif
+
 int
 emacs_open (char const *file, int oflags, int mode)
 {
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  int fd;
+#endif
+
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
   return emacs_openat (AT_FDCWD, file, oflags, mode);
+#else
+  while ((fd = android_open (file, oflags, mode)) < 0 && errno == EINTR)
+    maybe_quit ();
+
+  return fd;
+#endif
 }
 
 /* Same as above, but doesn't allow the user to quit.  */
@@ -2389,9 +2506,15 @@ emacs_open_noquit (char const *file, int oflags, int 
mode)
   if (! (oflags & O_TEXT))
     oflags |= O_BINARY;
   oflags |= O_CLOEXEC;
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
   do
     fd = open (file, oflags, mode);
   while (fd < 0 && errno == EINTR);
+#else
+  do
+    fd = android_open (file, oflags, mode);
+  while (fd < 0 && errno == EINTR);
+#endif
   return fd;
 }
 
@@ -2422,7 +2545,7 @@ emacs_fopen (char const *file, char const *mode)
       }
 
   fd = emacs_open (file, omode | oflags | bflag, 0666);
-  return fd < 0 ? 0 : fdopen (fd, mode);
+  return fd < 0 ? 0 : emacs_fdopen (fd, mode);
 }
 
 /* Create a pipe for Emacs use.  */
@@ -2441,6 +2564,8 @@ emacs_pipe (int fd[2])
    For the background behind this mess, please see Austin Group defect 529
    <https://austingroupbugs.net/view.php?id=529>.  */
 
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+
 #ifndef POSIX_CLOSE_RESTART
 # define POSIX_CLOSE_RESTART 1
 static int
@@ -2467,6 +2592,8 @@ posix_close (int fd, int flag)
 }
 #endif
 
+#endif
+
 /* Close FD, retrying if interrupted.  If successful, return 0;
    otherwise, return -1 and set errno to a non-EINTR value.  Consider
    an EINPROGRESS error to be successful, as that's merely a signal
@@ -2479,9 +2606,17 @@ posix_close (int fd, int flag)
 int
 emacs_close (int fd)
 {
+  int r;
+
   while (1)
     {
-      int r = posix_close (fd, POSIX_CLOSE_RESTART);
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+      r = posix_close (fd, POSIX_CLOSE_RESTART);
+#else
+      r = android_close (fd) == 0 || errno == EINTR ? 0 : -1;
+#define POSIX_CLOSE_RESTART 1
+#endif
+
       if (r == 0)
        return r;
       if (!POSIX_CLOSE_RESTART || errno != EINTR)
@@ -2492,6 +2627,108 @@ emacs_close (int fd)
     }
 }
 
+/* Wrapper around fdopen.  On Android, this calls `android_fclose' to
+   clear information associated with FD if necessary.  */
+
+FILE *
+emacs_fdopen (int fd, const char *mode)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+  return fdopen (fd, mode);
+#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
+  return android_fdopen (fd, mode);
+#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */
+}
+
+/* Wrapper around fclose.  On Android, this calls `android_fclose' to
+   clear information associated with the FILE's file descriptor if
+   necessary.  */
+
+int
+emacs_fclose (FILE *stream)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+  return fclose (stream);
+#else
+  return android_fclose (stream);
+#endif
+}
+
+/* Wrappers around unlink, symlink, rename, renameat_noreplace, and
+   rmdir.  These operations handle asset and content directories on
+   Android, and may return EINTR.  */
+
+int
+emacs_unlink (const char *name)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+  return unlink (name);
+#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
+  return android_unlink (name);
+#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */
+}
+
+int
+emacs_symlink (const char *target, const char *linkname)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+  return symlink (target, linkname);
+#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
+  return android_symlink (target, linkname);
+#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */
+}
+
+int
+emacs_rmdir (const char *dirname)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+  return rmdir (dirname);
+#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
+  return android_rmdir (dirname);
+#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */
+}
+
+int
+emacs_mkdir (const char *dirname, mode_t mode)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+  return mkdir (dirname, mode);
+#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
+  return android_mkdir (dirname, mode);
+#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */
+}
+
+int
+emacs_renameat_noreplace (int srcfd, const char *src,
+                         int dstfd, const char *dst)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+  return renameat_noreplace (srcfd, src, dstfd, dst);
+#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
+  return android_renameat_noreplace (srcfd, src, dstfd, dst);
+#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */
+}
+
+int
+emacs_rename (const char *src, const char *dst)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+  return rename (src, dst);
+#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
+  return android_rename (src, dst);
+#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */
+}
+
+int
+emacs_fchmodat (int fd, const char *path, mode_t mode, int flags)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+  return fchmodat (fd, path, mode, flags);
+#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
+  return android_fchmodat (fd, path, mode, flags);
+#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */
+}
+
 /* Maximum number of bytes to read or write in a single system call.
    This works around a serious bug in Linux kernels before 2.6.16; see
    <https://bugzilla.redhat.com/show_bug.cgi?format=multiple&id=612839>.
@@ -2736,6 +2973,15 @@ errwrite (void const *buf, ptrdiff_t nbuf)
 void
 close_output_streams (void)
 {
+  /* Android comes with some kind of ``file descriptor sanitizer''
+     that aborts when stdout or stderr is closed.  */
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  fflush (stderr);
+  fflush (stdout);
+  return;
+#endif
+
   if (close_stream (stdout) != 0)
     {
       emacs_perror ("Write error to standard output");
diff --git a/src/term.c b/src/term.c
index 4df3de8f4a5..9bcb2cb1386 100644
--- a/src/term.c
+++ b/src/term.c
@@ -62,6 +62,8 @@ static int been_here = -1;
 #include "w32term.h"
 #endif
 
+#ifndef HAVE_ANDROID
+
 static void tty_set_scroll_region (struct frame *f, int start, int stop);
 static void turn_on_face (struct frame *, int face_id);
 static void turn_off_face (struct frame *, int face_id);
@@ -73,11 +75,15 @@ static void clear_tty_hooks (struct terminal *terminal);
 static void set_tty_hooks (struct terminal *terminal);
 static void dissociate_if_controlling_tty (int fd);
 static void delete_tty (struct terminal *);
+
+#endif
+
 static AVOID maybe_fatal (bool, struct terminal *, const char *, const char *,
                          ...)
   ATTRIBUTE_FORMAT_PRINTF (3, 5) ATTRIBUTE_FORMAT_PRINTF (4, 5);
 static AVOID vfatal (const char *, va_list) ATTRIBUTE_FORMAT_PRINTF (1, 0);
 
+#ifndef HAVE_ANDROID
 
 #define OUTPUT(tty, a)                                          \
   emacs_tputs ((tty), a,                                        \
@@ -95,6 +101,8 @@ static AVOID vfatal (const char *, va_list) 
ATTRIBUTE_FORMAT_PRINTF (1, 0);
 
 #define OUTPUT1_IF(tty, a) do { if (a) emacs_tputs ((tty), a, 1, cmputc); } 
while (0)
 
+#endif
+
 /* Display space properties.  */
 
 /* Chain of all tty device parameters.  */
@@ -117,10 +125,14 @@ enum no_color_bit
 
 /* internal state */
 
+#ifndef HAVE_ANDROID
+
 /* The largest frame width in any call to calculate_costs.  */
 
 static int max_frame_cols;
 
+#endif
+
 
 
 #ifdef HAVE_GPM
@@ -133,6 +145,8 @@ struct tty_display_info *gpm_tty = NULL;
 static int last_mouse_x, last_mouse_y;
 #endif /* HAVE_GPM */
 
+#ifndef HAVE_ANDROID
+
 /* Ring the bell on a tty. */
 
 static void
@@ -718,7 +732,20 @@ encode_terminal_code (struct glyph *src, int src_len,
   return (encode_terminal_dst);
 }
 
+#else /* !HAVE_ANDROID */
 
+unsigned char *
+encode_terminal_code (struct glyph *src, int src_len,
+                     struct coding_system *coding)
+{
+  /* Text terminals are simply not supported on Android.  */
+  coding->produced = 0;
+  return NULL;
+}
+
+#endif /* HAVE_ANDROID */
+
+#ifndef HAVE_ANDROID
 
 /* An implementation of write_glyphs for termcap frames. */
 
@@ -1046,8 +1073,10 @@ int
 string_cost (const char *str)
 {
   cost = 0;
+#ifndef HAVE_ANDROID
   if (str)
     tputs (str, 0, evalcost);
+#endif
   return cost;
 }
 
@@ -1058,8 +1087,10 @@ static int
 string_cost_one_line (const char *str)
 {
   cost = 0;
+#ifndef HAVE_ANDROID
   if (str)
     tputs (str, 1, evalcost);
+#endif
   return cost;
 }
 
@@ -1070,11 +1101,13 @@ int
 per_line_cost (const char *str)
 {
   cost = 0;
+#ifndef HAVE_ANDROID
   if (str)
     tputs (str, 0, evalcost);
   cost = - cost;
   if (str)
     tputs (str, 10, evalcost);
+#endif
   return cost;
 }
 
@@ -1147,11 +1180,14 @@ calculate_ins_del_char_costs (struct frame *f)
     *p++ = (ins_startup_cost += ins_cost_per_char);
 }
 
+#endif
+
 void
 calculate_costs (struct frame *frame)
 {
   FRAME_COST_BAUD_RATE (frame) = baud_rate;
 
+#ifndef HAVE_ANDROID
   if (FRAME_TERMCAP_P (frame))
     {
       struct tty_display_info *tty = FRAME_TTY (frame);
@@ -1206,13 +1242,15 @@ calculate_costs (struct frame *frame)
 
       cmcostinit (FRAME_TTY (frame)); /* set up cursor motion costs */
     }
+#endif
 }
 
-struct fkey_table {
+struct fkey_table
+{
   const char *cap, *name;
 };
 
-#ifndef DOS_NT
+#if !defined DOS_NT && !defined HAVE_ANDROID
   /* Termcap capability names that correspond directly to X keysyms.
      Some of these (marked "terminfo") aren't supplied by old-style
      (Berkeley) termcap entries.  They're listed in X keysym order;
@@ -1443,6 +1481,9 @@ term_get_fkeys_1 (void)
 #endif /* not DOS_NT */
 
 
+
+#ifndef HAVE_ANDROID
+
 /***********************************************************************
                       Character Display Information
  ***********************************************************************/
@@ -1519,14 +1560,17 @@ append_glyph (struct it *it)
     }
 }
 
+#endif
+
 /* For external use.  */
 void
 tty_append_glyph (struct it *it)
 {
+#ifndef HAVE_ANDROID
   append_glyph (it);
+#endif
 }
 
-
 /* Produce glyphs for the display element described by IT.  *IT
    specifies what we want to produce a glyph for (character, image, ...),
    and where in the glyph matrix we currently are (glyph row and hpos).
@@ -1549,6 +1593,7 @@ tty_append_glyph (struct it *it)
 void
 produce_glyphs (struct it *it)
 {
+#ifndef HAVE_ANDROID
   /* If a hook is installed, let it do the work.  */
 
   /* Nothing but characters are supported on terminal frames.  */
@@ -1661,8 +1706,11 @@ produce_glyphs (struct it *it)
     it->current_x += it->pixel_width;
   it->ascent = it->max_ascent = it->phys_ascent = it->max_phys_ascent = 0;
   it->descent = it->max_descent = it->phys_descent = it->max_phys_descent = 1;
+#endif
 }
 
+#ifndef HAVE_ANDROID
+
 /* Append glyphs to IT's glyph_row for the composition IT->cmp_id.
    Called from produce_composite_glyph for terminal frames if
    IT->glyph_row != NULL.  IT->face_id contains the character's
@@ -2020,6 +2068,7 @@ turn_off_face (struct frame *f, int face_id)
     OUTPUT1_IF (tty, tty->TS_orig_pair);
 }
 
+#endif /* !HAVE_ANDROID */
 
 /* Return true if the terminal on frame F supports all of the
    capabilities in CAPS simultaneously.  */
@@ -2027,8 +2076,9 @@ turn_off_face (struct frame *f, int face_id)
 bool
 tty_capable_p (struct tty_display_info *tty, unsigned int caps)
 {
+#ifndef HAVE_ANDROID
 #define TTY_CAPABLE_P_TRY(tty, cap, TS, NC_bit)                                
\
-  if ((caps & (cap)) && (!(TS) || !MAY_USE_WITH_COLORS_P(tty, NC_bit)))        
\
+  if ((caps & (cap)) && (!(TS) || !MAY_USE_WITH_COLORS_P (tty, NC_bit)))       
\
     return 0;
 
   TTY_CAPABLE_P_TRY (tty,
@@ -2048,6 +2098,9 @@ tty_capable_p (struct tty_display_info *tty, unsigned int 
caps)
 
   /* We can do it!  */
   return 1;
+#else
+  return false;
+#endif
 }
 
 /* Return non-zero if the terminal is capable to display colors.  */
@@ -2081,7 +2134,7 @@ TERMINAL does not refer to a text terminal.  */)
   return make_fixnum (t ? t->display_info.tty->TN_max_colors : 0);
 }
 
-#ifndef DOS_NT
+#if !defined DOS_NT && !defined HAVE_ANDROID
 
 /* Declare here rather than in the function, as in the rest of Emacs,
    to work around an HPUX compiler bug (?). See
@@ -2186,7 +2239,7 @@ set_tty_color_mode (struct tty_display_info *tty, struct 
frame *f)
     }
 }
 
-#endif /* !DOS_NT */
+#endif /* !DOS_NT && !HAVE_ANDROID */
 
 char *
 tty_type_name (Lisp_Object terminal)
@@ -2278,6 +2331,7 @@ suspended.
 A suspended tty may be resumed by calling `resume-tty' on it.  */)
   (Lisp_Object tty)
 {
+#ifndef HAVE_ANDROID
   struct terminal *t = decode_tty_terminal (tty);
   FILE *f;
 
@@ -2300,8 +2354,8 @@ A suspended tty may be resumed by calling `resume-tty' on 
it.  */)
 
 #ifndef MSDOS
       if (f != t->display_info.tty->output)
-        fclose (t->display_info.tty->output);
-      fclose (f);
+        emacs_fclose (t->display_info.tty->output);
+      emacs_fclose (f);
 #endif
 
       t->display_info.tty->input = 0;
@@ -2314,6 +2368,10 @@ A suspended tty may be resumed by calling `resume-tty' 
on it.  */)
 
   /* Clear display hooks to prevent further output.  */
   clear_tty_hooks (t);
+#else
+  /* This will always signal on Android.  */
+  decode_tty_terminal (tty);
+#endif
 
   return Qnil;
 }
@@ -2337,9 +2395,12 @@ TTY may be a terminal object, a frame, or nil (meaning 
the selected
 frame's terminal). */)
   (Lisp_Object tty)
 {
-  struct terminal *t = decode_tty_terminal (tty);
+#ifndef HAVE_ANDROID
+  struct terminal *t;
   int fd;
 
+  t = decode_tty_terminal (tty);
+
   if (!t)
     error ("Attempt to resume a non-text terminal device");
 
@@ -2354,7 +2415,7 @@ frame's terminal). */)
 #else  /* !MSDOS */
       fd = emacs_open (t->display_info.tty->name, O_RDWR | O_NOCTTY, 0);
       t->display_info.tty->input = t->display_info.tty->output
-       = fd < 0 ? 0 : fdopen (fd, "w+");
+       = fd < 0 ? 0 : emacs_fdopen (fd, "w+");
 
       if (! t->display_info.tty->input)
        {
@@ -2396,10 +2457,15 @@ frame's terminal). */)
     }
 
   set_tty_hooks (t);
+#else
+  decode_tty_terminal (tty);
+#endif
 
   return Qnil;
 }
 
+#ifndef HAVE_ANDROID
+
 DEFUN ("tty--set-output-buffer-size", Ftty__set_output_buffer_size,
        Stty__set_output_buffer_size, 1, 2, 0, doc:
        /* Set the output buffer size for a TTY.
@@ -2438,12 +2504,14 @@ A value of zero means TTY uses the system's default 
value.  */)
   error ("Not a tty terminal");
 }
 
+#endif
+
 
 /***********************************************************************
                               Mouse
  ***********************************************************************/
 
-#ifndef DOS_NT
+#if !defined DOS_NT && !defined HAVE_ANDROID
 
 /* Implementation of draw_row_with_mouse_face for TTY/GPM and macOS.  */
 void
@@ -2713,7 +2781,7 @@ DEFUN ("gpm-mouse-stop", Fgpm_mouse_stop, Sgpm_mouse_stop,
                               Menus
  ***********************************************************************/
 
-#if !defined (MSDOS)
+#if !defined (MSDOS) && !defined HAVE_ANDROID
 
 /* TTY menu implementation and main ideas are borrowed from msdos.c.
 
@@ -3813,10 +3881,12 @@ tty_menu_show (struct frame *f, int x, int y, int 
menuflags,
   return SAFE_FREE_UNBIND_TO (specpdl_count, entry);
 }
 
-#endif /* !MSDOS */
+#endif /* !MSDOS && !defined HAVE_ANDROID */
 
 
-#ifndef MSDOS
+
+#if !defined MSDOS && !defined HAVE_ANDROID
+
 /***********************************************************************
                            Initialization
  ***********************************************************************/
@@ -3846,7 +3916,7 @@ tty_free_frame_resources (struct frame *f)
   xfree (f->output_data.tty);
 }
 
-#else  /* MSDOS */
+#elif defined MSDOS
 
 /* Delete frame F's face cache.  */
 
@@ -3856,8 +3926,13 @@ tty_free_frame_resources (struct frame *f)
   eassert (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f));
   free_frame_faces (f);
 }
-#endif /* MSDOS */
+
+#endif
+
 
+
+#ifndef HAVE_ANDROID
+
 /* Reset the hooks in TERMINAL.  */
 
 static void
@@ -3952,6 +4027,8 @@ dissociate_if_controlling_tty (int fd)
     }
 }
 
+#endif /* !HAVE_ANDROID */
+
 /* Create a termcap display on the tty device with the given name and
    type.
 
@@ -3961,11 +4038,23 @@ dissociate_if_controlling_tty (int fd)
 
    TERMINAL_TYPE is the termcap type of the device, e.g. "vt100".
 
-   If MUST_SUCCEED is true, then all errors are fatal.  */
+   If MUST_SUCCEED is true, then all errors are fatal.  This function
+   always signals on Android, where text terminals are prohibited by
+   system policy (and the required libraries are usually not
+   available.)  */
+
+#ifdef HAVE_ANDROID
+_Noreturn
+#endif
 
 struct terminal *
 init_tty (const char *name, const char *terminal_type, bool must_succeed)
 {
+#ifdef HAVE_ANDROID
+  maybe_fatal (must_succeed, 0, "Text terminals are not supported"
+              " under Android", "Text terminals are not supported"
+              " under Android");
+#else
   struct tty_display_info *tty = NULL;
   struct terminal *terminal = NULL;
 #ifndef DOS_NT
@@ -4039,7 +4128,7 @@ init_tty (const char *name, const char *terminal_type, 
bool must_succeed)
     tty->input = tty->output
       = ((fd < 0 || ! isatty (fd))
         ? NULL
-        : fdopen (fd, "w+"));
+        : emacs_fdopen (fd, "w+"));
 
     if (! tty->input)
       {
@@ -4455,6 +4544,7 @@ use the Bourne shell command 'TERM=...; export TERM' 
(C-shell:\n\
   init_sys_modes (tty);
 
   return terminal;
+#endif /* !HAVE_ANDROID */
 }
 
 
@@ -4479,8 +4569,13 @@ maybe_fatal (bool must_succeed, struct terminal 
*terminal,
 {
   va_list ap;
   va_start (ap, str2);
+
+#ifndef HAVE_ANDROID
   if (terminal)
     delete_tty (terminal);
+#else
+  eassert (terminal == NULL);
+#endif
 
   if (must_succeed)
     vfatal (str2, ap);
@@ -4498,6 +4593,8 @@ fatal (const char *str, ...)
 
 
 
+#ifndef HAVE_ANDROID
+
 /* Delete the given tty terminal, closing all frames on it.  */
 
 static void
@@ -4543,25 +4640,27 @@ delete_tty (struct terminal *terminal)
     {
       delete_keyboard_wait_descriptor (fileno (tty->input));
       if (tty->input != stdin)
-        fclose (tty->input);
+        emacs_fclose (tty->input);
     }
   if (tty->output && tty->output != stdout && tty->output != tty->input)
-    fclose (tty->output);
+    emacs_fclose (tty->output);
   if (tty->termscript)
-    fclose (tty->termscript);
+    emacs_fclose (tty->termscript);
 
   xfree (tty->old_tty);
   xfree (tty->Wcm);
   xfree (tty);
 }
 
+#endif
+
 void
 syms_of_term (void)
 {
   DEFVAR_BOOL ("system-uses-terminfo", system_uses_terminfo,
     doc: /* Non-nil means the system uses terminfo rather than termcap.
 This variable can be used by terminal emulator packages.  */);
-#ifdef TERMINFO
+#if defined TERMINFO || (defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
   system_uses_terminfo = 1;
 #else
   system_uses_terminfo = 0;
@@ -4602,21 +4701,25 @@ trigger redisplay.  */);
   defsubr (&Stty_top_frame);
   defsubr (&Ssuspend_tty);
   defsubr (&Sresume_tty);
+#ifndef HAVE_ANDROID
   defsubr (&Stty__set_output_buffer_size);
   defsubr (&Stty__output_buffer_size);
+#endif /* !HAVE_ANDROID */
 #ifdef HAVE_GPM
   defsubr (&Sgpm_mouse_start);
   defsubr (&Sgpm_mouse_stop);
 #endif /* HAVE_GPM */
 
-#ifndef DOS_NT
+#if !defined DOS_NT && !defined HAVE_ANDROID
   default_orig_pair = NULL;
   default_set_foreground = NULL;
   default_set_background = NULL;
-#endif /* !DOS_NT */
+#endif /* !DOS_NT && !HAVE_ANDROID */
 
+#ifndef HAVE_ANDROID
   encode_terminal_src = NULL;
   encode_terminal_dst = NULL;
+#endif
 
   DEFSYM (Qtty_mode_set_strings, "tty-mode-set-strings");
   DEFSYM (Qtty_mode_reset_strings, "tty-mode-reset-strings");
diff --git a/src/termhooks.h b/src/termhooks.h
index c8345c2715f..d978819aeca 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -63,7 +63,8 @@ enum output_method
   output_w32,
   output_ns,
   output_pgtk,
-  output_haiku
+  output_haiku,
+  output_android,
 };
 
 /* Input queue declarations and hooks.  */
@@ -520,12 +521,13 @@ struct terminal
   /* Device-type dependent data shared amongst all frames on this terminal.  */
   union display_info
   {
-    struct tty_display_info *tty;     /* termchar.h */
-    struct x_display_info *x;         /* xterm.h */
-    struct w32_display_info *w32;     /* w32term.h */
-    struct ns_display_info *ns;       /* nsterm.h */
-    struct pgtk_display_info *pgtk; /* pgtkterm.h */
-    struct haiku_display_info *haiku; /* haikuterm.h */
+    struct tty_display_info *tty;              /* termchar.h */
+    struct x_display_info *x;                  /* xterm.h */
+    struct w32_display_info *w32;              /* w32term.h */
+    struct ns_display_info *ns;                        /* nsterm.h */
+    struct pgtk_display_info *pgtk;            /* pgtkterm.h */
+    struct haiku_display_info *haiku;          /* haikuterm.h */
+    struct android_display_info *android;      /* androidterm.h */
   } display_info;
 
 
@@ -599,7 +601,8 @@ struct terminal
      BGCOLOR.  */
   void (*query_frame_background_color) (struct frame *f, Emacs_Color *bgcolor);
 
-#if defined (HAVE_X_WINDOWS) || defined (HAVE_NTGUI) || defined (HAVE_PGTK)
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_NTGUI) || defined (HAVE_PGTK) \
+  || defined (HAVE_ANDROID)
   /* On frame F, translate pixel colors to RGB values for the NCOLORS
      colors in COLORS.  Use cached information, if available.  */
 
@@ -934,6 +937,9 @@ extern struct terminal *terminal_list;
 #elif defined (HAVE_HAIKU)
 #define TERMINAL_FONT_CACHE(t)                                         \
   (t->type == output_haiku ? t->display_info.haiku->name_list_element : Qnil)
+#elif defined (HAVE_ANDROID)
+#define TERMINAL_FONT_CACHE(t)                                         \
+  (t->type == output_android ? t->display_info.android->name_list_element : 
Qnil)
 #endif
 
 extern struct terminal *decode_live_terminal (Lisp_Object);
diff --git a/src/terminal.c b/src/terminal.c
index d13e3466512..07c37883f0e 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -451,6 +451,8 @@ return values.  */)
       return Qpgtk;
     case output_haiku:
       return Qhaiku;
+    case output_android:
+      return Qandroid;
     default:
       emacs_abort ();
     }
diff --git a/src/textconv.c b/src/textconv.c
index 7ed8ede3544..60f3ba80577 100644
--- a/src/textconv.c
+++ b/src/textconv.c
@@ -25,25 +25,57 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
    ability to ``undo'' or ``edit'' previously composed text.  This is
    most commonly seen in input methods for CJK laguages for X Windows,
    and is extensively used throughout Android by input methods for all
-   kinds of scripts.  */
+   kinds of scripts.
+
+   In addition, these input methods may also need to make detailed
+   edits to the content of a buffer.  That is also handled here.  */
 
 #include <config.h>
 
 #include "textconv.h"
 #include "buffer.h"
 #include "syntax.h"
+#include "blockinput.h"
+#include "keyboard.h"
 
 
 
-/* The window system's text conversion interface.
-   NULL when the window system has not set up text conversion.
+/* Define debugging macros.  */
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#if 0
+#include <android/log.h>
+
+#define TEXTCONV_DEBUG(fmt, ...)                                       \
+  __android_log_print (ANDROID_LOG_VERBOSE, "EmacsInputConnection",    \
+                      "%s: " fmt, __func__, ## __VA_ARGS__)
+#endif /* 0 */
+#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
+#ifndef TEXTCONV_DEBUG
+#define TEXTCONV_DEBUG(...) ((void) 0)
+#endif /* TEXTCONV_DEBUG */
 
-   This interface will later be heavily extended on the
-   feature/android branch to deal with Android's much less
-   straightforward text conversion protocols.  */
+
+
+/* The window system's text conversion interface.  NULL when the
+   window system has not set up text conversion.  */
 
 static struct textconv_interface *text_interface;
 
+/* How many times text conversion has been disabled.  */
+
+static int suppress_conversion_count;
+
+/* Flags used to determine what must be sent after a batch edit
+   ends.  */
+
+enum textconv_batch_edit_flags
+  {
+    PENDING_POINT_CHANGE   = 1,
+    PENDING_COMPOSE_CHANGE = 2,
+  };
+
 
 
 /* Copy the portion of the current buffer described by BEG, BEG_BYTE,
@@ -77,13 +109,46 @@ copy_buffer (ptrdiff_t beg, ptrdiff_t beg_byte,
   size = end0 - beg0;
   memcpy (buffer, BYTE_POS_ADDR (beg0), size);
   if (beg1 != -1)
-    memcpy (buffer, BEG_ADDR + beg1, end1 - beg1);
+    memcpy (buffer + size, BEG_ADDR + beg1, end1 - beg1);
 }
 
 
 
 /* Conversion query.  */
 
+/* Return the position of the active mark, or -1 if there is no mark
+   or it is not active.  */
+
+static ptrdiff_t
+get_mark (void)
+{
+  if (!NILP (BVAR (current_buffer, mark_active))
+      && XMARKER (BVAR (current_buffer, mark))->buffer)
+    return marker_position (BVAR (current_buffer,
+                                 mark));
+
+  return -1;
+}
+
+/* Like Fselect_window.  However, if WINDOW is a mini buffer window
+   but not the active minibuffer window, select its frame's selected
+   window instead.  */
+
+static void
+select_window (Lisp_Object window, Lisp_Object norecord)
+{
+  struct window *w;
+
+  w = XWINDOW (window);
+
+  if (MINI_WINDOW_P (w)
+      && WINDOW_LIVE_P (window)
+      && !EQ (window, Factive_minibuffer_window ()))
+    window = WINDOW_XFRAME (w)->selected_window;
+
+  Fselect_window (window, norecord);
+}
+
 /* Perform the text conversion operation specified in QUERY and return
    the results.
 
@@ -91,19 +156,28 @@ copy_buffer (ptrdiff_t beg, ptrdiff_t beg_byte,
    window and QUERY->factor times QUERY->direction from that
    position.  Return it in QUERY->text.
 
+   If QUERY->position is TYPE_MINIMUM (EMACS_INT) or EMACS_INT_MAX,
+   start at the window's last point or mark, whichever is greater or
+   smaller.
+
    Then, either delete that text from the buffer if QUERY->operation
    is TEXTCONV_SUBSTITUTION, or return 0.
 
+   If FLAGS & TEXTCONV_SKIP_CONVERSION_REGION, then first move PT past
+   the conversion region in the specified direction if it is inside.
+
    Value is 0 if QUERY->operation was not TEXTCONV_SUBSTITUTION
    or if deleting the text was successful, and 1 otherwise.  */
 
 int
-textconv_query (struct frame *f, struct textconv_callback_struct *query)
+textconv_query (struct frame *f, struct textconv_callback_struct *query,
+               int flags)
 {
   specpdl_ref count;
-  ptrdiff_t pos, pos_byte, end, end_byte;
-  ptrdiff_t temp, temp1;
+  ptrdiff_t pos, pos_byte, end, end_byte, start;
+  ptrdiff_t temp, temp1, mark;
   char *buffer;
+  struct window *w;
 
   /* Save the excursion, as there will be extensive changes to the
      selected window.  */
@@ -113,14 +187,69 @@ textconv_query (struct frame *f, struct 
textconv_callback_struct *query)
   /* Inhibit quitting.  */
   specbind (Qinhibit_quit, Qt);
 
-  /* Temporarily switch to F's selected window.  */
-  Fselect_window (f->selected_window, Qt);
+  /* Temporarily switch to F's selected window at the time of the last
+     redisplay.  */
+  select_window ((WINDOW_LIVE_P (f->old_selected_window)
+                 ? f->old_selected_window
+                 : f->selected_window), Qt);
+  w = XWINDOW (selected_window);
 
   /* Now find the appropriate text bounds for QUERY.  First, move
      point QUERY->position steps forward or backwards.  */
 
   pos = PT;
 
+  /* If QUERY->position is EMACS_INT_MAX, use the last mark or the
+     ephemeral last point, whichever is greater.
+
+     The opposite applies for EMACS_INT_MIN.  */
+
+  mark = get_mark ();
+
+  if (query->position == EMACS_INT_MAX)
+    {
+      pos = (mark == -1
+            ? w->ephemeral_last_point
+            : max (w->ephemeral_last_point, mark));
+      goto escape1;
+    }
+  else if (query->position == TYPE_MINIMUM (EMACS_INT))
+    {
+      pos = (mark == -1
+            ? w->ephemeral_last_point
+            : min (w->ephemeral_last_point, mark));
+      goto escape1;
+    }
+
+  /* Next, if POS lies within the conversion region and the caller
+     asked for it to be moved away, move it away from the conversion
+     region.  */
+
+  if (flags & TEXTCONV_SKIP_CONVERSION_REGION
+      && MARKERP (f->conversion.compose_region_start))
+    {
+      start = marker_position (f->conversion.compose_region_start);
+      end = marker_position (f->conversion.compose_region_end);
+
+      if (pos >= start && pos < end)
+       {
+         switch (query->direction)
+           {
+           case TEXTCONV_FORWARD_CHAR:
+           case TEXTCONV_FORWARD_WORD:
+           case TEXTCONV_CARET_DOWN:
+           case TEXTCONV_NEXT_LINE:
+           case TEXTCONV_LINE_START:
+             pos = end;
+             break;
+
+           default:
+             pos = max (BEGV, start - 1);
+             break;
+           }
+       }
+    }
+
   /* If pos is outside the accessible part of the buffer or if it
      overflows, move back to point or to the extremes of the
      accessible region.  */
@@ -128,6 +257,8 @@ textconv_query (struct frame *f, struct 
textconv_callback_struct *query)
   if (ckd_add (&pos, pos, query->position))
     pos = PT;
 
+ escape1:
+
   if (pos < BEGV)
     pos = BEGV;
 
@@ -287,27 +418,1795 @@ textconv_query (struct frame *f, struct 
textconv_callback_struct *query)
   return 0;
 }
 
-
+/* Update the overlay displaying the conversion area on F after a
+   change to the conversion region.  */
 
-/* Window system interface.  These are called from the rest of
-   Emacs.  */
+static void
+sync_overlay (struct frame *f)
+{
+  if (MARKERP (f->conversion.compose_region_start)
+      && !NILP (Vtext_conversion_face))
+    {
+      if (NILP (f->conversion.compose_region_overlay))
+       {
+         f->conversion.compose_region_overlay
+           = Fmake_overlay (f->conversion.compose_region_start,
+                            f->conversion.compose_region_end, Qnil,
+                            Qt, Qnil);
+         Foverlay_put (f->conversion.compose_region_overlay,
+                       Qface, Vtext_conversion_face);
+       }
 
-/* Notice that F's selected window has been set from redisplay.
-   Reset F's input method state.  */
+      Fmove_overlay (f->conversion.compose_region_overlay,
+                    f->conversion.compose_region_start,
+                    f->conversion.compose_region_end, Qnil);
+    }
+  else if (!NILP (f->conversion.compose_region_overlay))
+    {
+      Fdelete_overlay (f->conversion.compose_region_overlay);
+      f->conversion.compose_region_overlay = Qnil;
+    }
+}
+
+/* Record a change to the current buffer as a result of an
+   asynchronous text conversion operation on F.
+
+   Consult the doc string of `text-conversion-edits' for the meaning
+   of BEG, END, and EPHEMERAL.  */
+
+static void
+record_buffer_change (ptrdiff_t beg, ptrdiff_t end,
+                     Lisp_Object ephemeral)
+{
+  Lisp_Object buffer, beg_marker, end_marker;
+
+  XSETBUFFER (buffer, current_buffer);
+
+  /* Make markers for both BEG and END.  */
+  beg_marker = build_marker (current_buffer, beg,
+                            CHAR_TO_BYTE (beg));
+
+  /* If BEG and END are identical, make sure to keep the markers
+     eq.  */
+
+  if (beg == end)
+    end_marker = beg_marker;
+  else
+    {
+      end_marker = build_marker (current_buffer, end,
+                                CHAR_TO_BYTE (end));
+
+      /* Otherwise, make sure the marker extends past inserted
+        text.  */
+      Fset_marker_insertion_type (end_marker, Qt);
+    }
+
+  Vtext_conversion_edits
+    = Fcons (list4 (buffer, beg_marker, end_marker,
+                   ephemeral),
+            Vtext_conversion_edits);
+}
+
+/* Reset F's text conversion state.  Delete any overlays or
+   markers inside.  */
 
 void
-report_selected_window_change (struct frame *f)
+reset_frame_state (struct frame *f)
 {
-  if (!text_interface)
+  struct text_conversion_action *last, *next;
+
+  /* Make the composition region markers point elsewhere.  */
+
+  if (!NILP (f->conversion.compose_region_start))
+    {
+      Fset_marker (f->conversion.compose_region_start, Qnil, Qnil);
+      Fset_marker (f->conversion.compose_region_end, Qnil, Qnil);
+      f->conversion.compose_region_start = Qnil;
+      f->conversion.compose_region_end = Qnil;
+    }
+
+  /* Delete the composition region overlay.  */
+
+  if (!NILP (f->conversion.compose_region_overlay))
+    Fdelete_overlay (f->conversion.compose_region_overlay);
+
+  /* Delete each text conversion action queued up.  */
+
+  next = f->conversion.actions;
+  while (next)
+    {
+      last = next;
+      next = next->next;
+
+      /* Say that the conversion is finished.  */
+      if (text_interface && text_interface->notify_conversion)
+       text_interface->notify_conversion (last->counter);
+
+      xfree (last);
+    }
+  f->conversion.actions = NULL;
+
+  /* Clear batch edit state.  */
+  f->conversion.batch_edit_count = 0;
+  f->conversion.batch_edit_flags = 0;
+}
+
+/* Return whether or not there are pending edits from an input method
+   on any frame.  */
+
+bool
+detect_conversion_events (void)
+{
+  Lisp_Object tail, frame;
+
+  FOR_EACH_FRAME (tail, frame)
+    {
+      /* See if there's a pending edit on this frame.  */
+      if (XFRAME (frame)->conversion.actions
+         && ((XFRAME (frame)->conversion.actions->operation
+              != TEXTCONV_BARRIER)
+             || (kbd_fetch_ptr == kbd_store_ptr)))
+       return true;
+    }
+
+  return false;
+}
+
+/* Restore the selected window WINDOW.  */
+
+static void
+restore_selected_window (Lisp_Object window)
+{
+  /* FIXME: not sure what to do if WINDOW has been deleted.  */
+  select_window (window, Qt);
+}
+
+/* Commit the given text in the composing region.  If there is no
+   composing region, then insert the text after F's selected window's
+   last point instead, unless the mark is active.  Finally, remove the
+   composing region.
+
+   If the mark is active, delete the text between mark and point.
+
+   Then, move point to POSITION relative to TEXT.  If POSITION is
+   greater than zero, it is relative to the character at the end of
+   TEXT; otherwise, it is relative to the start of TEXT.  */
+
+static void
+really_commit_text (struct frame *f, EMACS_INT position,
+                   Lisp_Object text)
+{
+  specpdl_ref count;
+  ptrdiff_t wanted, start, end, mark;
+  struct window *w;
+
+  /* If F's old selected window is no longer live, fail.  */
+
+  if (!WINDOW_LIVE_P (f->old_selected_window))
     return;
 
-  text_interface->reset (f);
+  count = SPECPDL_INDEX ();
+  record_unwind_protect (restore_selected_window,
+                        selected_window);
+
+  /* Temporarily switch to F's selected window at the time of the last
+     redisplay.  */
+  select_window (f->old_selected_window, Qt);
+
+  /* Now detect whether or not there is a composing or active region.
+     If there is, then replace it with TEXT.  Don't do that
+     otherwise.  */
+
+  mark = get_mark ();
+  if (MARKERP (f->conversion.compose_region_start) || mark != -1)
+    {
+      /* Replace its contents.  Set START and END to the start and end
+        of the composing region if it exists.  */
+
+      if (MARKERP (f->conversion.compose_region_start))
+       {
+         start = marker_position (f->conversion.compose_region_start);
+         end = marker_position (f->conversion.compose_region_end);
+       }
+      else
+       {
+         /* Otherwise, set it to the start and end of the region.  */
+         start = min (mark, PT);
+         end = max (mark, PT);
+       }
+
+      /* Now delete whatever needs to go.  */
+
+      del_range_1 (start, end, true, false);
+      record_buffer_change (start, start, Qt);
+
+      /* Don't record changes if TEXT is empty.  */
+
+      if (SCHARS (text))
+       {
+         /* Insert the new text.  Make sure to inherit text
+            properties from the surroundings: if this doesn't happen,
+            CC Mode fontification can get thrown off and become very
+            slow.  */
+
+         insert_from_string (text, 0, 0, SCHARS (text),
+                             SBYTES (text), true);
+         record_buffer_change (start, PT, text);
+       }
+
+      /* Move to a the position specified in POSITION.  */
+
+      if (position <= 0)
+       {
+         /* If POSITION is less than zero, it is relative to the
+            start of the text that was inserted.  */
+         wanted = start;
+
+         if (INT_ADD_WRAPV (wanted, position, &wanted)
+             || wanted < BEGV)
+           wanted = BEGV;
+
+         if (wanted > ZV)
+           wanted = ZV;
+
+         set_point (wanted);
+       }
+      else
+       {
+         /* Otherwise, it is relative to the last character in
+            TEXT.  */
+         wanted = PT;
+
+         if (INT_ADD_WRAPV (wanted, position - 1, &wanted)
+             || wanted > ZV)
+           wanted = ZV;
+
+         if (wanted < BEGV)
+           wanted = BEGV;
+
+         set_point (wanted);
+       }
+
+      /* Make the composition region markers point elsewhere.  */
+
+      if (!NILP (f->conversion.compose_region_start))
+       {
+         Fset_marker (f->conversion.compose_region_start, Qnil, Qnil);
+         Fset_marker (f->conversion.compose_region_end, Qnil, Qnil);
+         f->conversion.compose_region_start = Qnil;
+         f->conversion.compose_region_end = Qnil;
+       }
+
+      /* Delete the composition region overlay.  */
+
+      if (!NILP (f->conversion.compose_region_overlay))
+       Fdelete_overlay (f->conversion.compose_region_overlay);
+    }
+  else
+    {
+      /* Otherwise, move the text and point to an appropriate
+        location.  */
+      wanted = PT;
+
+      /* Don't record changes if TEXT is empty.  */
+
+      if (SCHARS (text))
+       {
+         /* Insert the new text.  Make sure to inherit text
+            properties from the surroundings: if this doesn't happen,
+            CC Mode fontification can get thrown off and become very
+            slow.  */
+
+         insert_from_string (text, 0, 0, SCHARS (text),
+                             SBYTES (text), true);
+
+         record_buffer_change (wanted, PT, text);
+       }
+
+      if (position <= 0)
+       {
+         if (INT_ADD_WRAPV (wanted, position, &wanted)
+             || wanted < BEGV)
+           wanted = BEGV;
+
+         if (wanted > ZV)
+           wanted = ZV;
+
+         set_point (wanted);
+       }
+      else
+       {
+         wanted = PT;
+
+         if (INT_ADD_WRAPV (wanted, position - 1, &wanted)
+             || wanted > ZV)
+           wanted = ZV;
+
+         if (wanted < BEGV)
+           wanted = BEGV;
+
+         set_point (wanted);
+       }
+    }
+
+  /* This should deactivate the mark.  */
+  call0 (Qdeactivate_mark);
+
+  /* Print some debugging information.  */
+  TEXTCONV_DEBUG ("text inserted: %s, point now: %zd",
+                 SSDATA (text), PT);
+
+  /* Update the ephemeral last point.  */
+  w = XWINDOW (selected_window);
+  w->ephemeral_last_point = PT;
+  unbind_to (count, Qnil);
 }
 
-/* Register INTERFACE as the text conversion interface.  */
+/* Remove the composition region on the frame F, while leaving its
+   contents intact.  If UPDATE, also notify the input method of the
+   change.  */
 
-void
-register_texconv_interface (struct textconv_interface *interface)
+static void
+really_finish_composing_text (struct frame *f, bool update)
 {
-  text_interface = interface;
+  if (!NILP (f->conversion.compose_region_start))
+    {
+      Fset_marker (f->conversion.compose_region_start, Qnil, Qnil);
+      Fset_marker (f->conversion.compose_region_end, Qnil, Qnil);
+      f->conversion.compose_region_start = Qnil;
+      f->conversion.compose_region_end = Qnil;
+
+      if (update && text_interface
+         && text_interface->compose_region_changed)
+       (*text_interface->compose_region_changed) (f);
+    }
+
+  /* Delete the composition region overlay.  */
+
+  if (!NILP (f->conversion.compose_region_overlay))
+    Fdelete_overlay (f->conversion.compose_region_overlay);
+
+  TEXTCONV_DEBUG ("conversion region removed");
+}
+
+/* Set the composing text on F to TEXT.  Then, move point to an
+   appropriate position relative to POSITION, and call
+   `compose_region_changed' in the text conversion interface should
+   point not have been changed relative to F's old selected window's
+   last point.  */
+
+static void
+really_set_composing_text (struct frame *f, ptrdiff_t position,
+                          Lisp_Object text)
+{
+  specpdl_ref count;
+  ptrdiff_t start, wanted, end;
+  struct window *w;
+
+  /* If F's old selected window is no longer live, fail.  */
+
+  if (!WINDOW_LIVE_P (f->old_selected_window))
+    return;
+
+  count = SPECPDL_INDEX ();
+  record_unwind_protect (restore_selected_window,
+                        selected_window);
+
+  /* Temporarily switch to F's selected window at the time of the last
+     redisplay.  */
+  w = XWINDOW (f->old_selected_window);
+  select_window (f->old_selected_window, Qt);
+
+  /* Now set up the composition region if necessary.  */
+
+  if (!MARKERP (f->conversion.compose_region_start))
+    {
+      /* Set START and END.  */
+      start = PT;
+      wanted = end = get_mark ();
+
+      /* If END is -1, set it to start.  */
+
+      if (end == -1)
+       end = start;
+      else
+       {
+         /* Now sort start and end.  */
+         start = min (start, end);
+         end  = max (PT, wanted);
+       }
+
+      /* If END is not the same as start, delete the text in
+        between.  */
+
+      if (end != start)
+       {
+         del_range_1 (start, end, true, false);
+         set_point (start);
+         record_buffer_change (start, start, Qt);
+       }
+
+      /* Now set the markers which denote the composition region.  */
+      f->conversion.compose_region_start
+       = build_marker (current_buffer, PT, PT_BYTE);
+      f->conversion.compose_region_end
+       = build_marker (current_buffer, PT, PT_BYTE);
+
+      Fset_marker_insertion_type (f->conversion.compose_region_end,
+                                 Qt);
+    }
+  else
+    {
+      /* Delete the text between the start of the composing region and
+        its end.  */
+      start = marker_position (f->conversion.compose_region_start);
+      end = marker_position (f->conversion.compose_region_end);
+      del_range_1 (start, end, true, false);
+      set_point (start);
+
+      if (start != end)
+       record_buffer_change (start, start, Qt);
+    }
+
+  /* Insert the new text.  Make sure to inherit text properties from
+     the surroundings: if this doesn't happen, CC Mode fontification
+     can get thrown off and become very slow.  */
+
+  insert_from_string (text, 0, 0, SCHARS (text),
+                     SBYTES (text), true);
+
+  if (start != PT)
+    record_buffer_change (start, PT, Qt);
+
+  /* Now move point to an appropriate location.  */
+  if (position <= 0)
+    {
+      wanted = start;
+
+      if (INT_SUBTRACT_WRAPV (wanted, position, &wanted)
+         || wanted < BEGV)
+       wanted = BEGV;
+
+      if (wanted > ZV)
+       wanted = ZV;
+    }
+  else
+    {
+      end = marker_position (f->conversion.compose_region_end);
+      wanted = end;
+
+      /* end should be PT after the edit.  */
+      eassert (end == PT);
+
+      if (INT_ADD_WRAPV (wanted, position - 1, &wanted)
+         || wanted > ZV)
+       wanted = ZV;
+
+      if (wanted < BEGV)
+       wanted = BEGV;
+    }
+
+  set_point (wanted);
+
+  /* This should deactivate the mark.  */
+  call0 (Qdeactivate_mark);
+
+  /* Move the composition overlay.  */
+  sync_overlay (f);
+
+  /* If TEXT is empty, remove the composing region.  This goes against
+     the documentation, but is ultimately what programs expect.  */
+
+  if (!SCHARS (text))
+    really_finish_composing_text (f, false);
+
+  /* If PT hasn't changed, the conversion region definitely has.
+     Otherwise, redisplay will update the input method instead.  */
+
+  if (PT == w->ephemeral_last_point
+      && text_interface
+      && text_interface->compose_region_changed)
+    {
+      if (f->conversion.batch_edit_count > 0)
+       f->conversion.batch_edit_flags |= PENDING_COMPOSE_CHANGE;
+      else
+       text_interface->compose_region_changed (f);
+    }
+
+  /* Update the ephemeral last point.  */
+  w = XWINDOW (selected_window);
+  w->ephemeral_last_point = PT;
+
+  if (SCHARS (text))
+    TEXTCONV_DEBUG ("conversion region set to: %td %td",
+                   marker_position (f->conversion.compose_region_start),
+                   marker_position (f->conversion.compose_region_end));
+  else
+    TEXTCONV_DEBUG ("conversion region removed; PT is now: %td", PT);
+
+  unbind_to (count, Qnil);
+}
+
+/* Set the composing region to START by END.  Make it that it is not
+   already set.  */
+
+static void
+really_set_composing_region (struct frame *f, ptrdiff_t start,
+                            ptrdiff_t end)
+{
+  specpdl_ref count;
+  struct window *w;
+
+  /* If F's old selected window is no longer live, fail.  */
+
+  if (!WINDOW_LIVE_P (f->old_selected_window))
+    return;
+
+  /* If MAX (0, start) == end, then this should behave the same as
+     really_finish_composing_text.  */
+
+  if (max (0, start) == max (0, end))
+    {
+      really_finish_composing_text (f, false);
+      return;
+    }
+
+  count = SPECPDL_INDEX ();
+  record_unwind_protect (restore_selected_window,
+                        selected_window);
+
+  /* Temporarily switch to F's selected window at the time of the last
+     redisplay.  */
+  select_window (f->old_selected_window, Qt);
+
+  /* Now set up the composition region if necessary.  */
+
+  if (!MARKERP (f->conversion.compose_region_start))
+    {
+      f->conversion.compose_region_start = Fmake_marker ();
+      f->conversion.compose_region_end = Fmake_marker ();
+      Fset_marker_insertion_type (f->conversion.compose_region_end,
+                                 Qt);
+    }
+
+  Fset_marker (f->conversion.compose_region_start,
+              make_fixnum (start), Qnil);
+  Fset_marker (f->conversion.compose_region_end,
+              make_fixnum (end), Qnil);
+  sync_overlay (f);
+
+  TEXTCONV_DEBUG ("composing region set to: %td, %td; point is: %td",
+                 start, end, PT);
+
+  /* Update the ephemeral last point.  */
+  w = XWINDOW (selected_window);
+  w->ephemeral_last_point = PT;
+
+  unbind_to (count, Qnil);
+}
+
+/* Delete LEFT and RIGHT chars around point or the active mark,
+   whichever is larger, avoiding the composing region if
+   necessary.  */
+
+static void
+really_delete_surrounding_text (struct frame *f, ptrdiff_t left,
+                               ptrdiff_t right)
+{
+  specpdl_ref count;
+  ptrdiff_t start, end, a, b, a1, b1, lstart, rstart;
+  struct window *w;
+  Lisp_Object text;
+
+  /* If F's old selected window is no longer live, fail.  */
+
+  if (!WINDOW_LIVE_P (f->old_selected_window))
+    return;
+
+  count = SPECPDL_INDEX ();
+  record_unwind_protect (restore_selected_window,
+                        selected_window);
+
+  /* Temporarily switch to F's selected window at the time of the last
+     redisplay.  */
+  select_window (f->old_selected_window, Qt);
+
+  /* Figure out where to start deleting from.  */
+
+  a = get_mark ();
+
+  if (a != -1 && a != PT)
+    lstart = rstart = max (a, PT);
+  else
+    lstart = rstart = PT;
+
+  /* Avoid the composing text.  This behavior is identical to how
+     Android's BaseInputConnection actually implements avoiding the
+     composing span.  */
+
+  if (MARKERP (f->conversion.compose_region_start))
+    {
+      a = marker_position (f->conversion.compose_region_start);
+      b = marker_position (f->conversion.compose_region_end);
+
+      a1 = min (a, b);
+      b1 = max (a, b);
+
+      lstart = min (lstart, min (PT, a1));
+      rstart = max (rstart, max (PT, b1));
+    }
+
+  if (lstart == rstart)
+    {
+      start = max (BEGV, lstart - left);
+      end = min (ZV, rstart + right);
+
+      text = del_range_1 (start, end, true, true);
+      record_buffer_change (start, start, text);
+    }
+  else
+    {
+      /* Don't record a deletion if the text which was deleted lies
+        after point.  */
+
+      start = rstart;
+      end = min (ZV, rstart + right);
+      text = del_range_1 (start, end, true, true);
+      record_buffer_change (start, start, Qnil);
+
+      /* Now delete what must be deleted on the left.  */
+
+      start = max (BEGV, lstart - left);
+      end = lstart;
+      text = del_range_1 (start, end, true, true);
+      record_buffer_change (start, start, text);
+    }
+
+  TEXTCONV_DEBUG ("deleted surrounding text: %td, %td; PT is now %td",
+                 left, right, PT);
+
+  /* if the mark is now equal to start, deactivate it.  */
+
+  if (get_mark () == PT)
+    call0 (Qdeactivate_mark);
+
+  /* Update the ephemeral last point.  */
+  w = XWINDOW (selected_window);
+  w->ephemeral_last_point = PT;
+
+  unbind_to (count, Qnil);
+}
+
+/* Update the interface with F's new point and mark.  If a batch edit
+   is in progress, schedule the update for when it finishes
+   instead.  */
+
+static void
+really_request_point_update (struct frame *f)
+{
+  /* If F's old selected window is no longer live, fail.  */
+
+  if (!WINDOW_LIVE_P (f->old_selected_window))
+    return;
+
+  if (f->conversion.batch_edit_count > 0)
+    f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
+  else if (text_interface && text_interface->point_changed)
+    text_interface->point_changed (f,
+                                  XWINDOW (f->old_selected_window),
+                                  current_buffer);
+}
+
+/* Set point in F to POSITION.  If MARK is not POSITION, activate the
+   mark and set MARK to that as well.
+
+   If it has not changed, signal an update through the text input
+   interface, which is necessary for the IME to acknowledge that the
+   change has completed.  */
+
+static void
+really_set_point_and_mark (struct frame *f, ptrdiff_t point,
+                          ptrdiff_t mark)
+{
+  specpdl_ref count;
+  struct window *w;
+
+  /* If F's old selected window is no longer live, fail.  */
+
+  if (!WINDOW_LIVE_P (f->old_selected_window))
+    return;
+
+  count = SPECPDL_INDEX ();
+  record_unwind_protect (restore_selected_window,
+                        selected_window);
+
+  /* Temporarily switch to F's selected window at the time of the last
+     redisplay.  */
+  select_window (f->old_selected_window, Qt);
+
+  if (point == PT)
+    {
+      if (f->conversion.batch_edit_count > 0)
+       f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
+      else if (text_interface && text_interface->point_changed)
+       text_interface->point_changed (f,
+                                      XWINDOW (f->old_selected_window),
+                                      current_buffer);
+    }
+  else
+    /* Set the point.  */
+    Fgoto_char (make_fixnum (point));
+
+  if (mark == point
+      && !NILP (BVAR (current_buffer, mark_active)))
+    call0 (Qdeactivate_mark);
+  else
+    call1 (Qpush_mark, make_fixnum (mark));
+
+  /* Update the ephemeral last point.  */
+  w = XWINDOW (selected_window);
+  w->ephemeral_last_point = PT;
+
+  TEXTCONV_DEBUG ("set point and mark: %td %td",
+                 PT, get_mark ());
+
+  unbind_to (count, Qnil);
+}
+
+/* Complete the edit specified by the counter value inside *TOKEN.  */
+
+static void
+complete_edit (void *token)
+{
+  if (text_interface && text_interface->notify_conversion)
+    text_interface->notify_conversion (*(unsigned long *) token);
+}
+
+/* Context for complete_edit_check.  */
+
+struct complete_edit_check_context
+{
+  /* The window.  */
+  struct window *w;
+
+  /* Whether or not editing was successful.  */
+  bool check;
+};
+
+/* If CONTEXT->check is false, then update W's ephemeral last point
+   and give it to the input method, the assumption being that an
+   editing operation signalled.  */
+
+static void
+complete_edit_check (void *ptr)
+{
+  struct complete_edit_check_context *context;
+  struct frame *f;
+
+  context = ptr;
+
+  if (!context->check)
+    {
+      /* Figure out the new position of point.  */
+      context->w->ephemeral_last_point
+       = window_point (context->w);
+
+      /* See if the frame is still alive.  */
+
+      f = WINDOW_XFRAME (context->w);
+
+      if (!FRAME_LIVE_P (f))
+       return;
+
+      if (text_interface && text_interface->point_changed)
+       {
+         if (f->conversion.batch_edit_count > 0)
+           f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
+         else
+           text_interface->point_changed (f, context->w, NULL);
+       }
+    }
+}
+
+/* Process and free the text conversion ACTION.  F must be the frame
+   on which ACTION will be performed.
+
+   Value is the window which was used, or NULL.  */
+
+static struct window *
+handle_pending_conversion_events_1 (struct frame *f,
+                                   struct text_conversion_action *action)
+{
+  Lisp_Object data;
+  enum text_conversion_operation operation;
+  struct buffer *buffer UNINIT;
+  struct window *w;
+  specpdl_ref count;
+  unsigned long token;
+  struct complete_edit_check_context context;
+
+  /* Next, process this action and free it.  */
+
+  data = action->data;
+  operation = action->operation;
+  token = action->counter;
+  xfree (action);
+
+  /* Text conversion events can still arrive immediately after
+     `conversion_disabled_p' becomes true.  In that case, process all
+     events, but don't perform any associated actions.  */
+
+  if (conversion_disabled_p ())
+    return NULL;
+
+  /* check is a flag used by complete_edit_check to determine whether
+     or not the editing operation completed successfully.  */
+  context.check = false;
+
+  /* Make sure completion is signalled.  */
+  count = SPECPDL_INDEX ();
+  record_unwind_protect_ptr (complete_edit, &token);
+  w = NULL;
+
+  if (WINDOW_LIVE_P (f->old_selected_window))
+    {
+      w = XWINDOW (f->old_selected_window);
+      buffer = XBUFFER (WINDOW_BUFFER (w));
+      context.w = w;
+
+      /* Notify the input method of any editing failures.  */
+      record_unwind_protect_ptr (complete_edit_check, &context);
+    }
+
+  switch (operation)
+    {
+    case TEXTCONV_START_BATCH_EDIT:
+      f->conversion.batch_edit_count++;
+      break;
+
+    case TEXTCONV_END_BATCH_EDIT:
+      if (f->conversion.batch_edit_count > 0)
+       f->conversion.batch_edit_count--;
+
+      if (!WINDOW_LIVE_P (f->old_selected_window))
+       break;
+
+      if (f->conversion.batch_edit_flags & PENDING_POINT_CHANGE)
+       text_interface->point_changed (f, w, buffer);
+
+      if (f->conversion.batch_edit_flags & PENDING_COMPOSE_CHANGE)
+       text_interface->compose_region_changed (f);
+
+      f->conversion.batch_edit_flags = 0;
+      break;
+
+    case TEXTCONV_COMMIT_TEXT:
+      really_commit_text (f, XFIXNUM (XCAR (data)), XCDR (data));
+      break;
+
+    case TEXTCONV_FINISH_COMPOSING_TEXT:
+      really_finish_composing_text (f, !NILP (data));
+      break;
+
+    case TEXTCONV_SET_COMPOSING_TEXT:
+      really_set_composing_text (f, XFIXNUM (XCAR (data)),
+                                XCDR (data));
+      break;
+
+    case TEXTCONV_SET_COMPOSING_REGION:
+      really_set_composing_region (f, XFIXNUM (XCAR (data)),
+                                  XFIXNUM (XCDR (data)));
+      break;
+
+    case TEXTCONV_SET_POINT_AND_MARK:
+      really_set_point_and_mark (f, XFIXNUM (XCAR (data)),
+                                XFIXNUM (XCDR (data)));
+      break;
+
+    case TEXTCONV_DELETE_SURROUNDING_TEXT:
+      really_delete_surrounding_text (f, XFIXNUM (XCAR (data)),
+                                     XFIXNUM (XCDR (data)));
+      break;
+
+    case TEXTCONV_REQUEST_POINT_UPDATE:
+      really_request_point_update (f);
+      break;
+
+    case TEXTCONV_BARRIER:
+      if (kbd_fetch_ptr != kbd_store_ptr)
+       emacs_abort ();
+
+      /* Once a barrier is hit, synchronize F's selected window's
+        `ephemeral_last_point' with its current point.  The reason
+        for this is because otherwise a previous keyboard event may
+        have taken place without redisplay happening in between.  */
+
+      if (w)
+       w->ephemeral_last_point = window_point (w);
+      break;
+    }
+
+  /* Signal success.  */
+  context.check = true;
+  unbind_to (count, Qnil);
+
+  return w;
+}
+
+/* Decrement the variable pointed to by *PTR.  */
+
+static void
+decrement_inside (void *ptr)
+{
+  int *i;
+
+  i = ptr;
+  (*i)--;
+}
+
+/* Process any outstanding text conversion events.
+   This may run Lisp or signal.  */
+
+void
+handle_pending_conversion_events (void)
+{
+  struct frame *f;
+  Lisp_Object tail, frame;
+  struct text_conversion_action *action, *next;
+  bool handled;
+  static int inside;
+  specpdl_ref count;
+  ptrdiff_t last_point;
+  struct window *w;
+
+  handled = false;
+
+  /* Reset Vtext_conversion_edits.  Do not do this if called
+     reentrantly.  */
+
+  if (!inside)
+    Vtext_conversion_edits = Qnil;
+
+  inside++;
+
+  count = SPECPDL_INDEX ();
+  record_unwind_protect_ptr (decrement_inside, &inside);
+
+  FOR_EACH_FRAME (tail, frame)
+    {
+      f = XFRAME (frame);
+      last_point = -1;
+      w = NULL;
+
+      /* Test if F has any outstanding conversion events.  Then
+        process them in bottom to up order.  */
+      while (true)
+       {
+         /* Update the input method if handled &&
+            w->ephemeral_last_point != last_point.  */
+         if (w && (last_point != w->ephemeral_last_point))
+           {
+             if (handled
+                 && last_point != -1
+                 && text_interface
+                 && text_interface->point_changed)
+               {
+                 if (f->conversion.batch_edit_count > 0)
+                   f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
+                 else
+                   text_interface->point_changed (f, NULL, NULL);
+               }
+
+             last_point = w->ephemeral_last_point;
+           }
+
+         /* Reload action.  This needs to be reentrant as buffer
+            modification functions can call `read-char'.  */
+         action = f->conversion.actions;
+
+         /* If there are no more actions, break.  */
+
+         if (!action)
+           break;
+
+         /* If action is a barrier event and the keyboard buffer is
+            not yet empty, break out of the loop.  */
+
+         if (action->operation == TEXTCONV_BARRIER
+             && kbd_store_ptr != kbd_fetch_ptr)
+           break;
+
+         /* Unlink this action.  */
+         next = action->next;
+         f->conversion.actions = next;
+
+         /* Handle and free the action.  */
+         w = handle_pending_conversion_events_1 (f, action);
+         handled = true;
+       }
+    }
+
+  unbind_to (count, Qnil);
+}
+
+/* Start a ``batch edit'' in F.  During a batch edit, point_changed
+   will not be called until the batch edit ends.
+
+   Process the actual operation in the event loop in keyboard.c; then,
+   call `notify_conversion' in the text conversion interface with
+   COUNTER.  */
+
+void
+start_batch_edit (struct frame *f, unsigned long counter)
+{
+  struct text_conversion_action *action, **last;
+
+  action = xmalloc (sizeof *action);
+  action->operation = TEXTCONV_START_BATCH_EDIT;
+  action->data = Qnil;
+  action->next = NULL;
+  action->counter = counter;
+  for (last = &f->conversion.actions; *last; last = &(*last)->next)
+    ;;
+  *last = action;
+  input_pending = true;
+}
+
+/* End a ``batch edit''.  It is ok to call this function even if a
+   batch edit has not yet started, in which case it does nothing.
+
+   COUNTER means the same as in `start_batch_edit'.  */
+
+void
+end_batch_edit (struct frame *f, unsigned long counter)
+{
+  struct text_conversion_action *action, **last;
+
+  action = xmalloc (sizeof *action);
+  action->operation = TEXTCONV_END_BATCH_EDIT;
+  action->data = Qnil;
+  action->next = NULL;
+  action->counter = counter;
+  for (last = &f->conversion.actions; *last; last = &(*last)->next)
+    ;;
+  *last = action;
+  input_pending = true;
+}
+
+/* Insert the specified STRING into F's current buffer's composition
+   region, and set point to POSITION relative to STRING.
+
+   If there is no composition region, use the active region instead.
+   If that doesn't exist either, insert STRING after point.
+
+   COUNTER means the same as in `start_batch_edit'.  */
+
+void
+commit_text (struct frame *f, Lisp_Object string,
+            ptrdiff_t position, unsigned long counter)
+{
+  struct text_conversion_action *action, **last;
+
+  action = xmalloc (sizeof *action);
+  action->operation = TEXTCONV_COMMIT_TEXT;
+  action->data = Fcons (make_fixnum (position), string);
+  action->next = NULL;
+  action->counter = counter;
+  for (last = &f->conversion.actions; *last; last = &(*last)->next)
+    ;;
+  *last = action;
+  input_pending = true;
+}
+
+/* Remove the composition region and its overlay from F's current
+   buffer.  Leave the text being composed intact.
+
+   If UPDATE, call `compose_region_changed' after the region is
+   removed.
+
+   COUNTER means the same as in `start_batch_edit'.  */
+
+void
+finish_composing_text (struct frame *f, unsigned long counter,
+                      bool update)
+{
+  struct text_conversion_action *action, **last;
+
+  action = xmalloc (sizeof *action);
+  action->operation = TEXTCONV_FINISH_COMPOSING_TEXT;
+  action->data = update ? Qt : Qnil;
+  action->next = NULL;
+  action->counter = counter;
+  for (last = &f->conversion.actions; *last; last = &(*last)->next)
+    ;;
+  *last = action;
+  input_pending = true;
+}
+
+/* Insert the given STRING and make it the currently active
+   composition.
+
+   If there is currently no composing or active region, then the new
+   value of point is used as the composing region.
+
+   Then, the composing or active region is replaced with the text in
+   the specified string.
+
+   Finally, move point to new_point, which is relative to either the
+   start or the end of OBJECT depending on whether or not it is less
+   than zero.
+
+   COUNTER means the same as in `start_batch_edit'.  */
+
+void
+set_composing_text (struct frame *f, Lisp_Object object,
+                   ptrdiff_t new_point, unsigned long counter)
+{
+  struct text_conversion_action *action, **last;
+
+  action = xmalloc (sizeof *action);
+  action->operation = TEXTCONV_SET_COMPOSING_TEXT;
+  action->data = Fcons (make_fixnum (new_point),
+                       object);
+  action->next = NULL;
+  action->counter = counter;
+  for (last = &f->conversion.actions; *last; last = &(*last)->next)
+    ;;
+  *last = action;
+  input_pending = true;
+}
+
+/* Make the region between START and END the currently active
+   ``composing region''.
+
+   The ``composing region'' is a region of text in the buffer that is
+   about to undergo editing by the input method.  */
+
+void
+set_composing_region (struct frame *f, ptrdiff_t start,
+                     ptrdiff_t end, unsigned long counter)
+{
+  struct text_conversion_action *action, **last;
+
+  if (start > MOST_POSITIVE_FIXNUM)
+    start = MOST_POSITIVE_FIXNUM;
+
+  if (end > MOST_POSITIVE_FIXNUM)
+    end = MOST_POSITIVE_FIXNUM;
+
+  action = xmalloc (sizeof *action);
+  action->operation = TEXTCONV_SET_COMPOSING_REGION;
+  action->data = Fcons (make_fixnum (start),
+                       make_fixnum (end));
+  action->next = NULL;
+  action->counter = counter;
+  for (last = &f->conversion.actions; *last; last = &(*last)->next)
+    ;;
+  *last = action;
+  input_pending = true;
+}
+
+/* Move point in F's selected buffer to POINT and maybe push MARK.
+
+   COUNTER means the same as in `start_batch_edit'.  */
+
+void
+textconv_set_point_and_mark (struct frame *f, ptrdiff_t point,
+                            ptrdiff_t mark, unsigned long counter)
+{
+  struct text_conversion_action *action, **last;
+
+  if (point > MOST_POSITIVE_FIXNUM)
+    point = MOST_POSITIVE_FIXNUM;
+
+  action = xmalloc (sizeof *action);
+  action->operation = TEXTCONV_SET_POINT_AND_MARK;
+  action->data = Fcons (make_fixnum (point),
+                       make_fixnum (mark));
+  action->next = NULL;
+  action->counter = counter;
+  for (last = &f->conversion.actions; *last; last = &(*last)->next)
+    ;;
+  *last = action;
+  input_pending = true;
+}
+
+/* Delete LEFT and RIGHT characters around point in F's old selected
+   window.  */
+
+void
+delete_surrounding_text (struct frame *f, ptrdiff_t left,
+                        ptrdiff_t right, unsigned long counter)
+{
+  struct text_conversion_action *action, **last;
+
+  action = xmalloc (sizeof *action);
+  action->operation = TEXTCONV_DELETE_SURROUNDING_TEXT;
+  action->data = Fcons (make_fixnum (left),
+                       make_fixnum (right));
+  action->next = NULL;
+  action->counter = counter;
+  for (last = &f->conversion.actions; *last; last = &(*last)->next)
+    ;;
+  *last = action;
+  input_pending = true;
+}
+
+/* Request an immediate call to INTERFACE->point_changed with the new
+   details of F's region unless a batch edit is in progress.  */
+
+void
+request_point_update (struct frame *f, unsigned long counter)
+{
+  struct text_conversion_action *action, **last;
+
+  action = xmalloc (sizeof *action);
+  action->operation = TEXTCONV_REQUEST_POINT_UPDATE;
+  action->data = Qnil;
+  action->next = NULL;
+  action->counter = counter;
+  for (last = &f->conversion.actions; *last; last = &(*last)->next)
+    ;;
+  *last = action;
+  input_pending = true;
+}
+
+/* Request that text conversion on F pause until the keyboard buffer
+   becomes empty.
+
+   Use this function to ensure that edits associated with a keyboard
+   event complete before the text conversion edits after the barrier
+   take place.  */
+
+void
+textconv_barrier (struct frame *f, unsigned long counter)
+{
+  struct text_conversion_action *action, **last;
+
+  action = xmalloc (sizeof *action);
+  action->operation = TEXTCONV_BARRIER;
+  action->data = Qnil;
+  action->next = NULL;
+  action->counter = counter;
+  for (last = &f->conversion.actions; *last; last = &(*last)->next)
+    ;;
+  *last = action;
+  input_pending = true;
+}
+
+/* Return N characters of text around point in F's old selected
+   window.
+
+   If N is -1, return the text between point and mark instead, given
+   that the mark is active.
+
+   Set *N to the actual number of characters returned, *START_RETURN
+   to the position of the first character returned, *START_OFFSET to
+   the offset of the lesser of mark and point within that text,
+   *END_OFFSET to the greater of mark and point within that text, and
+   *LENGTH to the actual number of characters returned, *BYTES to the
+   actual number of bytes returned, and *MARK_ACTIVE to whether or not
+   the mark is active.
+
+   Value is NULL upon failure, and a malloced string upon success.  */
+
+char *
+get_extracted_text (struct frame *f, ptrdiff_t n,
+                   ptrdiff_t *start_return,
+                   ptrdiff_t *start_offset,
+                   ptrdiff_t *end_offset, ptrdiff_t *length,
+                   ptrdiff_t *bytes, bool *mark_active)
+{
+  specpdl_ref count;
+  ptrdiff_t start, end, start_byte, end_byte, mark;
+  char *buffer;
+
+  if (!WINDOW_LIVE_P (f->old_selected_window))
+    return NULL;
+
+  /* Save the excursion, as there will be extensive changes to the
+     selected window.  */
+  count = SPECPDL_INDEX ();
+  record_unwind_protect_excursion ();
+
+  /* Inhibit quitting.  */
+  specbind (Qinhibit_quit, Qt);
+
+  /* Temporarily switch to F's selected window at the time of the last
+     redisplay.  */
+  select_window (f->old_selected_window, Qt);
+  buffer = NULL;
+
+  /* Figure out the bounds of the text to return.  */
+  if (n != -1)
+    {
+      /* Make sure n is at least 4, leaving two characters around
+        PT.  */
+      n = max (4, n);
+
+      start = PT - n / 2;
+      end = PT + n - n / 2;
+    }
+  else
+    {
+      if (!NILP (BVAR (current_buffer, mark_active))
+         && XMARKER (BVAR (current_buffer, mark))->buffer)
+       {
+         start = marker_position (BVAR (current_buffer, mark));
+         end = PT;
+
+         /* Sort start and end.  start_byte is used to hold a
+            temporary value.  */
+
+         if (start > end)
+           {
+             start_byte = end;
+             end = start;
+             start = start_byte;
+           }
+       }
+      else
+       goto finish;
+    }
+
+  start = max (start, BEGV);
+  end = min (end, ZV);
+
+  /* Detect overflow.  */
+
+  if (!(start <= PT && PT <= end))
+    goto finish;
+
+  /* Convert the character positions to byte positions.  */
+  start_byte = CHAR_TO_BYTE (start);
+  end_byte = CHAR_TO_BYTE (end);
+
+  /* Extract the text from the buffer.  */
+  buffer = xmalloc (end_byte - start_byte);
+  copy_buffer (start, start_byte, end, end_byte,
+              buffer);
+
+  /* Get the mark.  If it's not active, use PT.  */
+
+  mark = get_mark ();
+  *mark_active = true;
+
+  if (mark == -1)
+    {
+      mark = PT;
+      *mark_active = false;
+    }
+
+  /* Return the offsets.  */
+  *start_return = start;
+  *start_offset = min (mark - start, PT - start);
+  *end_offset = max (mark - start, PT - start);
+  *length = end - start;
+  *bytes = end_byte - start_byte;
+
+  TEXTCONV_DEBUG ("get_extracted_text: PT, mark, start: %td, %td, %td",
+                 PT, mark, start);
+
+ finish:
+  unbind_to (count, Qnil);
+  return buffer;
+}
+
+/* Return the text between the positions PT - LEFT and PT + RIGHT.  If
+   the mark is active, return the range of text relative to the bounds
+   of the region instead.
+
+   Set *LENGTH to the number of characters returned, *BYTES to the
+   number of bytes returned, *OFFSET to the character position of the
+   returned text, and *START_RETURN and *END_RETURN to the mark and
+   point relative to that position.  */
+
+char *
+get_surrounding_text (struct frame *f, ptrdiff_t left,
+                     ptrdiff_t right, ptrdiff_t *length,
+                     ptrdiff_t *bytes, ptrdiff_t *offset,
+                     ptrdiff_t *start_return,
+                     ptrdiff_t *end_return)
+{
+  specpdl_ref count;
+  ptrdiff_t start, end, start_byte, end_byte, mark, temp;
+  char *buffer;
+
+  if (!WINDOW_LIVE_P (f->old_selected_window))
+    return NULL;
+
+  /* Save the excursion, as there will be extensive changes to the
+     selected window.  */
+  count = SPECPDL_INDEX ();
+  record_unwind_protect_excursion ();
+
+  /* Inhibit quitting.  */
+  specbind (Qinhibit_quit, Qt);
+
+  /* Temporarily switch to F's selected window at the time of the last
+     redisplay.  */
+  select_window (f->old_selected_window, Qt);
+  buffer = NULL;
+
+  /* Figure out the bounds of the text to return.  */
+
+  /* First, obtain start and end.  */
+  end = get_mark ();
+  start = PT;
+
+  /* If the mark is not active, make it start and end.  */
+
+  if (end == -1)
+    end = start;
+
+  /* Now sort start and end.  */
+
+  if (end < start)
+    {
+      temp = start;
+      start = end;
+      end = temp;
+    }
+
+  /* And subtract left and right.  */
+
+  if (INT_SUBTRACT_WRAPV (start, left, &start)
+      || INT_ADD_WRAPV (end, right, &end))
+    goto finish;
+
+  start = max (start, BEGV);
+  end = min (end, ZV);
+
+  /* Detect overflow.  */
+
+  if (!(start <= PT && PT <= end))
+    goto finish;
+
+  /* Convert the character positions to byte positions.  */
+  start_byte = CHAR_TO_BYTE (start);
+  end_byte = CHAR_TO_BYTE (end);
+
+  /* Extract the text from the buffer.  */
+  buffer = xmalloc (end_byte - start_byte);
+  copy_buffer (start, start_byte, end, end_byte,
+              buffer);
+
+  /* Get the mark.  If it's not active, use PT.  */
+
+  mark = get_mark ();
+
+  if (mark == -1)
+    mark = PT;
+
+  /* Return the offsets.  Unlike `get_extracted_text', this need not
+     sort mark and point.  */
+
+  *offset = start;
+  *start_return = mark - start;
+  *end_return = PT - start;
+  *length = end - start;
+  *bytes = end_byte - start_byte;
+
+ finish:
+  unbind_to (count, Qnil);
+  return buffer;
+}
+
+/* Return whether or not text conversion is temporarily disabled.
+   `reset' should always call this to determine whether or not to
+   disable the input method.  */
+
+bool
+conversion_disabled_p (void)
+{
+  return suppress_conversion_count > 0;
+}
+
+
+
+/* Window system interface.  These are called from the rest of
+   Emacs.  */
+
+/* Notice that F's selected window has been set from redisplay.
+   Reset F's input method state.  */
+
+void
+report_selected_window_change (struct frame *f)
+{
+  struct window *w;
+
+  reset_frame_state (f);
+
+  if (!text_interface)
+    return;
+
+  /* When called from window.c, F's selected window has already been
+     redisplayed, but w->last_point has not yet been updated.  Update
+     it here to avoid race conditions when the IM asks for the initial
+     selection position immediately after.  */
+
+  if (WINDOWP (f->selected_window))
+    {
+      w = XWINDOW (f->selected_window);
+      w->ephemeral_last_point = window_point (w);
+    }
+
+  text_interface->reset (f);
+}
+
+/* Notice that the point in F's selected window's current buffer has
+   changed.
+
+   F is the frame whose selected window was changed, W is the window
+   in question, and BUFFER is that window's current buffer.
+
+   Tell the text conversion interface about the change; it will likely
+   pass the information on to the system input method.  */
+
+void
+report_point_change (struct frame *f, struct window *window,
+                    struct buffer *buffer)
+{
+  if (!text_interface || !text_interface->point_changed)
+    return;
+
+  if (f->conversion.batch_edit_count > 0)
+    f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
+  else
+    text_interface->point_changed (f, window, buffer);
+}
+
+/* Temporarily disable text conversion.  Must be paired with a
+   corresponding call to resume_text_conversion.  */
+
+void
+disable_text_conversion (void)
+{
+  Lisp_Object tail, frame;
+  struct frame *f;
+
+  suppress_conversion_count++;
+
+  if (!text_interface || suppress_conversion_count > 1)
+    return;
+
+  /* Loop through and reset the input method on each window system
+     frame.  It should call conversion_disabled_p and then DTRT.  */
+
+  FOR_EACH_FRAME (tail, frame)
+    {
+      f = XFRAME (frame);
+      reset_frame_state (f);
+
+      if (FRAME_WINDOW_P (f) && FRAME_VISIBLE_P (f))
+       text_interface->reset (f);
+    }
+}
+
+/* Undo the effect of the last call to `disable_text_conversion'.  */
+
+void
+resume_text_conversion (void)
+{
+  Lisp_Object tail, frame;
+  struct frame *f;
+
+  suppress_conversion_count--;
+  eassert (suppress_conversion_count >= 0);
+
+  if (!text_interface || suppress_conversion_count)
+    return;
+
+  /* Loop through and reset the input method on each window system
+     frame.  It should call conversion_disabled_p and then DTRT.  */
+
+  FOR_EACH_FRAME (tail, frame)
+    {
+      f = XFRAME (frame);
+      reset_frame_state (f);
+
+      if (FRAME_WINDOW_P (f) && FRAME_VISIBLE_P (f))
+       text_interface->reset (f);
+    }
+}
+
+/* Register INTERFACE as the text conversion interface.  */
+
+void
+register_textconv_interface (struct textconv_interface *interface)
+{
+  text_interface = interface;
+}
+
+
+
+/* List of buffers whose text conversion state will be reset after a
+   key sequence is read.  */
+static Lisp_Object postponed_buffers;
+
+/* Reset the text conversion style of each frame whose selected buffer
+   is contained inside `postponed_buffers'.  Set `postponed_buffers'
+   to nil.  */
+
+void
+check_postponed_buffers (void)
+{
+  Lisp_Object buffer, tail, frame;
+  struct buffer *b;
+  struct frame *f;
+
+  buffer = postponed_buffers;
+  postponed_buffers = Qnil;
+
+  if (!text_interface->reset)
+    return;
+
+  FOR_EACH_TAIL (buffer)
+    {
+      b = XBUFFER (XCAR (buffer));
+
+      /* Continue if this is a dead buffer.  */
+
+      if (!BUFFER_LIVE_P (b))
+       continue;
+
+      /* If no windows are displaying B anymore, continue.  */
+
+      if (!buffer_window_count (b))
+       continue;
+
+      /* Look for frames which have B selected.  */
+
+      FOR_EACH_FRAME (tail, frame)
+       {
+         f = XFRAME (frame);
+
+         if (WINDOW_LIVE_P (f->old_selected_window)
+             && FRAME_WINDOW_P (f)
+             /* N.B. that the same frame can't be reset twice as long
+                as the list of buffers remains unique.  */
+             && EQ (XWINDOW (f->old_selected_window)->contents,
+                    XCAR (buffer)))
+           {
+             block_input ();
+             reset_frame_state (f);
+             text_interface->reset (f);
+             unblock_input ();
+           }
+       }
+    }
+}
+
+/* Lisp interface.  */
+
+DEFUN ("set-text-conversion-style", Fset_text_conversion_style,
+       Sset_text_conversion_style, 1, 2, 0,
+       doc: /* Set the text conversion style in the current buffer.
+
+Set `text-conversion-style' to VALUE, then force any input method
+editing frame displaying this buffer to stop itself.
+
+This can lead to a significant amount of time being taken by the input
+method resetting itself, so you should not use this function lightly;
+instead, set `text-conversion-style' before your buffer is displayed,
+and let redisplay manage the input method appropriately.
+
+If a key sequence is currently being read (either through the command
+loop or by a call to `read-key-sequence') and AFTER-KEY-SEQUENCE is
+non-nil, don't perform changes to the input method until the key
+sequence is read.  This is useful within a function bound to
+`input-decode-map' or `local-function-key-map', as it prevents the
+input method from being redundantly enabled according to VALUE if the
+replacement key sequence returned starts a new key sequence and makes
+`read-key-sequence' disable text conversion again.  */)
+  (Lisp_Object value, Lisp_Object after_key_sequence)
+{
+  Lisp_Object tail, frame;
+  struct frame *f;
+  Lisp_Object buffer;
+
+  bset_text_conversion_style (current_buffer, value);
+
+  if (!text_interface)
+    return Qnil;
+
+  /* If there are any seleted windows displaying this buffer, reset
+     text conversion on their associated frames.  */
+
+  if (buffer_window_count (current_buffer))
+    {
+      buffer = Fcurrent_buffer ();
+
+      /* Postpone changes to the actual text conversion state if
+        AFTER_KEY_SEQUENCE is non-nil and a key sequence is being
+        read.  */
+
+      if (reading_key_sequence && !NILP (after_key_sequence))
+       {
+         if (NILP (Fmemq (buffer, postponed_buffers)))
+           /* `check_postponed_buffers' will hopefully be called soon
+              enough to avoid postponed_buffers growing
+              indefinitely.  */
+           postponed_buffers = Fcons (buffer, postponed_buffers);
+         return Qnil;
+       }
+
+      FOR_EACH_FRAME (tail, frame)
+       {
+         f = XFRAME (frame);
+
+         if (WINDOW_LIVE_P (f->old_selected_window)
+             && FRAME_WINDOW_P (f)
+             && EQ (XWINDOW (f->old_selected_window)->contents,
+                    buffer))
+           {
+             block_input ();
+             reset_frame_state (f);
+             text_interface->reset (f);
+             unblock_input ();
+           }
+       }
+    }
+
+  return Qnil;
+}
+
+
+
+void
+syms_of_textconv (void)
+{
+  DEFSYM (Qaction, "action");
+  DEFSYM (Qtext_conversion, "text-conversion");
+  DEFSYM (Qpush_mark, "push-mark");
+  DEFSYM (Qunderline, "underline");
+  DEFSYM (Qoverriding_text_conversion_style,
+         "overriding-text-conversion-style");
+
+  DEFVAR_LISP ("text-conversion-edits", Vtext_conversion_edits,
+    doc: /* List of buffers that were last edited as a result of text 
conversion.
+
+This list can be used while handling a `text-conversion' event to
+determine which changes have taken place.
+
+Each element of the list describes a single edit in a buffer, of the
+form:
+
+    (BUFFER BEG END EPHEMERAL)
+
+If an insertion or an edit to the buffer text is described, then BEG
+and END are markers which denote the bounds of the text that was
+changed or inserted.  If a deletion is described, then BEG and END are
+the same object.
+
+If EPHEMERAL is t, then the input method is preparing to make further
+edits to the text, so any actions that would otherwise be taken, such
+as indenting or automatically filling text, should not take place.
+
+Otherwise, it is either a string containing text that was inserted,
+text deleted before point, or nil if text was deleted after point.
+
+The list contents are ordered later edits first, so you must iterate
+through the list in reverse.  */);
+  Vtext_conversion_edits = Qnil;
+
+  DEFVAR_LISP ("overriding-text-conversion-style",
+              Voverriding_text_conversion_style,
+    doc: /* Non-buffer local version of `text-conversion-style'.
+
+If this variable is the symbol `lambda', it means to consult the
+buffer local variable `text-conversion-style' to determine whether or
+not to activate the input method.  Otherwise, its value is used in
+preference to any buffer local value of `text-conversion-style'.  */);
+  Voverriding_text_conversion_style = Qlambda;
+
+  DEFVAR_LISP ("text-conversion-face", Vtext_conversion_face,
+    doc: /* Face in which to display temporary edits by an input method.
+nil means to display no indication of a temporary edit.  */);
+  Vtext_conversion_face = Qunderline;
+
+  defsubr (&Sset_text_conversion_style);
+
+  postponed_buffers = Qnil;
+  staticpro (&postponed_buffers);
 }
diff --git a/src/textconv.h b/src/textconv.h
index f6e7eb7925f..feac5b805af 100644
--- a/src/textconv.h
+++ b/src/textconv.h
@@ -34,6 +34,21 @@ struct textconv_interface
      happen if the window is deleted or switches buffers, or an
      unexpected buffer change occurs.) */
   void (*reset) (struct frame *);
+
+  /* Notice that point or mark has moved in the specified frame's
+     selected window's selected buffer.  The second argument is the
+     window whose point changed, and the third argument is the
+     buffer.  */
+  void (*point_changed) (struct frame *, struct window *,
+                        struct buffer *);
+
+  /* Notice that the preconversion region has changed without point
+     being moved.  */
+  void (*compose_region_changed) (struct frame *);
+
+  /* Notice that an asynch conversion identified by COUNTER has
+     completed.  */
+  void (*notify_conversion) (unsigned long);
 };
 
 
@@ -103,7 +118,40 @@ struct textconv_callback_struct
   struct textconv_conversion_text text;
 };
 
-extern int textconv_query (struct frame *, struct textconv_callback_struct *);
-extern void register_texconv_interface (struct textconv_interface *);
+
+
+#define TEXTCONV_SKIP_CONVERSION_REGION (1 << 0)
+
+extern int textconv_query (struct frame *, struct textconv_callback_struct *,
+                          int);
+extern bool detect_conversion_events (void);
+extern void handle_pending_conversion_events (void);
+extern void start_batch_edit (struct frame *, unsigned long);
+extern void end_batch_edit (struct frame *, unsigned long);
+extern void commit_text (struct frame *, Lisp_Object, ptrdiff_t,
+                        unsigned long);
+extern void finish_composing_text (struct frame *, unsigned long,
+                                  bool);
+extern void set_composing_text (struct frame *, Lisp_Object,
+                               ptrdiff_t, unsigned long);
+extern void set_composing_region (struct frame *, ptrdiff_t, ptrdiff_t,
+                                 unsigned long);
+extern void textconv_set_point_and_mark (struct frame *, ptrdiff_t,
+                                        ptrdiff_t, unsigned long);
+extern void delete_surrounding_text (struct frame *, ptrdiff_t,
+                                    ptrdiff_t, unsigned long);
+extern void request_point_update (struct frame *, unsigned long);
+extern void textconv_barrier (struct frame *, unsigned long);
+extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *,
+                                ptrdiff_t *, ptrdiff_t *, ptrdiff_t *,
+                                ptrdiff_t *, bool *);
+extern char *get_surrounding_text (struct frame *, ptrdiff_t,
+                                  ptrdiff_t, ptrdiff_t *,
+                                  ptrdiff_t *, ptrdiff_t *,
+                                  ptrdiff_t *, ptrdiff_t *);
+extern bool conversion_disabled_p (void);
+extern void check_postponed_buffers (void);
+
+extern void register_textconv_interface (struct textconv_interface *);
 
 #endif /* _TEXTCONV_H_ */
diff --git a/src/verbose.mk.in b/src/verbose.mk.in
index a4e2aad9325..97799cee813 100644
--- a/src/verbose.mk.in
+++ b/src/verbose.mk.in
@@ -32,6 +32,11 @@ AM_V_GEN =
 AM_V_GLOBALS =
 AM_V_NO_PD =
 AM_V_RC =
+AM_V_JAVAC =
+AM_V_DX =
+AM_V_AAPT =
+AM_V_ZIPALIGN =
+AM_V_SILENT =
 else
 
 # Whether $(info ...) works.  This is to work around a bug in GNU Make
@@ -76,4 +81,10 @@ AM_V_GEN     = @$(info $   GEN      $@)
 AM_V_GLOBALS = @$(info $   GEN      globals.h)
 AM_V_NO_PD = --no-print-directory
 AM_V_RC      = @$(info $   RC       $@)
+
+# These are used for the Android port.
+AM_V_JAVAC     = @$(info $   JAVAC    $@)
+AM_V_D8                = @$(info $   D8       $@)
+AM_V_AAPT      = @$(info $   AAPT     $@)
+AM_V_SILENT    = @
 endif
diff --git a/src/w32.c b/src/w32.c
index a6bc0f4b2ee..c75beb630e5 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -10335,7 +10335,8 @@ check_windows_init_file (void)
         names from UTF-8 to ANSI.  */
       init_file = build_string ("term/w32-win");
       fd =
-       openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil, 0, 0);
+       openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil, 0, 0,
+              NULL);
       if (fd < 0)
        {
          Lisp_Object load_path_print = Fprin1_to_string (Vload_path,
diff --git a/src/w32fns.c b/src/w32fns.c
index dcf9a212bdd..07b389df84a 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -3852,7 +3852,7 @@ deliver_wm_chars (int do_translate, HWND hwnd, UINT msg, 
UINT wParam,
 
       /* What follows is just heuristics; the correct treatment requires
         non-destructive ToUnicode():
-          
http://search.cpan.org/~ilyaz/UI-KeyboardLayout/lib/UI/KeyboardLayout.pm#Can_an_application_on_Windows_accept_keyboard_events?_Part_IV:_application-specific_modifiers
+          
https://metacpan.org/dist/UI-KeyboardLayout/view/lib/UI/KeyboardLayout.pm#Can-an-application-on-Windows-accept-keyboard-events?-Part-IV:-application-specific-modifiers
 
         What one needs to find is:
           * which of the present modifiers AFFECT the resulting char(s)
@@ -3914,7 +3914,7 @@ deliver_wm_chars (int do_translate, HWND hwnd, UINT msg, 
UINT wParam,
         character is the same for AltGr-* (=rAlt-*) and Ctrl-Alt-* (in any
         combination of handedness).  For description of masks, see
 
-          
http://search.cpan.org/~ilyaz/UI-KeyboardLayout/lib/UI/KeyboardLayout.pm#Keyboard_input_on_Windows,_Part_I:_what_is_the_kernel_doing?
+          
https://metacpan.org/dist/UI-KeyboardLayout/view/lib/UI/KeyboardLayout.pm#Keyboard-input-on-Windows,-Part-I:-what-is-the-kernel-doing?
 
         By default, Emacs was using these coincidences via the following
         heuristics: it was treating:
diff --git a/src/w32font.c b/src/w32font.c
index 2917fa55f9f..0371b24e1d1 100644
--- a/src/w32font.c
+++ b/src/w32font.c
@@ -2031,7 +2031,7 @@ static void
 fill_in_logfont (struct frame *f, LOGFONT *logfont, Lisp_Object font_spec)
 {
   Lisp_Object tmp, extra;
-  int dpi = FRAME_RES_Y (f);
+  int dpi = FRAME_RES (f);
 
   tmp = AREF (font_spec, FONT_DPI_INDEX);
   if (FIXNUMP (tmp))
diff --git a/src/w32proc.c b/src/w32proc.c
index 77a4ac1ff7e..edc4394b17f 100644
--- a/src/w32proc.c
+++ b/src/w32proc.c
@@ -1956,7 +1956,7 @@ sys_spawnve (int mode, char *cmdname, char **argv, char 
**envp)
       program = build_string (cmdname);
       full = Qnil;
       openp (Vexec_path, program, Vexec_suffixes, &full, make_fixnum (X_OK),
-            0, 0);
+            0, 0, NULL);
       if (NILP (full))
        {
          errno = EINVAL;
diff --git a/src/w32term.h b/src/w32term.h
index 69ef297cbcc..68237bfff1d 100644
--- a/src/w32term.h
+++ b/src/w32term.h
@@ -29,7 +29,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
    calls us.  The ALIGN_STACK attribute forces GCC to emit a preamble
    code to re-align the stack at function entry.  Further details
    about this can be found in
-   http://www.peterstock.co.uk/games/mingw_sse/.  */
+   https://www.peterstock.co.uk/games/mingw_sse/.  */
 #ifdef __GNUC__
 # if USE_STACK_LISP_OBJECTS && !defined _WIN64 && !defined __x86_64__  \
   && __GNUC__ + (__GNUC_MINOR__ > 1) >= 5
diff --git a/src/window.c b/src/window.c
index 2a0c62f5d53..968b982c135 100644
--- a/src/window.c
+++ b/src/window.c
@@ -3514,7 +3514,10 @@ window-start value is reasonable when this function is 
called.  */)
 void
 replace_buffer_in_windows (Lisp_Object buffer)
 {
-  call1 (Qreplace_buffer_in_windows, buffer);
+  /* When kill-buffer is called early during loadup, this function is
+     undefined.  */
+  if (!NILP (Ffboundp (Qreplace_buffer_in_windows)))
+    call1 (Qreplace_buffer_in_windows, buffer);
 }
 
 /* If BUFFER is shown in a window, safely replace it with some other
diff --git a/src/window.h b/src/window.h
index 9da6126c321..413293420fd 100644
--- a/src/window.h
+++ b/src/window.h
@@ -286,6 +286,25 @@ struct window
        it should be positive.  */
     ptrdiff_t last_point;
 
+#ifdef HAVE_TEXT_CONVERSION
+    /* ``ephemeral'' last point position.  This is used while
+       processing text conversion events.
+
+       `last_point' is normally used during redisplay to indicate the
+       position of point as seem by the input method.  However, it is
+       not updated if consequtive conversions are processed at the
+       same time.
+
+       This `ephemeral_last_point' field is either the last point as
+       set in redisplay or the last point after a text editing
+       operation.  */
+    ptrdiff_t ephemeral_last_point;
+#endif
+
+    /* Value of mark in the selected window at the time of the last
+       redisplay.  -1 if the mark is not valid or active.  */
+    ptrdiff_t last_mark;
+
     /* Line number and position of a line somewhere above the top of the
        screen.  If this field is zero, it means we don't have a base line.  */
     ptrdiff_t base_line_number;
@@ -740,14 +759,14 @@ wset_next_buffers (struct window *w, Lisp_Object val)
    + WINDOW_RIGHT_PIXEL_EDGE (W))
 
 /* True if W is a menu bar window.  */
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined 
(USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
 #define WINDOW_MENU_BAR_P(W) \
   (WINDOWP (WINDOW_XFRAME (W)->menu_bar_window) \
    && (W) == XWINDOW (WINDOW_XFRAME (W)->menu_bar_window))
-#else
+#else /* !HAVE_WINDOW_SYSTEM || HAVE_EXT_MENU_BAR */
 /* No menu bar windows if X toolkit is in use.  */
 #define WINDOW_MENU_BAR_P(W) false
-#endif
+#endif /* HAVE_WINDOW_SYSTEM && !HAVE_EXT_MENU_BAR */
 
 /* True if W is a tab bar window.  */
 #if defined (HAVE_WINDOW_SYSTEM)
diff --git a/src/xdisp.c b/src/xdisp.c
index aa49749edf9..d055bb3bf6a 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -14639,21 +14639,32 @@ tab_bar_item_info (struct frame *f, struct glyph 
*glyph,
                             Qmenu_item, f->current_tab_bar_string);
   if (! FIXNUMP (prop))
     return false;
+
   *prop_idx = XFIXNUM (prop);
 
-  *close_p = !NILP (Fget_text_property (make_fixnum (charpos),
-                                        Qclose_tab,
-                                        f->current_tab_bar_string));
+  if (close_p)
+    *close_p = !NILP (Fget_text_property (make_fixnum (charpos),
+                                         Qclose_tab,
+                                         f->current_tab_bar_string));
 
   return true;
 }
 
 
-/* Get information about the tab-bar item at position X/Y on frame F.
-   Return in *GLYPH a pointer to the glyph of the tab-bar item in
-   the current matrix of the tab-bar window of F, or NULL if not
-   on a tab-bar item.  Return in *PROP_IDX the index of the tab-bar
-   item in F->tab_bar_items.  Value is
+/* Get information about the tab-bar item at position X/Y on frame F's
+   tab bar window.
+
+   Set *GLYPH to a pointer to the glyph of the tab-bar item in the
+   current matrix of the tab-bar window of F, or NULL if not on a
+   tab-bar item.  Return in *PROP_IDX the index of the tab-bar item in
+   F->tab_bar_items.
+
+   Place the window-relative vpos of Y in *VPOS, and the
+   window-relative hpos of X in *HPOS.  If CLOSE_P, set it to whether
+   or not the tab bar item represents a button that should close a
+   tab.
+
+   Value is
 
    -1  if X/Y is not on a tab-bar item
    0   if X/Y is on the same item that was highlighted before.
@@ -14661,7 +14672,7 @@ tab_bar_item_info (struct frame *f, struct glyph *glyph,
 
 static int
 get_tab_bar_item (struct frame *f, int x, int y, struct glyph **glyph,
-                  int *hpos, int *vpos, int *prop_idx, bool *close_p)
+                 int *hpos, int *vpos, int *prop_idx, bool *close_p)
 {
   struct window *w = XWINDOW (f->tab_bar_window);
   int area;
@@ -14679,6 +14690,38 @@ get_tab_bar_item (struct frame *f, int x, int y, 
struct glyph **glyph,
   return *prop_idx == f->last_tab_bar_item ? 0 : 1;
 }
 
+/* EXPORT:
+
+   Like `get_tab_bar_item'.  However, don't return anything for GLYPH,
+   HPOS, or VPOS, and treat X and Y as relative to F itself, as
+   opposed to its tab bar window.  */
+
+int
+get_tab_bar_item_kbd (struct frame *f, int x, int y, int *prop_idx,
+                     bool *close_p)
+{
+  struct window *w;
+  int area, vpos, hpos;
+  struct glyph *glyph;
+
+  w = XWINDOW (f->tab_bar_window);
+
+  /* Convert X and Y to window coordinates.  */
+  frame_to_window_pixel_xy (w, &x, &y);
+
+  /* Find the glyph under X/Y.  */
+  glyph = x_y_to_hpos_vpos (w, x, y, &hpos, &vpos, 0,
+                           0, &area);
+  if (glyph == NULL)
+    return -1;
+
+  /* Get the start of this tab-bar item's properties in
+     f->tab_bar_items.  */
+  if (!tab_bar_item_info (f, glyph, prop_idx, close_p))
+    return -1;
+
+  return *prop_idx == f->last_tab_bar_item ? 0 : 1;
+}
 
 /* EXPORT:
    Handle mouse button event on the tab-bar of frame F, at
@@ -15011,7 +15054,10 @@ update_tool_bar (struct frame *f, bool save_match_data)
 
 /* Set F->desired_tool_bar_string to a Lisp string representing frame
    F's desired tool-bar contents.  F->tool_bar_items must have
-   been set up previously by calling prepare_menu_bars.  */
+   been set up previously by calling prepare_menu_bars.
+
+   Also set F->tool_bar_wraps_p to whether or not the tool bar
+   contains explicit line breaking items.  */
 
 static void
 build_desired_tool_bar_string (struct frame *f)
@@ -15033,9 +15079,11 @@ build_desired_tool_bar_string (struct frame *f)
   size_needed = f->n_tool_bar_items;
 
   /* Reuse f->desired_tool_bar_string, if possible.  */
+
   if (size < size_needed || NILP (f->desired_tool_bar_string))
-    fset_desired_tool_bar_string
-      (f, Fmake_string (make_fixnum (size_needed), make_fixnum (' '), Qnil));
+    /* Don't initialize the contents of this string yet, as they will
+       be set within the loop below.  */
+    fset_desired_tool_bar_string (f, make_uninit_string (size_needed));
   else
     {
       AUTO_LIST4 (props, Qdisplay, Qnil, Qmenu_item, Qnil);
@@ -15043,6 +15091,8 @@ build_desired_tool_bar_string (struct frame *f)
                               props, f->desired_tool_bar_string);
     }
 
+  f->tool_bar_wraps_p = false;
+
   /* Put a `display' property on the string for the images to display,
      put a `menu_item' property on tool-bar items with a value that
      is the index of the item in F's tool-bar item vector.  */
@@ -15055,6 +15105,21 @@ build_desired_tool_bar_string (struct frame *f)
       bool selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
       int hmargin, vmargin, relief, idx, end;
 
+      if (!NILP (PROP (TOOL_BAR_ITEM_WRAP)))
+       {
+         /* This is a line wrap.  Instead of building a tool bar
+            item, display a new line character instead.  */
+         SSET (f->desired_tool_bar_string, i, '\n');
+
+         /* Set F->tool_bar_wraps_p.  This tells redisplay_tool_bar
+            to allow individual rows to be different heights.  */
+         f->tool_bar_wraps_p = true;
+         continue;
+       }
+
+      /* Replace this with a space character.  */
+      SSET (f->desired_tool_bar_string, i, ' ');
+
       /* If image is a vector, choose the image according to the
         button state.  */
       image = PROP (TOOL_BAR_ITEM_IMAGES);
@@ -15166,6 +15231,16 @@ build_desired_tool_bar_string (struct frame *f)
                            props, f->desired_tool_bar_string);
 #undef PROP
     }
+
+  /* Now replace each character between i and the end of the tool bar
+     string with spaces, to prevent stray newlines from accumulating
+     when the number of tool bar items decreases.  `size' is 0 if the
+     tool bar string is new, but in that case the string will have
+     been completely initialized anyway.  */
+
+  for (; i < size; ++i)
+    /* Replace this with a space character.  */
+    SSET (f->desired_tool_bar_string, i, ' ');
 }
 
 
@@ -15179,7 +15254,10 @@ build_desired_tool_bar_string (struct frame *f)
    If HEIGHT is -1, we are counting needed tool-bar lines, so don't
    count a final empty row in case the tool-bar width exactly matches
    the window width.
-*/
+
+   HEIGHT may also be -1 if there is an explicit line wrapping item
+   inside the tool bar; in that case, allow individual rows of the
+   tool bar to differ in height.  */
 
 static void
 display_tool_bar_line (struct it *it, int height)
@@ -15243,8 +15321,18 @@ display_tool_bar_line (struct it *it, int height)
          ++i;
        }
 
-      /* Stop at line end.  */
+      /* Stop at the end of the iterator, and move to the next line
+         upon a '\n' appearing in the tool bar string.  Tool bar
+         strings may contain multiple new line characters when
+         explicit wrap items are encountered.  */
+
       if (ITERATOR_AT_END_OF_LINE_P (it))
+       {
+         reseat_at_next_visible_line_start (it, false);
+         break;
+       }
+
+      if (ITERATOR_AT_END_P (it))
        break;
 
       set_iterator_to_next (it, true);
@@ -15271,7 +15359,8 @@ display_tool_bar_line (struct it *it, int height)
     last->left_box_line_p = true;
 
   /* Make line the desired height and center it vertically.  */
-  if ((height -= it->max_ascent + it->max_descent) > 0)
+  if (height != -1
+      && (height -= it->max_ascent + it->max_descent) > 0)
     {
       /* Don't add more than one line height.  */
       height %= FRAME_LINE_HEIGHT (it->f);
@@ -15305,6 +15394,7 @@ display_tool_bar_line (struct it *it, int height)
 /* Value is the number of pixels needed to make all tool-bar items of
    frame F visible.  The actual number of glyph rows needed is
    returned in *N_ROWS if non-NULL.  */
+
 static int
 tool_bar_height (struct frame *f, int *n_rows, bool pixelwise)
 {
@@ -15382,7 +15472,9 @@ redisplay_tool_bar (struct frame *f)
   struct window *w;
   struct it it;
   struct glyph_row *row;
+  bool change_height_p;
 
+  change_height_p = false;
   f->tool_bar_redisplayed = true;
 
   /* If frame hasn't a tool-bar window or if it is zero-height, don't
@@ -15435,6 +15527,15 @@ redisplay_tool_bar (struct frame *f)
          /* Always do that now.  */
          clear_glyph_matrix (w->desired_matrix);
          f->fonts_changed = true;
+
+         /* Kludge (this applies to the X Windows version as well as
+            Android): when the tool bar size changes,
+            adjust_window_size (presumably called by
+            change_tool_bar_height_hook) does not call through to
+            resize_frame_windows.  Pending further investigation,
+            just call it here as well.  */
+         resize_frame_windows (f, FRAME_INNER_HEIGHT (f), false);
+
          return true;
        }
     }
@@ -15457,18 +15558,39 @@ redisplay_tool_bar (struct frame *f)
        border = 0;
 
       rows = f->n_tool_bar_rows;
-      height = max (1, (it.last_visible_y - border) / rows);
-      extra = it.last_visible_y - border - height * rows;
 
-      while (it.current_y < it.last_visible_y)
+      if (f->tool_bar_wraps_p)
        {
-         int h = 0;
-         if (extra > 0 && rows-- > 0)
+         /* If the tool bar contains explicit line wrapping items,
+            don't force each row to have a fixed height.  */
+
+         while (!ITERATOR_AT_END_P (&it))
+           display_tool_bar_line (&it, -1);
+
+         /* Because changes to individual tool bar items may now
+            change the height of the tool bar, adjust the height of
+            the tool bar window if it is different from the tool bar
+            height in any way.  */
+
+         if (it.current_y != it.last_visible_y)
+           change_height_p = true;
+       }
+      else
+       {
+         height = max (1, (it.last_visible_y - border) / rows);
+         extra = it.last_visible_y - border - height * rows;
+
+         while (it.current_y < it.last_visible_y)
            {
-             h = (extra + rows - 1) / rows;
-             extra -= h;
+             int h = 0;
+             if (extra > 0 && rows-- > 0)
+               {
+                 h = (extra + rows - 1) / rows;
+                 extra -= h;
+               }
+         
+             display_tool_bar_line (&it, height + h);
            }
-         display_tool_bar_line (&it, height + h);
        }
     }
   else
@@ -15484,8 +15606,6 @@ redisplay_tool_bar (struct frame *f)
 
   if (!NILP (Vauto_resize_tool_bars))
     {
-      bool change_height_p = false;
-
       /* If we couldn't display everything, change the tool-bar's
         height if there is room for more.  */
       if (IT_STRING_CHARPOS (it) < it.end_charpos)
@@ -16623,7 +16743,7 @@ redisplay_internal (void)
         display area, displaying a different frame means redisplay
         the whole thing.  */
       SET_FRAME_GARBAGED (sf);
-#ifndef DOS_NT
+#if !defined DOS_NT && !defined HAVE_ANDROID
       set_tty_color_mode (FRAME_TTY (sf), sf);
 #endif
       FRAME_TTY (sf)->previous_frame = sf;
@@ -17464,6 +17584,9 @@ static void
 mark_window_display_accurate_1 (struct window *w, bool accurate_p)
 {
   struct buffer *b = XBUFFER (w->contents);
+#ifdef HAVE_TEXT_CONVERSION
+  ptrdiff_t prev_point, prev_mark;
+#endif /* HAVE_TEXT_CONVERSION */
 
   w->last_modified = accurate_p ? BUF_MODIFF (b) : 0;
   w->last_overlay_modified = accurate_p ? BUF_OVERLAY_MODIFF (b) : 0;
@@ -17493,11 +17616,51 @@ mark_window_display_accurate_1 (struct window *w, 
bool accurate_p)
       w->last_cursor_vpos = w->cursor.vpos;
       w->last_cursor_off_p = w->cursor_off_p;
 
+#ifdef HAVE_TEXT_CONVERSION
+      prev_point = w->last_point;
+      prev_mark = w->last_mark;
+#endif /* HAVE_TEXT_CONVERSION */
+
       if (w == XWINDOW (selected_window))
        w->last_point = BUF_PT (b);
       else
        w->last_point = marker_position (w->pointm);
 
+      /* w->last_mark is recorded for text conversion purposes.
+         Input methods aren't interested in the value of the mark
+         if it is inactive, so set it to -1 if it's not.  */
+
+      if (XMARKER (BVAR (b, mark))->buffer == b
+         && !NILP (BVAR (b, mark_active)))
+       w->last_mark = marker_position (BVAR (b, mark));
+      else
+       w->last_mark = -1;
+
+#ifdef HAVE_TEXT_CONVERSION
+      /* See the description of this field in struct window.  */
+      w->ephemeral_last_point = w->last_point;
+
+      /* Point motion is only propagated to the input method for use
+        in text conversion during a redisplay.  While this can lead
+        to inconsistencies when point has moved but the change has
+        not yet been displayed, it leads to better results most of
+        the time, as point often changes within calls to
+        `save-excursion', and the only way to detect such calls is to
+        observe that the next redisplay never ends with those changes
+        applied.
+
+         Changes to buffer text are immediately propagated to the
+         input method, and the position of point is also updated
+         during such a change, so the consequences are not that
+         severe.  */
+
+      if ((prev_point != w->last_point
+          || prev_mark != w->last_mark)
+         && FRAME_WINDOW_P (WINDOW_XFRAME (w))
+         && w == XWINDOW (WINDOW_XFRAME (w)->selected_window))
+       report_point_change (WINDOW_XFRAME (w), w, b);
+#endif /* HAVE_TEXT_CONVERSION */
+
       struct glyph_row *row;
       /* These conditions should be consistent with CHECK_WINDOW_END.  */
       if (w->window_end_vpos < w->current_matrix->nrows
@@ -26557,7 +26720,7 @@ display_menu_bar (struct window *w)
   init_iterator (&it, w, -1, -1, f->desired_matrix->rows, MENU_FACE_ID);
   it.first_visible_x = 0;
   it.last_visible_x = FRAME_PIXEL_WIDTH (f);
-#elif defined (HAVE_X_WINDOWS) /* X without toolkit.  */
+#elif defined (HAVE_X_WINDOWS) || defined (HAVE_ANDROID)
   struct window *menu_window = NULL;
   struct face *face = FACE_FROM_ID (f, MENU_FACE_ID);
 
@@ -26627,7 +26790,11 @@ display_menu_bar (struct window *w)
   it.glyph_row->truncated_on_left_p = false;
   it.glyph_row->truncated_on_right_p = false;
 
-#if defined (HAVE_X_WINDOWS) && !defined (USE_X_TOOLKIT) && !defined (USE_GTK)
+  /* This will break the moment someone tries to add another window
+     system that uses the no toolkit menu bar.  Oh well.  At least
+     there will be an error, meaning he will correct the ifdef inside
+     which `face' is defined.  */
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
   /* Make a 3D menu bar have a shadow at its right end.  */
   extend_face_to_end_of_line (&it);
   if (face->box != FACE_NO_BOX)
@@ -26668,6 +26835,11 @@ display_menu_bar (struct window *w)
 #endif
 }
 
+/* This code is never used on Android where there are only GUI and
+   initial frames.  */
+
+#ifndef HAVE_ANDROID
+
 /* Deep copy of a glyph row, including the glyphs.  */
 static void
 deep_copy_glyph_row (struct glyph_row *to, struct glyph_row *from)
@@ -26788,6 +26960,9 @@ display_tty_menu_item (const char *item_text, int 
width, int face_id,
   row->full_width_p = saved_width;
   row->reversed_p = saved_reversed;
 }
+
+#endif
+
 
 /***********************************************************************
                              Mode Line
@@ -33728,7 +33903,9 @@ draw_row_with_mouse_face (struct window *w, int 
start_x, struct glyph_row *row,
     }
 #endif
 
+#ifndef HAVE_ANDROID
   tty_draw_row_with_mouse_face (w, row, start_hpos, end_hpos, draw);
+#endif
 }
 
 /* Display the active region described by mouse_face_* according to DRAW.  */
@@ -35219,7 +35396,8 @@ note_mouse_highlight (struct frame *f, int x, int y)
   struct buffer *b;
 
   /* When a menu is active, don't highlight because this looks odd.  */
-#if defined (HAVE_X_WINDOWS) || defined (HAVE_NS) || defined (MSDOS)
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_NS) || defined (MSDOS) \
+  || defined (HAVE_ANDROID)
   if (popup_activated ())
     return;
 #endif
@@ -36400,14 +36578,10 @@ expose_frame (struct frame *f, int x, int y, int w, 
int h)
       |= expose_window (XWINDOW (f->tool_bar_window), &r);
 #endif
 
-#ifdef HAVE_X_WINDOWS
-#ifndef MSDOS
-#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
   if (WINDOWP (f->menu_bar_window))
     mouse_face_overwritten_p
       |= expose_window (XWINDOW (f->menu_bar_window), &r);
-#endif /* not USE_X_TOOLKIT and not USE_GTK */
-#endif
 #endif
 
   /* Some window managers support a focus-follows-mouse style with
@@ -36491,6 +36665,55 @@ gui_intersect_rectangles (const Emacs_Rectangle *r1, 
const Emacs_Rectangle *r2,
   return intersection_p;
 }
 
+/* EXPORT:
+   Determine the union of the rectangles A and B.  Return the smallest
+   rectangle encompassing both the bounds of A and B in *RESULT.  It
+   is safe for all three arguments to point to each other.  */
+
+void
+gui_union_rectangles (const Emacs_Rectangle *a, const Emacs_Rectangle *b,
+                     Emacs_Rectangle *result)
+{
+  struct gui_box a_box, b_box, result_box;
+
+  /* Handle special cases where one of the rectangles is empty.  */
+
+  if (!a->width || !a->height)
+    {
+      *result = *b;
+      return;
+    }
+  else if (!b->width || !b->height)
+    {
+      *result = *a;
+      return;
+    }
+
+  /* Convert A and B to boxes.  */
+  a_box.x1 = a->x;
+  a_box.y1 = a->y;
+  a_box.x2 = a->x + a->width;
+  a_box.y2 = a->y + a->height;
+
+  b_box.x1 = b->x;
+  b_box.y1 = b->y;
+  b_box.x2 = b->x + b->width;
+  b_box.y2 = b->y + b->height;
+
+  /* Compute the union of the boxes.  */
+  result_box.x1 = min (a_box.x1, b_box.x1);
+  result_box.y1 = min (a_box.y1, b_box.y1);
+  result_box.x2 = max (a_box.x2, b_box.x2);
+  result_box.y2 = max (a_box.y2, b_box.y2);
+
+  /* Convert result_box to an XRectangle and put the result in
+     RESULT.  */
+  result->x = result_box.x1;
+  result->y = result_box.y1;
+  result->width = result_box.x2 - result_box.x1;
+  result->height = result_box.y2 - result_box.y1;
+}
+
 #endif /* HAVE_WINDOW_SYSTEM */
 
 
diff --git a/src/xfaces.c b/src/xfaces.c
index 4c64fb9f377..90d2b68e580 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -254,6 +254,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #ifdef HAVE_HAIKU
 #define GCGraphicsExposures 0
 #endif /* HAVE_HAIKU */
+
+#ifdef HAVE_ANDROID
+#define GCGraphicsExposures 0
+#endif /* HAVE_ANDROID */
 #endif /* HAVE_WINDOW_SYSTEM */
 
 #include "buffer.h"
@@ -607,6 +611,39 @@ x_free_gc (struct frame *f, Emacs_GC *gc)
 }
 #endif  /* HAVE_NS */
 
+#ifdef HAVE_ANDROID
+
+/* Android real GCs.  */
+
+static struct android_gc *
+x_create_gc (struct frame *f, unsigned long value_mask,
+            Emacs_GC *xgcv)
+{
+  struct android_gc_values gcv;
+  unsigned long mask;
+
+  gcv.foreground = xgcv->foreground;
+  gcv.background = xgcv->background;
+
+  mask = 0;
+
+  if (value_mask & GCForeground)
+    mask |= ANDROID_GC_FOREGROUND;
+
+  if (value_mask & GCBackground)
+    mask |= ANDROID_GC_BACKGROUND;
+
+  return android_create_gc (mask, &gcv);
+}
+
+static void
+x_free_gc (struct frame *f, struct android_gc *gc)
+{
+  android_free_gc (gc);
+}
+
+#endif
+
 /***********************************************************************
                           Frames and faces
  ***********************************************************************/
@@ -1575,7 +1612,7 @@ the face font sort order, see 
`face-font-selection-order'.  */)
     {
       Lisp_Object font = AREF (vec, i);
       int point = PIXEL_TO_POINT (XFIXNUM (AREF (font, FONT_SIZE_INDEX)) * 10,
-                                 FRAME_RES_Y (f));
+                                 FRAME_RES (f));
       Lisp_Object spacing = Ffont_get (font, QCspacing);
       Lisp_Object v = CALLN (Fvector,
                             AREF (font, FONT_FAMILY_INDEX),
@@ -2136,7 +2173,7 @@ set_lface_from_font (struct frame *f, Lisp_Object lface,
 
   if (force_p || UNSPECIFIEDP (LFACE_HEIGHT (lface)))
     {
-      int pt = PIXEL_TO_POINT (font->pixel_size * 10, FRAME_RES_Y (f));
+      int pt = PIXEL_TO_POINT (font->pixel_size * 10, FRAME_RES (f));
 
       eassert (pt > 0);
       ASET (lface, LFACE_HEIGHT_INDEX, make_fixnum (pt));
@@ -6951,20 +6988,22 @@ where R,G,B are numbers between 0 and 255 and name is 
an arbitrary string.  */)
       int num;
 
       while (fgets (buf, sizeof (buf), fp) != NULL)
-       if (sscanf (buf, "%d %d %d %n", &red, &green, &blue, &num) == 3)
-         {
+       {
+         if (sscanf (buf, "%d %d %d %n", &red, &green, &blue, &num) == 3)
+           {
 #ifdef HAVE_NTGUI
-           int color = RGB (red, green, blue);
+             int color = RGB (red, green, blue);
 #else
-           int color = (red << 16) | (green << 8) | blue;
+             int color = (red << 16) | (green << 8) | blue;
 #endif
-           char *name = buf + num;
-           ptrdiff_t len = strlen (name);
-           len -= 0 < len && name[len - 1] == '\n';
-           cmap = Fcons (Fcons (make_string (name, len), make_fixnum (color)),
-                         cmap);
-         }
-      fclose (fp);
+             char *name = buf + num;
+             ptrdiff_t len = strlen (name);
+             len -= 0 < len && name[len - 1] == '\n';
+             cmap = Fcons (Fcons (make_string (name, len), make_fixnum 
(color)),
+                           cmap);
+           }
+       }
+      emacs_fclose (fp);
     }
   unblock_input ();
   return cmap;
diff --git a/src/xfns.c b/src/xfns.c
index 222e24f1326..aea2f4b880e 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -3883,7 +3883,7 @@ xic_string_conversion_callback (XIC ic, XPointer 
client_data,
     request.operation = TEXTCONV_RETRIEVAL;
 
   /* Now perform the string conversion.  */
-  rc = textconv_query (f, &request);
+  rc = textconv_query (f, &request, 0);
 
   if (rc)
     {
@@ -5729,6 +5729,8 @@ that operating systems cannot be developed and 
distributed noncommercially.)
 The optional argument TERMINAL specifies which display to ask about.
 
 For GNU and Unix systems, this queries the X server software.
+For Android systems, value is the manufacturer who developed the Android
+system that is being used.
 For MS Windows and Nextstep the result is hard-coded.
 
 TERMINAL should be a terminal object, a frame or a display name (a string).
@@ -5752,7 +5754,8 @@ Protocol used on TERMINAL and the 3rd number is the 
distributor-specific
 release number.  For MS Windows, the 3 numbers report the OS major and
 minor version and build number.  For Nextstep, the first 2 numbers are
 hard-coded and the 3rd represents the OS version.  For Haiku, all 3
-numbers are hard-coded.
+numbers are hard-coded.  For Android, the first number represents the
+Android API level, and the next two numbers are all zero.
 
 See also the function `x-server-vendor'.
 
diff --git a/src/xterm.c b/src/xterm.c
index 61b9d972e57..f454733c659 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -20187,6 +20187,24 @@ handle_one_xevent (struct x_display_info *dpyinfo,
            }
 #endif
 
+         /* See if keysym should make Emacs quit.  */
+
+         if (keysym == dpyinfo->quit_keysym
+             && (xkey.time - dpyinfo->quit_keysym_time
+                 <= 350))
+           {
+             Vquit_flag = Qt;
+             goto done_keysym;
+           }
+
+         if (keysym == dpyinfo->quit_keysym)
+           {
+             /* Otherwise, set the last time that keysym was
+                pressed.  */
+             dpyinfo->quit_keysym_time = xkey.time;
+             goto done_keysym;
+           }
+
           /* If not using XIM/XIC, and a compose sequence is in progress,
              we break here.  Otherwise, chars_matched is always 0.  */
           if (compose_status.chars_matched > 0 && nbytes == 0)
@@ -23950,6 +23968,24 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                    }
 #endif
 
+                 /* See if keysym should make Emacs quit.  */
+
+                 if (keysym == dpyinfo->quit_keysym
+                     && (xev->time - dpyinfo->quit_keysym_time
+                         <= 350))
+                   {
+                     Vquit_flag = Qt;
+                     goto xi_done_keysym;
+                   }
+
+                 if (keysym == dpyinfo->quit_keysym)
+                   {
+                     /* Otherwise, set the last time that keysym was
+                        pressed.  */
+                     dpyinfo->quit_keysym_time = xev->time;
+                     goto xi_done_keysym;
+                   }
+
                  /* First deal with keysyms which have defined
                     translations to characters.  */
                  if (keysym >= 32 && keysym < 128)
@@ -30066,6 +30102,7 @@ x_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
   struct terminal *terminal;
   struct x_display_info *dpyinfo;
   XrmDatabase xrdb;
+  Lisp_Object tem, quit_keysym;
 #ifdef USE_XCB
   xcb_connection_t *xcb_conn;
 #endif
@@ -30076,7 +30113,7 @@ x_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
   GdkScreen *gscr;
 #endif
 #ifdef HAVE_XFIXES
-  Lisp_Object tem, lisp_name;
+  Lisp_Object lisp_name;
   int num_fast_selections;
   Atom selection_name;
 #ifdef USE_XCB
@@ -30353,6 +30390,28 @@ x_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
     terminal->kboard->reference_count++;
   }
 
+  /* Now look through Vx_quit_keysym for the quit keysym associated
+     with this display.  */
+  tem = Vx_quit_keysym;
+  FOR_EACH_TAIL_SAFE (tem)
+    {
+      quit_keysym = XCAR (tem);
+
+      /* Check if its car is a string and its cdr a valid keysym.
+        Skip if it is not.  */
+
+      if (!CONSP (quit_keysym) || !FIXNUMP (XCDR (quit_keysym))
+         || !STRINGP (XCAR (quit_keysym)))
+       continue;
+
+      /* Check if this is the keysym to be used.  */
+
+      if (strcmp (SSDATA (XCAR (quit_keysym)), ServerVendor (dpy)))
+       continue;
+
+      dpyinfo->quit_keysym = XFIXNUM (XCDR (quit_keysym));
+    }
+
   /* Put this display on the chain.  */
   dpyinfo->next = x_display_list;
   x_display_list = dpyinfo;
@@ -31780,7 +31839,7 @@ init_xterm (void)
 #endif
 
 #ifdef HAVE_X_I18N
-  register_texconv_interface (&text_conversion_interface);
+  register_textconv_interface (&text_conversion_interface);
 #endif
 }
 
@@ -32114,7 +32173,9 @@ adjusted if the default value does not work for 
whatever reason.  */);
 A value of nil means Emacs doesn't use toolkit scroll bars.
 With the X Window system, the value is a symbol describing the
 X toolkit.  Possible values are: gtk, motif, xaw, or xaw3d.
-With MS Windows, Haiku windowing or Nextstep, the value is t.  */);
+With MS Windows, Haiku windowing or Nextstep, the value is t.
+With Android, the value is nil, but that is because Emacs on
+Android does not support scroll bars at all.  */);
 #ifdef USE_TOOLKIT_SCROLL_BARS
 #ifdef USE_MOTIF
   Vx_toolkit_scroll_bars = intern_c_string ("motif");
@@ -32463,4 +32524,23 @@ frame placement via frame parameters, 
`set-frame-position', and
 `set-frame-size', along with the actual state of a frame after
 `x_make_frame_invisible'.  */);
   Vx_lax_frame_positioning = Qnil;
+
+  DEFVAR_LISP ("x-quit-keysym", Vx_quit_keysym,
+    doc: /* Keysyms which will cause Emacs to quit if rapidly pressed twice.
+
+This is used to support quitting on devices that do not have any kind
+of physical keyboard, or where the physical keyboard is incapable of
+entering `C-g'.  It defaults to `XF86XK_AudioLowerVolume' on XFree86
+and X.Org servers, and is unset.
+
+The value is an alist associating between strings, describing X server
+vendor names, and a single number describing the keysym to use.  The
+keysym to use for each display connection is determined upon
+connection setup, and does not reflect further changes to this
+variable.  */);
+  Vx_quit_keysym
+    = list2 (Fcons (build_string ("The X.Org Foundation"),
+                   make_int (269025041)),
+            Fcons (build_string ("The XFree86 Project, Inc."),
+                   make_int (269025041)));
 }
diff --git a/src/xterm.h b/src/xterm.h
index c7db54e2bcf..cb477645bfa 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -976,6 +976,13 @@ struct x_display_info
      server_time_monotonic_p will be true).  */
   int_fast64_t server_time_offset;
 #endif
+
+  /* Keysym that will cause Emacs to quit if pressed twice within 150
+     ms.  */
+  KeySym quit_keysym;
+
+  /* The last time that keysym was pressed.  */
+  Time quit_keysym_time;
 };
 
 #ifdef HAVE_XINPUT2
diff --git a/test/lisp/align-resources/align-post.c 
b/test/lisp/align-resources/align-post.c
deleted file mode 100644
index 157e1d6242a..00000000000
--- a/test/lisp/align-resources/align-post.c
+++ /dev/null
@@ -1,3 +0,0 @@
-int
-main (int       argc,
-      char     *argv[]);
diff --git a/test/lisp/align-resources/align-post.java 
b/test/lisp/align-resources/align-post.java
deleted file mode 100644
index e0ea8e727f1..00000000000
--- a/test/lisp/align-resources/align-post.java
+++ /dev/null
@@ -1,9 +0,0 @@
-class X
-{
-    String     field1;
-    String[]   field2;
-    int                field3;
-    int[]      field4;
-    X          field5;
-    X[]                field6;
-}
diff --git a/test/lisp/align-resources/align-pre.c 
b/test/lisp/align-resources/align-pre.c
deleted file mode 100644
index b1774181a40..00000000000
--- a/test/lisp/align-resources/align-pre.c
+++ /dev/null
@@ -1,3 +0,0 @@
-int
-main (int argc,
-      char *argv[]);
diff --git a/test/lisp/align-resources/align-pre.java 
b/test/lisp/align-resources/align-pre.java
deleted file mode 100644
index fe7a87a9393..00000000000
--- a/test/lisp/align-resources/align-pre.java
+++ /dev/null
@@ -1,9 +0,0 @@
-class X
-{
-    String     field1;
-    String[] field2;
-    int                field3;
-    int[] field4;
-    X          field5;
-    X[] field6;
-}
diff --git a/test/lisp/align-resources/align-regexp.erts 
b/test/lisp/align-resources/align-regexp.erts
new file mode 100644
index 00000000000..fbbd6d6bd33
--- /dev/null
+++ b/test/lisp/align-resources/align-regexp.erts
@@ -0,0 +1,13 @@
+Name: align function declaration
+
+=-=
+Fred (123) 456-7890
+Alice (123) 456-7890
+Mary-Anne (123) 456-7890
+Joe (123) 456-7890
+=-=
+Fred      (123) 456-7890
+Alice     (123) 456-7890
+Mary-Anne (123) 456-7890
+Joe       (123) 456-7890
+=-=-=
diff --git a/test/lisp/align-resources/c-mode.erts 
b/test/lisp/align-resources/c-mode.erts
new file mode 100644
index 00000000000..a28c2bdbed0
--- /dev/null
+++ b/test/lisp/align-resources/c-mode.erts
@@ -0,0 +1,23 @@
+Name: align function declaration
+
+=-=
+int
+main (int argc,
+      char *argv[]);
+=-=
+int
+main (int       argc,
+      char     *argv[]);
+=-=-=
+
+Name: example from Commentary
+
+=-=
+    int a = 1;
+    short foo = 2;
+    double blah = 4;
+=-=
+    int                a    = 1;
+    short      foo  = 2;
+    double     blah = 4;
+=-=-=
diff --git a/test/lisp/align-resources/conf-toml-mode.erts 
b/test/lisp/align-resources/conf-toml-mode.erts
new file mode 100644
index 00000000000..d1fcbd58708
--- /dev/null
+++ b/test/lisp/align-resources/conf-toml-mode.erts
@@ -0,0 +1,45 @@
+Name: align key-value pairs
+
+=-=
+[foo]
+foo1=10
+foo22=20
+
+[bar]
+bar333="example.org"
+bar4444 = "zzz"
+=-=
+[foo]
+foo1  = 10
+foo22 = 20
+
+[bar]
+bar333  = "example.org"
+bar4444 = "zzz"
+=-=-=
+
+Name: align list values
+
+=-=
+[foo]
+a = 1
+some_list = [
+    true,
+    false,
+]
+some_other_list = [
+    1,
+    2,
+]
+=-=
+[foo]
+a               = 1
+some_list       = [
+    true,
+    false,
+]
+some_other_list = [
+    1,
+    2,
+]
+=-=-=
diff --git a/test/lisp/align-resources/css-mode.erts 
b/test/lisp/align-resources/css-mode.erts
new file mode 100644
index 00000000000..e4455601083
--- /dev/null
+++ b/test/lisp/align-resources/css-mode.erts
@@ -0,0 +1,23 @@
+Name: align attributes
+
+=-=
+div {
+  border: 1px solid black;
+  padding: 25px 50px 75px 100px;
+  background-color: lightblue;
+}
+p.center {
+  text-align: center;
+  color: red;
+}
+=-=
+div {
+  border:           1px solid black;
+  padding:          25px 50px 75px 100px;
+  background-color: lightblue;
+}
+p.center {
+  text-align: center;
+  color:      red;
+}
+=-=-=
diff --git a/test/lisp/align-resources/java-mode.erts 
b/test/lisp/align-resources/java-mode.erts
new file mode 100644
index 00000000000..693a4123121
--- /dev/null
+++ b/test/lisp/align-resources/java-mode.erts
@@ -0,0 +1,23 @@
+Name: align class fields
+
+=-=
+class X
+{
+    String     field1;
+    String[] field2;
+    int                field3;
+    int[] field4;
+    X          field5;
+    X[] field6;
+}
+=-=
+class X
+{
+    String     field1;
+    String[]   field2;
+    int                field3;
+    int[]      field4;
+    X          field5;
+    X[]                field6;
+}
+=-=-=
diff --git a/test/lisp/align-resources/latex-mode.erts 
b/test/lisp/align-resources/latex-mode.erts
new file mode 100644
index 00000000000..cdc93e4a925
--- /dev/null
+++ b/test/lisp/align-resources/latex-mode.erts
@@ -0,0 +1,29 @@
+Name: tex-record-separator and basic-line-continuation
+
+=-=
+\documentclass{}
+
+\begin{document}
+
+\begin{tabular}{l|l}
+  \textit{Player name}  &\textit{Career home runs}  \\
+  \hline
+  Hank Aaron  &755 \\
+  Babe Ruth &714
+\end{tabular}
+
+\end{document}
+=-=
+\documentclass{}
+
+\begin{document}
+
+\begin{tabular}{l|l}
+  \textit{Player name} & \textit{Career home runs} \\
+  \hline
+  Hank Aaron           & 755                       \\
+  Babe Ruth            & 714
+\end{tabular}
+
+\end{document}
+=-=-=
diff --git a/test/lisp/align-resources/python-mode.erts 
b/test/lisp/align-resources/python-mode.erts
new file mode 100644
index 00000000000..1ce50b32dba
--- /dev/null
+++ b/test/lisp/align-resources/python-mode.erts
@@ -0,0 +1,29 @@
+Name: align assignments
+
+=-=
+foo = "bar"
+x = 1
+zzzzz = True
+y        = None
+=-=
+foo   = "bar"
+x     = 1
+zzzzz = True
+y     = None
+=-=-=
+
+Name: python-chain-logic and basic-line-continuation
+
+=-=
+if foo or\
+   b and \
+   bcxxx and \
+   c:
+    pass
+=-=
+if foo   or  \
+   b     and \
+   bcxxx and \
+   c:
+    pass
+=-=-=
diff --git a/test/lisp/align-tests.el b/test/lisp/align-tests.el
index 62ef9cf27fa..a4d9303827f 100644
--- a/test/lisp/align-tests.el
+++ b/test/lisp/align-tests.el
@@ -25,22 +25,47 @@
 (require 'ert-x)
 (require 'align)
 
-(defun test-align-compare (file function)
-  (should (equal
-           (with-temp-buffer
-             (insert-file-contents (ert-resource-file (format file "pre")))
-             (funcall function)
-             (align (point-min) (point-max))
-             (buffer-substring-no-properties (point-min) (point-max)))
-           (with-temp-buffer
-             (insert-file-contents (ert-resource-file (format file "post")))
-             (buffer-string)))))
+;;;; align
 
-(ert-deftest align-java ()
-  (test-align-compare "align-%s.java" #'java-mode))
+(defun test-align-transform-fun (function)
+  (lambda ()
+    (funcall function)
+    (align (point-min) (point-max))))
 
 (ert-deftest align-c ()
-  (test-align-compare "align-%s.c" #'c-mode))
+  (ert-test-erts-file (ert-resource-file "c-mode.erts")
+                      (test-align-transform-fun #'c-mode)))
+
+(ert-deftest align-css ()
+  (let ((indent-tabs-mode nil))
+    (ert-test-erts-file (ert-resource-file "css-mode.erts")
+                        (test-align-transform-fun #'css-mode))))
+
+(ert-deftest align-java ()
+  (ert-test-erts-file (ert-resource-file "java-mode.erts")
+                      (test-align-transform-fun #'java-mode)))
+
+(ert-deftest align-latex ()
+  (ert-test-erts-file (ert-resource-file "latex-mode.erts")
+                      (test-align-transform-fun #'latex-mode)))
+
+(ert-deftest align-python ()
+  (ert-test-erts-file (ert-resource-file "python-mode.erts")
+                      (test-align-transform-fun #'python-mode)))
+
+(ert-deftest align-toml ()
+  (let ((indent-tabs-mode nil))
+    (ert-test-erts-file (ert-resource-file "conf-toml-mode.erts")
+                        (test-align-transform-fun #'conf-toml-mode))))
+
+;;;; align-regexp
+
+(ert-deftest align-regexp ()
+  (let ((indent-tabs-mode nil))
+    (ert-test-erts-file (ert-resource-file "align-regexp.erts")
+                        (lambda ()
+                          (align-regexp (point-min) (point-max)
+                                        "\\(\\s-*\\)(")))))
 
 (provide 'align-tests)
 
diff --git a/test/lisp/emacs-lisp/bytecomp-tests.el 
b/test/lisp/emacs-lisp/bytecomp-tests.el
index 593fd117685..246ffff532f 100644
--- a/test/lisp/emacs-lisp/bytecomp-tests.el
+++ b/test/lisp/emacs-lisp/bytecomp-tests.el
@@ -2001,6 +2001,40 @@ EXPECTED-POINT BINDINGS (MODES \\='\\='(ruby-mode 
js-mode python-mode)) \
                                      (backtrace-frame-args frame))
                                call))))))))))
 
+(ert-deftest bytecomp--eq-symbols-with-pos-enabled ()
+  ;; Verify that we don't optimise away a binding of
+  ;; `symbols-with-pos-enabled' around an application of `eq' (bug#65017).
+  (let* ((sym-with-pos1 (read-positioning-symbols "sym"))
+         (sym-with-pos2 (read-positioning-symbols " sym"))  ; <- space!
+         (without-pos-eq (lambda (a b)
+                           (let ((symbols-with-pos-enabled nil))
+                             (eq a b))))
+         (without-pos-eq-compiled (byte-compile without-pos-eq))
+         (with-pos-eq (lambda (a b)
+                        (let ((symbols-with-pos-enabled t))
+                          (eq a b))))
+         (with-pos-eq-compiled (byte-compile with-pos-eq)))
+    (dolist (mode '(interpreted compiled))
+      (ert-info ((symbol-name mode) :prefix "mode: ")
+        (ert-info ("disabled" :prefix "symbol-pos: ")
+          (let ((eq-fn (pcase-exhaustive mode
+                         ('interpreted without-pos-eq)
+                         ('compiled    without-pos-eq-compiled))))
+            (should (equal (funcall eq-fn 'sym 'sym) t))
+            (should (equal (funcall eq-fn sym-with-pos1 'sym) nil))
+            (should (equal (funcall eq-fn 'sym sym-with-pos1) nil))
+            (should (equal (funcall eq-fn sym-with-pos1 sym-with-pos1) t))
+            (should (equal (funcall eq-fn sym-with-pos1 sym-with-pos2) nil))))
+        (ert-info ("enabled" :prefix "symbol-pos: ")
+          (let ((eq-fn (pcase-exhaustive mode
+                         ('interpreted with-pos-eq)
+                         ('compiled    with-pos-eq-compiled))))
+            (should (equal (funcall eq-fn 'sym 'sym) t))
+            (should (equal (funcall eq-fn sym-with-pos1 'sym) t))
+            (should (equal (funcall eq-fn 'sym sym-with-pos1) t))
+            (should (equal (funcall eq-fn sym-with-pos1 sym-with-pos1) t))
+            (should (equal (funcall eq-fn sym-with-pos1 sym-with-pos2) 
t))))))))
+
 ;; Local Variables:
 ;; no-byte-compile: t
 ;; End:
diff --git a/test/lisp/emacs-lisp/nadvice-tests.el 
b/test/lisp/emacs-lisp/nadvice-tests.el
index f6bd5733ba3..7dfa936214a 100644
--- a/test/lisp/emacs-lisp/nadvice-tests.el
+++ b/test/lisp/emacs-lisp/nadvice-tests.el
@@ -65,8 +65,9 @@
   (defun sm-test2 (x) (+ x 4))
   (declare-function sm-test2 nil)
   (should (equal (sm-test2 6) 10))
-  (defadvice sm-test2 (around sm-test activate)
-    ad-do-it (setq ad-return-value (* ad-return-value 5)))
+  (with-suppressed-warnings ((obsolete defadvice))
+    (defadvice sm-test2 (around sm-test activate)
+      ad-do-it (setq ad-return-value (* ad-return-value 5))))
   (should (equal (sm-test2 6) 50))
   (ad-deactivate 'sm-test2)
   (should (equal (sm-test2 6) 10))
@@ -81,8 +82,9 @@
   (should (equal (sm-test2 6) 20))
   (should (equal (null (get 'sm-test2 'defalias-fset-function)) t))
 
-  (defadvice sm-test4 (around wrap-with-toto activate)
-    ad-do-it (setq ad-return-value `(toto ,ad-return-value)))
+  (with-suppressed-warnings ((obsolete defadvice))
+    (defadvice sm-test4 (around wrap-with-toto activate)
+      ad-do-it (setq ad-return-value `(toto ,ad-return-value))))
   (defmacro sm-test4 (x) `(call-test4 ,x))
   (should (equal (macroexpand '(sm-test4 56)) '(toto (call-test4 56))))
   (defmacro sm-test4 (x) `(call-testq ,x))
@@ -90,8 +92,9 @@
 
   ;; This used to signal an error (bug#12858).
   (autoload 'sm-test6 "foo")
-  (defadvice sm-test6 (around test activate)
-    ad-do-it))
+  (with-suppressed-warnings ((obsolete defadvice))
+    (defadvice sm-test6 (around test activate)
+      ad-do-it)))
 
 (ert-deftest advice-tests-combination ()
   "Combining old style and new style advices."
@@ -100,8 +103,9 @@
   (should (equal (sm-test5 6) 10))
   (advice-add 'sm-test5 :around (lambda (f y) (* (funcall f y) 5)))
   (should (equal (sm-test5 6) 50))
-  (defadvice sm-test5 (around test activate)
-    ad-do-it (setq ad-return-value (+ ad-return-value 0.1)))
+  (with-suppressed-warnings ((obsolete defadvice))
+    (defadvice sm-test5 (around test activate)
+      ad-do-it (setq ad-return-value (+ ad-return-value 0.1))))
   (should (equal (sm-test5 5) 45.1))
   (ad-deactivate 'sm-test5)
   (should (equal (sm-test5 6) 50))
@@ -174,18 +178,20 @@ function being an around advice."
 (ert-deftest advice-test-interactive ()
   "Check handling of interactive spec."
   (defun sm-test8 (a) (interactive "p") a)
-  (defadvice sm-test8 (before adv1 activate) nil)
-  (defadvice sm-test8 (before adv2 activate) (interactive "P") nil)
+  (with-suppressed-warnings ((obsolete defadvice))
+    (defadvice sm-test8 (before adv1 activate) nil)
+    (defadvice sm-test8 (before adv2 activate) (interactive "P") nil))
   (should (equal (interactive-form 'sm-test8) '(interactive "P"))))
 
 (ert-deftest advice-test-preactivate ()
   (should (equal (null (get 'sm-test9 'defalias-fset-function)) t))
   (defun sm-test9 (a) (interactive "p") a)
   (should (equal (null (get 'sm-test9 'defalias-fset-function)) t))
-  (defadvice sm-test9 (before adv1 pre act protect compile) nil)
-  (should (equal (null (get 'sm-test9 'defalias-fset-function)) nil))
-  (defadvice sm-test9 (before adv2 pre act protect compile)
-    (interactive "P") nil)
+  (with-suppressed-warnings ((obsolete defadvice))
+    (defadvice sm-test9 (before adv1 pre act protect compile) nil)
+    (should (equal (null (get 'sm-test9 'defalias-fset-function)) nil))
+    (defadvice sm-test9 (before adv2 pre act protect compile)
+      (interactive "P") nil))
   (should (equal (interactive-form 'sm-test9) '(interactive "P"))))
 
 (ert-deftest advice-test-multiples ()
diff --git a/test/lisp/emacs-lisp/rx-tests.el b/test/lisp/emacs-lisp/rx-tests.el
index 4928d5adf9d..ae83f28d9c4 100644
--- a/test/lisp/emacs-lisp/rx-tests.el
+++ b/test/lisp/emacs-lisp/rx-tests.el
@@ -148,7 +148,7 @@
   (should (equal (rx (any "-]^" ascii) (not (any "-]^" ascii)))
                  "[]^[:ascii:]-][^]^[:ascii:]-]"))
   (should (equal (rx (any "^" lower upper) (not (any "^" lower upper)))
-                 "[[:lower:]^[:upper:]][^^[:lower:][:upper:]]"))
+                 "[[:lower:][:upper:]^][^^[:lower:][:upper:]]"))
   (should (equal (rx (any "-" lower upper) (not (any "-" lower upper)))
                  "[[:lower:][:upper:]-][^[:lower:][:upper:]-]"))
   (should (equal (rx (any "]" lower upper) (not (any "]" lower upper)))
@@ -284,7 +284,7 @@
                  "^\\`\\'\\`\\'\\`\\'\\`\\'$"))
   (should (equal (rx point word-start word-end bow eow symbol-start symbol-end
                      word-boundary not-word-boundary not-wordchar)
-                 "\\=\\<\\>\\<\\>\\_<\\_>\\b\\B\\W"))
+                 "\\=\\<\\>\\<\\>\\_<\\_>\\b\\B[^[:word:]]"))
   (should (equal (rx digit numeric num control cntrl)
                  "[[:digit:]][[:digit:]][[:digit:]][[:cntrl:]][[:cntrl:]]"))
   (should (equal (rx hex-digit hex xdigit blank)
@@ -306,7 +306,7 @@
   (should (equal (rx (syntax whitespace) (syntax punctuation)
                      (syntax word) (syntax symbol)
                      (syntax open-parenthesis) (syntax close-parenthesis))
-                 "\\s-\\s.\\sw\\s_\\s(\\s)"))
+                 "\\s-\\s.\\w\\s_\\s(\\s)"))
   (should (equal (rx (syntax string-quote) (syntax paired-delimiter)
                      (syntax escape) (syntax character-quote)
                      (syntax comment-start) (syntax comment-end)
@@ -354,8 +354,9 @@
                  "\\B"))
   (should (equal (rx (not ascii) (not lower-case) (not wordchar))
                  "[^[:ascii:]][^[:lower:]][^[:word:]]"))
-  (should (equal (rx (not (syntax punctuation)) (not (syntax escape)))
-                 "\\S.\\S\\"))
+  (should (equal (rx (not (syntax punctuation)) (not (syntax escape))
+                     (not (syntax word)))
+                 "\\S.\\S\\\\W"))
   (should (equal (rx (not (category tone-mark)) (not (category lao)))
                  "\\C4\\Co"))
   (should (equal (rx (not (not ascii)) (not (not (not (any "a-z")))))
@@ -610,6 +611,57 @@
              (rx-submatch-n '(group-n 3 (+ nonl) eol)))
            "\\(?3:.+$\\)")))
 
+;;; unit tests for internal functions
+
+(ert-deftest rx--complement-intervals ()
+  (should (equal (rx--complement-intervals '())
+                 '((0 . #x3fffff))))
+  (should (equal (rx--complement-intervals '((10 . 20) (30 . 40)))
+                 '((0 . 9) (21 . 29) (41 . #x3fffff))))
+  (should (equal (rx--complement-intervals '((0 . #x3fffff)))
+                 '()))
+  (should (equal (rx--complement-intervals
+                  '((0 . 10) (20 . 20) (30 . #x3fffff)))
+                 '((11 . 19) (21 . 29)))))
+
+(ert-deftest rx--union-intervals ()
+  (should (equal (rx--union-intervals '() '()) '()))
+  (should (equal (rx--union-intervals '() '((10 . 20) (30 . 40)))
+                 '((10 . 20) (30 . 40))))
+  (should (equal (rx--union-intervals '((10 . 20) (30 . 40)) '())
+                 '((10 . 20) (30 . 40))))
+  (should (equal (rx--union-intervals '((5 . 15) (18 . 24) (32 . 40))
+                                      '((10 . 20) (30 . 40) (50 . 60)))
+                 '((5 . 24) (30 . 40) (50 . 60))))
+  (should (equal (rx--union-intervals '((10 . 20) (30 . 40) (50 . 60))
+                                      '((0 . 9) (21 . 29) (41 . 50)))
+                 '((0 . 60))))
+  (should (equal (rx--union-intervals '((10 . 20) (30 . 40))
+                                      '((12 . 18) (28 . 42)))
+                 '((10 . 20) (28 . 42))))
+  (should (equal (rx--union-intervals '((10 . 20) (30 . 40))
+                                      '((0 . #x3fffff)))
+                 '((0 . #x3fffff)))))
+
+(ert-deftest rx--intersect-intervals ()
+  (should (equal (rx--intersect-intervals '() '()) '()))
+  (should (equal (rx--intersect-intervals '() '((10 . 20) (30 . 40)))
+                 '()))
+  (should (equal (rx--intersect-intervals '((10 . 20) (30 . 40)) '())
+                 '()))
+  (should (equal (rx--intersect-intervals '((5 . 15) (18 . 24) (32 . 40))
+                                          '((10 . 20) (30 . 40) (50 . 60)))
+                 '((10 . 15) (18 . 20) (32 . 40))))
+  (should (equal (rx--intersect-intervals '((10 . 20) (30 . 40) (50 . 60))
+                                          '((0 . 9) (21 . 29) (41 . 50)))
+                 '((50 . 50))))
+  (should (equal (rx--intersect-intervals '((10 . 20) (30 . 40))
+                                          '((12 . 18) (28 . 42)))
+                 '((12 . 18) (30 . 40))))
+  (should (equal (rx--intersect-intervals '((10 . 20) (30 . 40))
+                                          '((0 . #x3fffff)))
+                 '((10 . 20) (30 . 40)))))
+
 (provide 'rx-tests)
 
 ;;; rx-tests.el ends here
diff --git a/test/lisp/eshell/esh-util-tests.el 
b/test/lisp/eshell/esh-util-tests.el
index 8585677e14e..fe4eb9f31dd 100644
--- a/test/lisp/eshell/esh-util-tests.el
+++ b/test/lisp/eshell/esh-util-tests.el
@@ -125,4 +125,35 @@
     (should (equal (eshell-convert-to-number "123") "123"))
     (should (equal (eshell-convert-to-number "1.23") "1.23"))))
 
+(ert-deftest esh-util-test/eshell-printable-size ()
+  (should (equal (eshell-printable-size (expt 2 16)) "65536"))
+  (should (equal (eshell-printable-size (expt 2 32)) "4294967296")))
+
+(ert-deftest esh-util-test/eshell-printable-size/zero ()
+  (should (equal (eshell-printable-size 0 1000 nil t) "0")))
+
+(ert-deftest esh-util-test/eshell-printable-size/terabyte ()
+  (should (equal (eshell-printable-size (1- (expt 2 40)) 1024 nil t) "1024G"))
+  (should (equal (eshell-printable-size (expt 2 40) 1024 nil t) "1T"))
+  (should (equal (eshell-printable-size (1- (expt 10 12)) 1000 nil t) "1000G"))
+  (should (equal (eshell-printable-size (expt 10 12) 1000 nil t) "1T")))
+
+(ert-deftest esh-util-test/eshell-printable-size/use-colors ()
+  (should (equal-including-properties
+           (eshell-printable-size (1- (expt 2 20)) 1024 nil t)
+           "1024k"))
+  (should (equal-including-properties
+           (eshell-printable-size (1- (expt 2 30)) 1024 nil t)
+           (propertize "1024M" 'face 'bold)))
+  (should (equal-including-properties
+           (eshell-printable-size (1- (expt 2 40)) 1024 nil t)
+           (propertize "1024G" 'face 'bold-italic))))
+
+(ert-deftest esh-util-test/eshell-printable-size/block-size ()
+  (should (equal (eshell-printable-size (1- (expt 2 20)) nil 4096) "256"))
+  (should (equal (eshell-printable-size (1- (expt 2 30)) nil 4096) "262144")))
+
+(ert-deftest esh-util-test/eshell-printable-size/human-readable-arg ()
+  (should-error (eshell-printable-size 0 999 nil t)))
+
 ;;; esh-util-tests.el ends here
diff --git a/test/lisp/image/image-dired-util-tests.el 
b/test/lisp/image/image-dired-util-tests.el
index 273a32d5dbb..1f3747a82b1 100644
--- a/test/lisp/image/image-dired-util-tests.el
+++ b/test/lisp/image/image-dired-util-tests.el
@@ -57,23 +57,20 @@
                      "jpg")))))
 
 (ert-deftest image-dired-thumb-name/per-directory ()
-  (let ((image-dired-thumbnail-storage 'per-directory)
-        (rel-path "foo.jpg")
-        (abs-path "/tmp/foo.jpg")
-        (hash-name (concat (sha1 "foo.jpg") ".jpg")))
-    (should (file-name-absolute-p (image-dired-thumb-name rel-path)))
-    (should (file-name-absolute-p (image-dired-thumb-name abs-path)))
+  (let ((image-dired-thumbnail-storage 'per-directory))
+    (should (file-name-absolute-p (image-dired-thumb-name "foo.jpg")))
+    (should (file-name-absolute-p (image-dired-thumb-name "/tmp/foo.jpg")))
     (should (equal
-             (file-name-nondirectory (image-dired-thumb-name rel-path))
-             (file-name-nondirectory (image-dired-thumb-name abs-path))))
+             (file-name-nondirectory (image-dired-thumb-name "foo.jpg"))
+             (file-name-nondirectory (image-dired-thumb-name "/tmp/foo.jpg"))))
     ;; The cdr below avoids the system dependency in the car of the
     ;; list returned by 'file-name-split': it's "" on Posix systems,
     ;; but the drive letter on MS-Windows.
     (should (equal (cdr (file-name-split
-                         (image-dired-thumb-name abs-path)))
-                   (list "tmp" ".image-dired" hash-name)))
+                         (image-dired-thumb-name "/tmp/foo.jpg")))
+                   '("tmp" ".image-dired" "foo.jpg.thumb.jpg")))
     (should (equal (file-name-nondirectory
-                    (image-dired-thumb-name rel-path))
-                   hash-name))))
+                    (image-dired-thumb-name "foo.jpg"))
+                   "foo.jpg.thumb.jpg"))))
 
 ;;; image-dired-util-tests.el ends here
diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el
index ee9c09df9d8..a6c430fb1c0 100644
--- a/test/lisp/net/tramp-tests.el
+++ b/test/lisp/net/tramp-tests.el
@@ -7083,6 +7083,12 @@ This does not support external Emacs calls."
   (string-equal
    "mock" (file-remote-p ert-remote-temporary-file-directory 'method)))
 
+(defun tramp--test-openbsd-p ()
+  "Check, whether the remote host runs OpenBSD."
+  ;; We must refill the cache.  `file-truename' does it.
+  (file-truename ert-remote-temporary-file-directory)
+  (ignore-errors (tramp-check-remote-uname tramp-test-vec "OpenBSD")))
+
 (defun tramp--test-out-of-band-p ()
   "Check, whether an out-of-band method is used."
   (tramp-method-out-of-band-p tramp-test-vec 1))
@@ -7403,6 +7409,7 @@ This requires restrictions of file name syntax."
          (cond ((or (tramp--test-ange-ftp-p)
                     (tramp--test-container-p)
                     (tramp--test-gvfs-p)
+                    (tramp--test-openbsd-p)
                     (tramp--test-rclone-p)
                     (tramp--test-sudoedit-p)
                     (tramp--test-windows-nt-or-smb-p))
@@ -7485,7 +7492,8 @@ This requires restrictions of file name syntax."
        "Автостопом по гала́ктике"
        ;; Use codepoints without a name.  See Bug#31272.
        ;; Works on some Android systems only.
-       (unless (tramp--test-adb-p) "™›šbung")
+       (unless (or (tramp--test-adb-p) (tramp--test-openbsd-p))
+        "™›šbung")
        ;; Use codepoints from Supplementary Multilingual Plane (U+10000
        ;; to U+1FFFF).
        "🌈🍒👋")
@@ -7848,7 +7856,7 @@ process sentinels.  They shall not disturb each other."
 
 (ert-deftest tramp-test47-read-password ()
   "Check Tramp password handling."
-  :tags '(:expensive-test)
+  :tags '(:expensive-test :unstable)
   (skip-unless (tramp--test-enabled))
   (skip-unless (tramp--test-mock-p))
   ;; Not all read commands understand argument "-s" or "-p".
diff --git a/test/lisp/progmodes/cperl-mode-resources/sub-names.pl 
b/test/lisp/progmodes/cperl-mode-resources/sub-names.pl
new file mode 100644
index 00000000000..46d05b4dbd2
--- /dev/null
+++ b/test/lisp/progmodes/cperl-mode-resources/sub-names.pl
@@ -0,0 +1,25 @@
+use 5.038;
+use feature 'class';
+use warnings;
+no warnings 'experimental';
+
+class C {
+    # "method" is not yet understood by perl-mode, but it isn't
+    # relevant here: We can use "sub" because what matters is the
+    # name, which collides with a builtin.
+    sub m {
+       "m called"
+    }
+}
+
+say C->new->m;
+
+# This comment has a method name in it, and we don't want "method"
+# to be fontified as a keyword, nor "name" fontified as a name.
+
+__END__
+
+=head1 Test using the keywords POD
+
+This piece of POD has a method name in it, and we don't want "method"
+to be fontified as a keyword, nor "name" fontified as a name.
diff --git a/test/lisp/progmodes/cperl-mode-tests.el 
b/test/lisp/progmodes/cperl-mode-tests.el
index eaf228cb2e2..8f334245c64 100644
--- a/test/lisp/progmodes/cperl-mode-tests.el
+++ b/test/lisp/progmodes/cperl-mode-tests.el
@@ -256,6 +256,39 @@ These can occur as \"local\" aliases."
     (should (equal (get-text-property (point) 'face)
                    'font-lock-variable-name-face))))
 
+(ert-deftest cperl-test-fontify-sub-names ()
+    "Test fontification of subroutines named like builtins.
+On declaration, they should look like other used defined
+functions.  When called, they should not be fontified.  In
+comments and POD they should be fontified as POD."
+  (let ((file (ert-resource-file "sub-names.pl")))
+    (with-temp-buffer
+      (insert-file-contents file)
+      (goto-char (point-min))
+      (funcall cperl-test-mode)
+      (font-lock-ensure)
+      ;; The declaration
+      (search-forward-regexp "sub \\(m\\)")
+      (should (equal (get-text-property (match-beginning 1) 'face)
+                     'font-lock-function-name-face))
+      ;; calling as a method
+      (search-forward-regexp "C->new->\\(m\\)")
+      (should (equal (get-text-property (match-beginning 1) 'face)
+                     (if (equal cperl-test-mode 'perl-mode) nil
+                       'cperl-method-call)))
+      ;; POD
+      (search-forward-regexp "\\(method\\) \\(name\\)")
+      (should (equal (get-text-property (match-beginning 1) 'face)
+                     'font-lock-comment-face))
+      (should (equal (get-text-property (match-beginning 2) 'face)
+                     'font-lock-comment-face))
+      ;; comment
+      (search-forward-regexp "\\(method\\) \\(name\\)")
+      (should (equal (get-text-property (match-beginning 1) 'face)
+                     'font-lock-comment-face))
+      (should (equal (get-text-property (match-beginning 2) 'face)
+                     'font-lock-comment-face)))))
+
 (ert-deftest cperl-test-identify-heredoc ()
   "Test whether a construct containing \"<<\" followed by a
   bareword is properly identified for a here-document if
diff --git a/test/lisp/textmodes/conf-mode-tests.el 
b/test/lisp/textmodes/conf-mode-tests.el
index 5e21d2cfacb..677a6d35d66 100644
--- a/test/lisp/textmodes/conf-mode-tests.el
+++ b/test/lisp/textmodes/conf-mode-tests.el
@@ -93,12 +93,13 @@ x.2.y.1.z.2.zz =")
       (should (equal (face-at-point) 'font-lock-variable-name-face))
       (search-forward "val")
       (should-not (face-at-point)))
-    (while (re-search-forward "a-z" nil t)
+    (while (re-search-forward "[xyz]" nil t)
       (backward-char)
       (should (equal (face-at-point) 'font-lock-variable-name-face))
-      (re-search-forward "[0-0]" nil t)
-      (backward-char)
-      (should (equal (face-at-point) 'font-lock-constant-face)))))
+      (forward-char)
+      (when (re-search-forward "[0-9]" nil t)
+        (backward-char)
+        (should (equal (face-at-point) 'font-lock-constant-face))))))
 
 (ert-deftest conf-test-space-mode ()
   ;; From `conf-space-mode' docstring.
@@ -157,7 +158,6 @@ image/tiff                  tiff tif
     (should-not (face-at-point))))
 
 (ert-deftest conf-test-toml-mode ()
-  ;; From `conf-toml-mode' docstring.
   (with-temp-buffer
     (insert "[entry]
 value = \"some string\"")
@@ -173,6 +173,22 @@ value = \"some string\"")
     (search-forward "som")
     (should (equal (face-at-point) 'font-lock-string-face))))
 
+(ert-deftest conf-test-toml-mode/boolean ()
+  ;; https://toml.io/en/v1.0.0#boolean
+  (with-temp-buffer
+    (insert "[entry]
+a = true
+b = True")
+    (goto-char (point-min))
+    (conf-toml-mode)
+    (font-lock-mode)
+    (font-lock-ensure)
+    (search-forward "tru")
+    (should (equal (face-at-point) 'font-lock-keyword-face))
+    ;; Do not fontify upper-case "True".
+    (search-forward "Tru")
+    (should (equal (face-at-point) nil))))
+
 (ert-deftest conf-test-desktop-mode ()
   ;; From `conf-desktop-mode' dostring.
   (with-temp-buffer
diff --git a/test/lisp/uniquify-tests.el b/test/lisp/uniquify-tests.el
index 224991cb048..38510c3bd77 100644
--- a/test/lisp/uniquify-tests.el
+++ b/test/lisp/uniquify-tests.el
@@ -89,21 +89,6 @@
                          '("a/dir/" "b/dir/")))
           (mapc #'kill-buffer bufs))))))
 
-(ert-deftest uniquify-home ()
-  "uniquify works, albeit confusingly, in the presence of directories named 
\"~\""
-  (let (bufs)
-    (save-excursion
-      (push (find-file-noselect "~") bufs)
-      (push (find-file-noselect "./~") bufs)
-      (should (equal (mapcar #'buffer-name bufs)
-                     '("~<test>" "~<>")))
-      (push (find-file-noselect "~/foo") bufs)
-      (push (find-file-noselect "./~/foo") bufs)
-      (should (equal (mapcar #'buffer-name bufs)
-                     '("foo<~>" "foo</nonexistent>" "~<test>" "~<>")))
-      (while bufs
-        (kill-buffer (pop bufs))))))
-
 (ert-deftest uniquify-rename-to-dir ()
   "Giving a buffer a name which matches a directory doesn't rename the buffer"
   (let ((uniquify-buffer-name-style 'forward)
diff --git a/test/src/treesit-tests.el b/test/src/treesit-tests.el
index 34540b463cd..65994ce608f 100644
--- a/test/src/treesit-tests.el
+++ b/test/src/treesit-tests.el
@@ -34,6 +34,7 @@
 (declare-function treesit-parser-buffer "treesit.c")
 (declare-function treesit-parser-language "treesit.c")
 
+(declare-function treesit-pattern-expand "treesit.c")
 (declare-function treesit-query-expand "treesit.c")
 (declare-function treesit-query-compile "treesit.c")
 (declare-function treesit-query-capture "treesit.c")



reply via email to

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