emacs-diffs
[Top][All Lists]
Advanced

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

feature/android cfbc8a5dbc 1/2: Bring up the Android operating system an


From: Po Lu
Subject: feature/android cfbc8a5dbc 1/2: Bring up the Android operating system and its window system
Date: Sat, 31 Dec 2022 05:05:29 -0500 (EST)

branch: feature/android
commit cfbc8a5dbcd362b69b37b4e6832ae4a31834364c
Author: Po Lu <luangruo@yahoo.com>
Commit: 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.
---
 .dir-locals.el                              |    2 +-
 .gitignore                                  |   17 +
 Makefile.in                                 |   68 +-
 admin/merge-gnulib                          |    2 +
 configure.ac                                |  526 ++++-
 etc/DEBUG                                   |   24 +
 java/AndroidManifest.xml                    |   54 +
 java/Makefile.in                            |  150 ++
 java/README                                 |    6 +
 java/debug.sh                               |  242 ++
 java/emacs.keystore                         |  Bin 0 -> 2776 bytes
 java/org/gnu/emacs/EmacsActivity.java       |  146 ++
 java/org/gnu/emacs/EmacsApplication.java    |   27 +
 java/org/gnu/emacs/EmacsCopyArea.java       |  131 ++
 java/org/gnu/emacs/EmacsDrawLine.java       |  137 ++
 java/org/gnu/emacs/EmacsDrawPoint.java      |   30 +
 java/org/gnu/emacs/EmacsDrawRectangle.java  |  127 ++
 java/org/gnu/emacs/EmacsDrawable.java       |   33 +
 java/org/gnu/emacs/EmacsFillPolygon.java    |  150 ++
 java/org/gnu/emacs/EmacsFillRectangle.java  |  129 ++
 java/org/gnu/emacs/EmacsFontDriver.java     |  150 ++
 java/org/gnu/emacs/EmacsGC.java             |  116 +
 java/org/gnu/emacs/EmacsHandleObject.java   |   62 +
 java/org/gnu/emacs/EmacsNative.java         |   72 +
 java/org/gnu/emacs/EmacsPaintQueue.java     |  138 ++
 java/org/gnu/emacs/EmacsPaintReq.java       |   33 +
 java/org/gnu/emacs/EmacsPixmap.java         |  102 +
 java/org/gnu/emacs/EmacsSdk7FontDriver.java |  460 ++++
 java/org/gnu/emacs/EmacsService.java        |  384 ++++
 java/org/gnu/emacs/EmacsSurfaceView.java    |   83 +
 java/org/gnu/emacs/EmacsThread.java         |   44 +
 java/org/gnu/emacs/EmacsView.java           |  211 ++
 java/org/gnu/emacs/EmacsWindow.java         |  336 +++
 lib-src/Makefile.in                         |    8 +
 lib/Makefile.in                             |  137 +-
 lib/faccessat.c                             |    4 +
 lib/fpending.c                              |    6 +-
 lib/open.c                                  |    4 +
 lib/unistd.c                                |    2 +-
 lisp/frame.el                               |   23 +-
 lisp/image/wallpaper.el                     |    2 +
 lisp/loadup.el                              |    5 +
 lisp/net/eww.el                             |    8 +-
 lisp/term/android-win.el                    |   62 +
 src/Makefile.in                             |   52 +-
 src/alloc.c                                 |   12 +
 src/android-emacs.c                         |   30 +
 src/android.c                               | 2335 ++++++++++++++++++++
 src/android.h                               |   70 +
 src/androidfns.c                            | 1779 +++++++++++++++
 src/androidfont.c                           |  955 ++++++++
 src/androidgui.h                            |  315 +++
 src/androidterm.c                           | 3161 +++++++++++++++++++++++++++
 src/androidterm.h                           |  366 ++++
 src/dired.c                                 |    5 +-
 src/dispextern.h                            |   31 +-
 src/dispnew.c                               |   48 +-
 src/editfns.c                               |    8 +
 src/emacs.c                                 |   37 +-
 src/epaths.in                               |   22 +
 src/fileio.c                                |   25 +-
 src/filelock.c                              |    6 +
 src/fns.c                                   |    4 +
 src/font.c                                  |   16 +
 src/font.h                                  |    3 +
 src/frame.c                                 |   30 +-
 src/frame.h                                 |   35 +-
 src/image.c                                 |   88 +-
 src/lisp.h                                  |   12 +
 src/lread.c                                 |    6 +-
 src/pdumper.c                               |    2 +-
 src/process.c                               |   17 +-
 src/scroll.c                                |    7 +
 src/sysdep.c                                |   98 +-
 src/term.c                                  |  135 +-
 src/termhooks.h                             |   22 +-
 src/terminal.c                              |    2 +
 src/verbose.mk.in                           |    8 +
 src/xdisp.c                                 |   26 +-
 src/xfaces.c                                |   59 +-
 xcompile/Makefile.in                        |  209 ++
 xcompile/README                             |    7 +
 xcompile/langinfo.h                         |   20 +
 83 files changed, 14298 insertions(+), 218 deletions(-)

diff --git a/.dir-locals.el b/.dir-locals.el
index fc89dff87f..d8eece9224 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 af0ba0eb41..b5cc319125 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,6 +52,15 @@ src/config.h
 src/epaths.h
 src/emacs-module.h
 
+# Built by recursive call to `configure'.
+*.android
+
+# Built by `java'.
+java/install_temp/*
+java/*.apk*
+java/*.dex
+java/org/gnu/emacs/*.class
+
 # C-level sources built by 'make'.
 lib/alloca.h
 lib/assert.h
@@ -81,6 +90,14 @@ src/globals.h
 src/lisp.mk
 src/verbose.mk
 
+# Stuff built during cross compilation
+xcompile/lib/*
+xcompile/src/*
+xcompile/lib-src/*
+xcompile/sys/*
+xcompile/config.status
+xcompile/*.bak
+
 # Lisp-level sources built by 'make'.
 *cus-load.el
 *loaddefs.el
diff --git a/Makefile.in b/Makefile.in
index 93609a4e16..d8c5ba555d 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
+       $(MAKE) -C $@ all
+
+xcompile: src
+       $(MAKE) -C $@ all
+
 trampolines: src lisp
 ifeq ($(HAVE_NATIVE_COMP),yes)
        $(MAKE) -C lisp trampolines
@@ -561,20 +571,38 @@ blessmail: Makefile src
 # then attempts to build that file.  This forces 'Makefile', 'lib/Makefile',
 # etc. to be built without running into similar recursion problems.
 MAKEFILE_NAME = Makefile
+ifeq ($(ANDROID),)
 $(MAKEFILE_NAME): config.status $(srcdir)/configure \
         $(srcdir)/lib/gnulib.mk.in \
          $(srcdir)/Makefile.in $(SUBDIR_MAKEFILES_IN) $(CONFIG_STATUS_FILES_IN)
        MAKE='$(MAKE)' ./config.status
+else
+# Note that calling config.status is insufficient on Android due to
+# the recursive calls to configure.
+
+$(MAKEFILE_NAME): $(srcdir)/configure \
+        $(srcdir)/lib/gnulib.mk.in \
+         $(srcdir)/Makefile.in $(SUBDIR_MAKEFILES_IN) $(CONFIG_STATUS_FILES_IN)
+       $(CFG) $(srcdir)/configure $(CONFIGURE_FLAGS);
+endif
 
 # 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.
+
+ifneq ($(ANDROID),)
 config.status: ${srcdir}/configure
-       if [ -x ./config.status ]; then \
+       $(CFG) $(srcdir)/configure $(CONFIGURE_FLAGS)
+else
+config.status: ${srcdir}/configure
+       if [ -x ./config.status ]; then         \
            $(CFG) ./config.status --recheck;   \
-       else                            \
+       else                                    \
            $(CFG) $(srcdir)/configure $(CONFIGURE_FLAGS); \
        fi
+endif
 
 $(srcdir)/configure: $(srcdir)/configure.ac $(srcdir)/m4/*.m4
        cd $(srcdir) && ./autogen.sh autoconf
@@ -999,7 +1027,7 @@ 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 nextstep admin/charsets admin/unidata
 
 $(foreach dir,$(clean_dirs),$(eval $(call submake_template,$(dir),clean)))
 
diff --git a/admin/merge-gnulib b/admin/merge-gnulib
index 99f834b0ae..f5d3e69e79 100755
--- a/admin/merge-gnulib
+++ b/admin/merge-gnulib
@@ -134,3 +134,5 @@ cp -- "$gnulib_srcdir"/lib/af_alg.h \
    "$src"lib &&
 { test -z "$src" || cd "$src"; } &&
 ./autogen.sh
+
+echo "Please update the block of vpath statements in lib/Makefile.in"
diff --git a/configure.ac b/configure.ac
index 6e9b11986c..ad2d306c64 100644
--- a/configure.ac
+++ b/configure.ac
@@ -26,6 +26,16 @@ 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 CXX to ANDROID_CXX.
+  CC=$ANDROID_CC
+  CXX=$ANDROID_CXX
+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 +136,25 @@ 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
+and ANDROID_CXX variables when you ran configure.])
+    ;;
+  esac
+  AC_MSG_RESULT([$host_alias])
+fi
+
 AC_CANONICAL_HOST
+AC_CANONICAL_BUILD
 
 case $host in
  *-mingw*)
@@ -207,6 +235,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
@@ -498,6 +533,7 @@ 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])
 
 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)])],
@@ -682,6 +718,237 @@ AC_ARG_ENABLE([build-details],
   [test "$enableval" = no && BUILD_DETAILS=--no-build-details])
 AC_SUBST([BUILD_DETAILS])
 
+# 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=
+ZIPALIGN=
+DX=
+ANDROID_JAR=
+ANDROID_ABI=
+
+# This is a list of Makefiles that have alternative versions for
+# Android.
+android_makefiles="lib/Makefile lib/gnulib.mk lib-src/Makefile src/Makefile"
+
+AC_ARG_VAR([JAVAC], [Java compiler path.  Used for Android.])
+AC_ARG_VAR([JARSIGNER], [Java package signer path.  Used for Android.])
+AC_ARG_VAR([SDK_BULD_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
+              ANDROID_CXX=/path/to/armv7a-linux-androideabi19-clang++])
+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 or not 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
+
+  ANDROID_JAR="$with_android"
+
+  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([DX], [dx d8], [], "${SDK_BUILD_TOOLS}:$PATH")
+  if test "DX" = ""; 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" = "" || test "$ANDROID_CXX" = ""; 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    \\
+  ANDROID_CXX=/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
+      ;;
+    *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 and ANDROID_CXX variables when you ran configure.])
+      ;;
+  esac
+  AC_MSG_RESULT([$android_abi])
+
+  ANDROID_ABI=$android_abi
+
+  # Save confdefs.h and config.log for now.
+  mv -f confdefs.h _confdefs.h
+  mv -f config.log _config.log
+
+  AS_IF([XCONFIGURE=android ANDROID_CC="$ANDROID_CC" \
+  ANDROID_CXX="$ANDROID_CXX" $0], [], [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
+
+  # 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([DX])
+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, all build options must be
+  # disabled, both within the recursive invocation of configure and
+  # outside.
+  with_xpm=no
+  with_jpeg=no
+  with_tiff=no
+  with_gif=no
+  with_png=no
+  with_rsvg=no
+  with_webp=no
+  with_sqlite3=no
+  with_lcms2=no
+  with_libsystemd=no
+  with_cairo=no
+  with_xml2=no
+  with_imagemagick=no
+  with_json=no
+  with_tree_sitter=no
+  with_xft=no
+  with_harfbuzz=no
+  with_libotf=no
+  with_gpm=no
+  with_dbus=no
+  with_gsettings=no
+  with_selinx=no
+  with_gnutls=no
+  with_zlib=no
+  with_modules=no
+  with_threads=no
+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.
@@ -705,6 +972,11 @@ dnl quotation begins
 opsys='' unported=no
 case "${canonical}" in
 
+  ## Android
+  *linux-android* )
+    opsys=android
+  ;;
+
   ## GNU/Linux and similar ports
   *-*-linux* )
     opsys=gnu-linux
@@ -871,6 +1143,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
@@ -1123,6 +1396,12 @@ 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.
+
+  nw="$nw -Wsuggest-attribute=noreturn"
+
   gl_MANYWARN_ALL_GCC([ws])
   gl_MANYWARN_COMPLEMENT([ws], [$ws], [$nw])
   for w in $ws; do
@@ -1143,6 +1422,8 @@ 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])
+    gl_WARN_ADD([-Wno-bitwise-instead-of-logical])
   fi
 
   # This causes too much noise in the MinGW build
@@ -1265,7 +1546,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
@@ -1762,7 +2043,10 @@ AC_SUBST([SYSTEM_TYPE])
 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],
@@ -1945,14 +2229,49 @@ AC_SUBST([AUTO_DEPEND])
 
 window_system=none
 
+ANDROID_OBJ=
+ANDROID_LIBS=
+ANDROID_CFLAGS=
+CM_OBJ="cm.o"
+
+if test "${ANDROID}" = "yes"; then
+  window_system=android
+  no_x=yes
+  ANDROID_OBJ="androidterm.o androidfns.o androidfont.o android.o"
+  ANDROID_LIBS=
+  CM_OBJ=
+
+  AC_DEFINE([HAVE_ANDROID], [1], [Define to 1 if Emacs is being built
+with Android support])
+
+  if test "${XCONFIGURE}" != "android"; then
+    AC_DEFINE([ANDROID_STUBIFY], [1], [Define to 1 if Emacs is being built
+for Android, but all API calls need to be stubbed out])
+  else
+    # 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_CFLAGS="-fPIC -fvisibility=hidden"
+
+    # Link with libraries required for Android support.
+    ANDROID_LIBS="-landroid -llog"
+  fi
+fi
+
+AC_SUBST(ANDROID)
+AC_SUBST(ANDROID_OBJ)
+AC_SUBST(ANDROID_LIBS)
+AC_SUBST(ANDROID_CFLAGS)
+
 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=
@@ -2276,7 +2595,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.])
@@ -2439,7 +2757,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
@@ -2773,7 +3095,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" \
@@ -4202,7 +4523,7 @@ 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])
@@ -5003,6 +5324,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
@@ -5108,7 +5463,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.
@@ -5175,7 +5530,7 @@ fail;
     fi
     ;;
 
-  mingw32)
+  mingw32 | android)
     TERMINFO=no
     LIBS_TERMCAP=
     ;;
@@ -5729,7 +6084,7 @@ case $opsys in
     AC_DEFINE([FIRST_PTY_LETTER], ['p'])
     ;;
 
-  gnu-linux | gnu-kfreebsd | dragonfly | freebsd | openbsd | netbsd | darwin | 
nacl )
+  gnu-linux | gnu-kfreebsd | dragonfly | freebsd | openbsd | netbsd | darwin | 
nacl | android )
     dnl if HAVE_GRANTPT
     if test "x$ac_cv_func_grantpt" = xyes; then
       AC_DEFINE([UNIX98_PTYS], [1], [Define if the system has Unix98 PTYs.])
@@ -6656,6 +7011,7 @@ AC_DEFINE_UNQUOTED([EMACS_CONFIG_FEATURES], 
["${emacs_config_features}"],
   [Summary of some of the main features enabled by configure.])
 
 AS_ECHO(["  Does Emacs use -lXaw3d?                                 
${HAVE_XAW3D}
+  Does Emacs use Android?                                 ${ANDROID}
   Does Emacs use -lXpm?                                   ${HAVE_XPM}
   Does Emacs use -ljpeg?                                  ${HAVE_JPEG}
   Does Emacs use -ltiff?                                  ${HAVE_TIFF}
@@ -6759,12 +7115,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])
 
-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)
+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])
+
+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
@@ -6772,24 +7131,31 @@ 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"
 fi
 
+if test "$XCOMPILE" = "yes"; then
+  SUBDIR_MAKEFILES="$SUBDIR_MAKEFILES xcompile/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
 
 
@@ -6800,65 +7166,75 @@ 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])
+     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])
+    fi
   fi
 fi
 
+# Make java/Makefile
+ARCH_INDEPENDENT_CONFIG_FILES([java/Makefile])
+ARCH_INDEPENDENT_CONFIG_FILES([xcompile/Makefile])
+
 AC_OUTPUT
 
 if test ! "$with_mailutils"; then
diff --git a/etc/DEBUG b/etc/DEBUG
index 01c75f8da7..2fe2fbc2ce 100644
--- a/etc/DEBUG
+++ b/etc/DEBUG
@@ -1094,6 +1094,30 @@ 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.
+
 
 This file is part of GNU Emacs.
 
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
new file mode 100644
index 0000000000..75aa5bdf40
--- /dev/null
+++ b/java/AndroidManifest.xml
@@ -0,0 +1,54 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android";
+         package="org.gnu.emacs" android:installLocation="auto">
+
+  <!-- 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.INTERNET" />
+  <uses-permission android:name="android.permission.SET_WALLPAPER" />
+  <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" />
+
+  <uses-sdk android:minSdkVersion="7"
+           android:targetSdkVersion="28"/>
+
+  <application android:name="org.gnu.emacs.EmacsApplication"
+              android:label="GNU Emacs"
+              android:hardwareAccelerated="true"
+              android:supportsRtl="true"
+              android:theme="@android:style/Theme"
+              android:debuggable="true"
+              android:extractNativeLibs="true">
+    <activity android:name="org.gnu.emacs.EmacsActivity">
+      <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>
+
+    <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/Makefile.in b/java/Makefile.in
new file mode 100644
index 0000000000..e9fcc625cb
--- /dev/null
+++ b/java/Makefile.in
@@ -0,0 +1,150 @@
+### @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@
+
+-include ${top_builddir}/src/verbose.mk
+
+SHELL = @SHELL@
+JAVAC = @JAVAC@
+AAPT = @AAPT@
+DX = @DX@
+ZIPALIGN = @ZIPALIGN@
+JARSIGNER = @JARSIGNER@
+ANDROID_JAR = @ANDROID_JAR@
+ANDROID_ABI = @ANDROID_ABI@
+
+WARN_JAVAFLAGS = -Xlint:deprecation
+JAVAFLAGS = -classpath "$(ANDROID_JAR):." -target 1.7 -source 1.7 \
+  $(WARN_JAVAFLAGS)
+
+SIGN_EMACS = -keystore emacs.keystore -storepass emacs1
+
+JAVA_FILES = $(shell find . -type f -name *.java)
+CLASS_FILES = $(foreach file,$(JAVA_FILES),$(basename $(file)).class)
+
+# 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)/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: emacs.apk
+
+# Binaries to cross-compile.
+CROSS_BINS = ../xcompile/src/android-emacs ../xcompile/lib-src/ctags   \
+            ../xcompile/lib-src/hexl ../xcompile/lib-src/movemail      \
+            ../xcompile/lib-src/ebrowse
+
+# Libraries to cross-compile.
+CROSS_LIBS = ../xcompile/src/libemacs.so
+
+.PHONY: $(CROSS_BINS) $(CROSS_LIBS)
+
+../xcompile/src/android-emacs ../xcompile/src/libemacs.so:
+       make -C ../xcompile src/$(notdir $@)
+
+../xcompile/lib-src/hexl ../xcompile/lib-src/movemail \
+../xcompile/lib-src/ctags ../xcompile/lib-src/ebrowse &:
+       make -C ../xcompile lib-src/$(notdir $@)
+
+emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) AndroidManifest.xml
+# Make the working directory for this stuff
+       rm -rf install_temp
+       mkdir -p install_temp/lib/$(ANDROID_ABI)
+       mkdir -p install_temp/assets/etc
+       mkdir -p install_temp/assets/lisp
+       mkdir -p install_temp/assets/info
+# Install architecture independents to assets/etc and assets/lisp
+       cp -r $(top_builddir)/lisp install_temp/assets
+       cp -r $(top_builddir)/etc install_temp/assets
+# Remove undesirable files from those directories.
+       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
+# Install architecture dependents to lib/$(ANDROID_ABI).  This
+# perculiar naming scheme is required to make Android preserve these
+# binaries upon installation.
+       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
+       for file in $(CROSS_LIBS); do                           \
+         if [ -x $$file ]; then                                \
+           cp -f $$file install_temp/lib/$(ANDROID_ABI);       \
+         fi                                                    \
+       done
+# Package everything.
+       $(AAPT) package -I "$(ANDROID_JAR)" -F $@ -f -M AndroidManifest.xml
+       pushd install_temp; $(AAPT) add ../$@ `find lib -type f`; popd
+       pushd install_temp; $(AAPT) add ../$@ `find assets -type f`; popd
+       rm -rf install_temp
+
+.SUFFIXES: .java .class
+.java.class &:
+       $(AM_V_JAVAC) $(JAVAC) $(JAVAFLAGS) $<
+
+# N.B. that find must be called all over again in case javac generated
+# nested classes.
+
+classes.dex: $(CLASS_FILES)
+       $(AM_V_DX) $(DX) --classpath $(ANDROID_JAR) \
+         $(subst $$,\$$,$(shell find . -type f -name *.class))
+
+# 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
+
+emacs.apk: classes.dex emacs.apk-in emacs.keystore
+       cp -f emacs.apk-in $@.unaligned
+       $(AAPT) add $@.unaligned classes.dex
+       $(JARSIGNER) $(SIGN_EMACS) $@.unaligned "Emacs keystore"
+       $(ZIPALIGN) -f 4 $@.unaligned $@
+       rm -f $@.unaligned
+
+clean:
+       rm -f emacs.apk emacs.apk-in *.dex *.unaligned *.class
+       rm -rf install-temp
+       find . -name '*.class' -delete
+
+maintainer-clean: clean
diff --git a/java/README b/java/README
new file mode 100644
index 0000000000..50c2332ce9
--- /dev/null
+++ b/java/README
@@ -0,0 +1,6 @@
+This directory holds the Java sources of the port of GNU Emacs to
+Android-like systems.
+
+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.
diff --git a/java/debug.sh b/java/debug.sh
new file mode 100755
index 0000000000..dd710dc31a
--- /dev/null
+++ b/java/debug.sh
@@ -0,0 +1,242 @@
+#!/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
+
+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
+           ;;
+       "--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 "  --help              print this message"
+           echo ""
+           echo "Available devices:"
+           for device in $devices; do
+               echo "  " $device
+           done
+           echo ""
+           exit 0
+           ;;
+       "--jdb" )
+           jdb=yes
+           ;;
+       "--port" )
+           gdb_port=$1
+           ;;
+       "--" )
+           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..."
+
+# Find which PIDs are associated with org.gnu.emacs
+package_uid=`adb -s $device shell run-as $package id -u`
+
+if [ -z $package_uid ]; then
+    echo "Failed to obtain UID of packages named $package"
+    exit 1
+fi
+
+# First, run ps -u $package_uid -o PID,CMD to fetch the list of
+# process IDs.
+package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o 
PID,CMD`
+
+# Next, remove lines matching "ps" itself.
+package_pids=`awk -- '{
+  if (!match ($0, /(PID|ps)/))
+    print $1
+}' <<< $package_pids`
+
+# 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 "$package/$activity"
+if [ ! $? ]; then
+    exit 1;
+fi
+
+# Now look for processes matching the package again.
+package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o 
PID,CMD`
+
+# Next, remove lines matching "ps" itself.
+package_pids=`awk -- '{
+  if (!match ($0, /(PID|ps)/))
+    print $1
+}' <<< $package_pids`
+
+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 -u $package_uid | awk -- '{
+      if (!match ($0, /ps/))
+        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
+
+# Start JDB to make the wait dialog disappear.
+echo "Attaching JDB to unblock the application."
+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
+
+exec 4<> /tmp/file-descriptor-stamp
+
+# Now run JDB with IO redirected to file descriptor 4 in a subprocess.
+$jdb_command <&4 >&4 &
+
+character=
+# Next, wait until the prompt is found.
+while read -n1 -u 4 character; do
+    if [ "$character" = ">" ]; then
+       echo "JDB attached successfully"
+       break;
+    fi
+done
+
+# Now start gdbserver on the device asynchronously.
+
+echo "Attaching gdbserver to $pid on $device..."
+exec 5<> /tmp/file-descriptor-stamp
+adb -s $device shell run-as $package /system/bin/gdbserver --once \
+    "+debug.$package_uid.socket" --attach $pid >&5 &
+
+# Wait until gdbserver successfully runs.
+line=
+while read -u 5 line; do
+    case "$line" in
+       *Attached* )
+           break;
+           ;;
+       *error* | *Error* | failed )
+           echo $line
+           exit 1
+           ;;
+       * )
+           ;;
+    esac
+done
+
+# Send EOF to JDB to make it go away.  This will also cause Android to
+# allow Emacs to continue executing.
+echo "Making JDB go away..."
+echo "exit" >&4
+read -u 4 line
+echo "JDB has gone away with $line"
+
+# Forward the gdb server port here.
+adb -s $device forward "tcp:$gdb_port" \
+    "localfilesystem:$app_data_dir/debug.$package_uid.socket"
+if [ ! $? ]; then
+    echo "Failed to forward $app_data_dir/debug.$package_uid.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 "" --eval-command "target remote localhost:$gdb_port" 
$gdbargs
diff --git a/java/emacs.keystore b/java/emacs.keystore
new file mode 100644
index 0000000000..76d80b6db6
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 0000000000..cacd5f13e6
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsActivity.java
@@ -0,0 +1,146 @@
+/* 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.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.FrameLayout;
+import android.widget.FrameLayout.LayoutParams;
+
+public class EmacsActivity extends Activity
+{
+  public static final String TAG = "EmacsActivity";
+
+  /* List of all activities that do not have an associated
+     EmacsWindow.  */
+  public static List<EmacsActivity> availableActivities;
+
+  /* The currently attached EmacsWindow, or null if none.  */
+  private EmacsWindow window;
+
+  /* The frame layout associated with the activity.  */
+  private FrameLayout layout;
+
+  static
+  {
+    /* Set up the list of available activities.  */
+    availableActivities = new ArrayList<EmacsActivity> ();
+  };
+
+  public void
+  attachChild (EmacsWindow child)
+  {
+    if (window != null)
+      throw new IllegalStateException ("trying to attach window when one"
+                                      + " already exists");
+
+    /* Record and attach the view.  */
+    window = child;
+    layout.addView (window.view);
+
+    /* Remove the objects from the lists of what is available.  */
+    EmacsService.availableChildren.remove (child);
+    availableActivities.remove (this);
+
+    /* Now set child->activity.  */
+    child.setActivity (this);
+  }
+
+  /* Make this activity available for future windows to attach
+     again.  */
+
+  public void
+  makeAvailable ()
+  {
+    window = null;
+
+    for (EmacsWindow iterWindow
+          : EmacsService.availableChildren)
+      {
+       synchronized (iterWindow)
+         {
+           if (!iterWindow.isDestroyed ())
+             attachChild (iterWindow);
+
+           return;
+         }
+      }
+
+    availableActivities.add (this);
+  }
+
+  @Override
+  public void
+  onCreate (Bundle savedInstanceState)
+  {
+    FrameLayout.LayoutParams params;
+
+    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);
+
+    /* Make the activity available before starting the
+       service.  */
+    makeAvailable ();
+
+    if (EmacsService.SERVICE == null)
+      /* Start the Emacs service now.  */
+      startService (new Intent (this, EmacsService.class));
+
+    super.onCreate (savedInstanceState);
+  }
+
+  @Override
+  public void
+  onStop ()
+  {
+    /* The activity is no longer visible.  If there is a window
+       attached, detach it.  */
+
+    if (window != null)
+      {
+       layout.removeView (window.view);
+
+       /* Notice that the window is already available too.  But do
+          not call noticeAvailableChild; that might assign it to some
+          other activity, which behaves badly.  */
+       EmacsService.availableChildren.add (window);
+       window = null;
+      }
+
+    /* Finally, remove this activity from the list of available
+       activities.  */
+    availableActivities.remove (this);
+    super.onStop ();
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsApplication.java 
b/java/org/gnu/emacs/EmacsApplication.java
new file mode 100644
index 0000000000..125da05cfd
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsApplication.java
@@ -0,0 +1,27 @@
+/* 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.app.Application;
+
+public class EmacsApplication extends Application
+{
+  /* This class currently does nothing.  */
+};
diff --git a/java/org/gnu/emacs/EmacsCopyArea.java 
b/java/org/gnu/emacs/EmacsCopyArea.java
new file mode 100644
index 0000000000..f34d1ecde0
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsCopyArea.java
@@ -0,0 +1,131 @@
+/* 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.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.Xfermode;
+
+public class EmacsCopyArea implements EmacsPaintReq
+{
+  private int src_x, src_y, dest_x, dest_y, width, height;
+  private EmacsDrawable destination, source;
+  private EmacsGC immutableGC;
+  private static Xfermode xorAlu, srcInAlu;
+
+  static
+  {
+    xorAlu = new PorterDuffXfermode (Mode.XOR);
+    srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
+  };
+
+  public
+  EmacsCopyArea (EmacsDrawable destination, EmacsDrawable source,
+                int src_x, int src_y, int width, int height,
+                int dest_x, int dest_y, EmacsGC immutableGC)
+  {
+    this.destination = destination;
+    this.source = source;
+    this.src_x = src_x;
+    this.src_y = src_y;
+    this.width = width;
+    this.height = height;
+    this.dest_x = dest_x;
+    this.dest_y = dest_y;
+    this.immutableGC = immutableGC;
+  }
+
+  @Override
+  public Rect
+  getRect ()
+  {
+    return new Rect (dest_x, dest_y, dest_x + width,
+                    dest_y + height);
+  }
+
+  @Override
+  public EmacsDrawable
+  getDrawable ()
+  {
+    return destination;
+  }
+
+  @Override
+  public EmacsGC
+  getGC ()
+  {
+    return immutableGC;
+  }
+
+  @Override
+  public void
+  paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
+  {
+    int alu;
+    Bitmap bitmap;
+    Paint maskPaint;
+    Canvas maskCanvas;
+    Bitmap maskBitmap;
+    Rect rect, srcRect;
+
+    /* TODO implement stippling.  */
+    if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+      return;
+
+    alu = immutableGC.function;
+    rect = getRect ();
+    bitmap = source.getBitmap ();
+
+    if (alu == EmacsGC.GC_COPY)
+      paint.setXfermode (null);
+    else
+      paint.setXfermode (xorAlu);
+
+    if (immutableGC.clip_mask == null)
+      canvas.drawBitmap (bitmap, new Rect (src_x, src_y,
+                                          src_x + width,
+                                          src_y + height),
+                        rect, paint);
+    else
+      {
+       maskPaint = new Paint ();
+       srcRect = new Rect (0, 0, rect.width (),
+                           rect.height ());
+       maskBitmap
+         = immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888,
+                                              true);
+
+       if (maskBitmap == null)
+         return;
+
+       maskPaint.setXfermode (srcInAlu);
+       maskCanvas = new Canvas (maskBitmap);
+       maskCanvas.drawBitmap (bitmap, new Rect (src_x, src_y,
+                                                src_x + width,
+                                                src_y + height),
+                              srcRect, maskPaint);
+       canvas.drawBitmap (maskBitmap, srcRect, rect, paint);
+      }
+  }
+}
diff --git a/java/org/gnu/emacs/EmacsDrawLine.java 
b/java/org/gnu/emacs/EmacsDrawLine.java
new file mode 100644
index 0000000000..6389031bbf
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDrawLine.java
@@ -0,0 +1,137 @@
+/* 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.Math;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.Xfermode;
+
+public class EmacsDrawLine implements EmacsPaintReq
+{
+  private int x, y, x2, y2;
+  private EmacsDrawable drawable;
+  private EmacsGC immutableGC;
+  private static Xfermode xorAlu, srcInAlu;
+
+  static
+  {
+    xorAlu = new PorterDuffXfermode (Mode.XOR);
+    srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
+  };
+
+  public
+  EmacsDrawLine (EmacsDrawable drawable, int x, int y,
+                int x2, int y2, EmacsGC immutableGC)
+  {
+    this.drawable = drawable;
+    this.x = x;
+    this.y = y;
+    this.x2 = x2;
+    this.y2 = y2;
+    this.immutableGC = immutableGC;
+  }
+
+  @Override
+  public Rect
+  getRect ()
+  {
+    return new Rect (Math.min (x, x2 + 1),
+                    Math.min (y, y2 + 1),
+                    Math.max (x2 + 1, x),
+                    Math.max (y2 + 1, y));
+  }
+
+  @Override
+  public EmacsDrawable
+  getDrawable ()
+  {
+    return drawable;
+  }
+
+  @Override
+  public EmacsGC
+  getGC ()
+  {
+    return immutableGC;
+  }
+
+  @Override
+  public void
+  paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
+  {
+    int alu;
+    Paint maskPaint;
+    Canvas maskCanvas;
+    Bitmap maskBitmap;
+    Rect rect, srcRect;
+    int width, height;
+
+    /* TODO implement stippling.  */
+    if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+      return;
+
+    alu = immutableGC.function;
+    rect = getRect ();
+    width = rect.width ();
+    height = rect.height ();
+
+    paint.setStyle (Paint.Style.STROKE);
+
+    if (alu == EmacsGC.GC_COPY)
+      paint.setXfermode (null);
+    else
+      paint.setXfermode (xorAlu);
+
+    if (immutableGC.clip_mask == null)
+      {
+        paint.setColor (immutableGC.foreground | 0xff000000);
+       canvas.drawLine ((float) x, (float) y,
+                        (float) x2, (float) y2,
+                        paint);
+      }
+    else
+      {
+       maskPaint = new Paint ();
+       maskBitmap
+         = immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888,
+                                              true);
+
+       if (maskBitmap == null)
+         return;
+
+       maskPaint.setXfermode (srcInAlu);
+       maskPaint.setColor (immutableGC.foreground | 0xff000000);
+       maskCanvas = new Canvas (maskBitmap);
+       srcRect = new Rect (0, 0, maskBitmap.getWidth (),
+                           maskBitmap.getHeight ());
+       maskCanvas.drawLine (0.0f, 0.0f, (float) Math.abs (x - x2),
+                            (float) Math.abs (y - y2), maskPaint);
+       canvas.drawBitmap (maskBitmap, srcRect, rect, paint);
+      }
+
+    paint.setXfermode (null);
+  }
+}
diff --git a/java/org/gnu/emacs/EmacsDrawPoint.java 
b/java/org/gnu/emacs/EmacsDrawPoint.java
new file mode 100644
index 0000000000..772757ff42
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDrawPoint.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;
+
+public class EmacsDrawPoint extends EmacsDrawRectangle
+{
+  public
+  EmacsDrawPoint (EmacsDrawable drawable, int x, int y,
+                 EmacsGC immutableGC)
+  {
+    super (drawable, x, y, 1, 1, immutableGC);
+  }
+}
diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java 
b/java/org/gnu/emacs/EmacsDrawRectangle.java
new file mode 100644
index 0000000000..462bf7c85b
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDrawRectangle.java
@@ -0,0 +1,127 @@
+/* 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.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.Xfermode;
+
+public class EmacsDrawRectangle implements EmacsPaintReq
+{
+  private int x, y, width, height;
+  private EmacsDrawable drawable;
+  private EmacsGC immutableGC;
+  private static Xfermode xorAlu, srcInAlu;
+
+  static
+  {
+    xorAlu = new PorterDuffXfermode (Mode.XOR);
+    srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
+  };
+
+  public
+  EmacsDrawRectangle (EmacsDrawable drawable, int x, int y,
+                     int width, int height,
+                     EmacsGC immutableGC)
+  {
+    this.drawable = drawable;
+    this.x = x;
+    this.y = y;
+    this.width = width;
+    this.height = height;
+    this.immutableGC = immutableGC;
+  }
+
+  @Override
+  public Rect
+  getRect ()
+  {
+    return new Rect (x, y, x + width, y + height);
+  }
+
+  @Override
+  public EmacsDrawable
+  getDrawable ()
+  {
+    return drawable;
+  }
+
+  @Override
+  public EmacsGC
+  getGC ()
+  {
+    return immutableGC;
+  }
+
+  @Override
+  public void
+  paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
+  {
+    int alu;
+    Paint maskPaint;
+    Canvas maskCanvas;
+    Bitmap maskBitmap;
+    Rect rect, srcRect;
+
+    /* TODO implement stippling.  */
+    if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+      return;
+
+    alu = immutableGC.function;
+    rect = getRect ();
+
+    paint.setStyle (Paint.Style.STROKE);
+
+    if (alu == EmacsGC.GC_COPY)
+      paint.setXfermode (null);
+    else
+      paint.setXfermode (xorAlu);
+
+    if (immutableGC.clip_mask == null)
+      {
+        paint.setColor (immutableGC.foreground | 0xff000000);
+       canvas.drawRect (rect, paint);
+      }
+    else
+      {
+       maskPaint = new Paint ();
+       maskBitmap
+         = immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888,
+                                              true);
+
+       if (maskBitmap == null)
+         return;
+
+       maskPaint.setXfermode (srcInAlu);
+       maskPaint.setColor (immutableGC.foreground | 0xff000000);
+       maskCanvas = new Canvas (maskBitmap);
+       srcRect = new Rect (0, 0, maskBitmap.getWidth (),
+                           maskBitmap.getHeight ());
+       maskCanvas.drawRect (srcRect, maskPaint);
+       canvas.drawBitmap (maskBitmap, srcRect, rect, paint);
+      }
+
+    paint.setXfermode (null);
+  }
+}
diff --git a/java/org/gnu/emacs/EmacsDrawable.java 
b/java/org/gnu/emacs/EmacsDrawable.java
new file mode 100644
index 0000000000..1906213721
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDrawable.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;
+
+import android.graphics.Rect;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+
+public interface EmacsDrawable
+{
+  public Canvas lockCanvas ();
+  public void unlockCanvas ();
+  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 0000000000..3198c7f07c
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsFillPolygon.java
@@ -0,0 +1,150 @@
+/* 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.Math;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Point;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Xfermode;
+
+public class EmacsFillPolygon implements EmacsPaintReq
+{
+  private EmacsDrawable drawable;
+  private EmacsGC immutableGC;
+  private Path path;
+
+  private static Xfermode xorAlu, srcInAlu;
+
+  static
+  {
+    xorAlu = new PorterDuffXfermode (Mode.XOR);
+    srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
+  };
+
+  public
+  EmacsFillPolygon (EmacsDrawable drawable, Point points[],
+                   EmacsGC immutableGC)
+  {
+    int i;
+
+    this.drawable = drawable;
+    this.immutableGC = immutableGC;
+
+    /* 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 ();
+      }
+  }
+
+  @Override
+  public Rect
+  getRect ()
+  {
+    RectF rect;
+
+    rect = new RectF (0, 0, 0, 0);
+    path.computeBounds (rect, true);
+
+    return new Rect ((int) Math.floor (rect.left),
+                    (int) Math.floor (rect.top),
+                    (int) Math.ceil (rect.right),
+                    (int) Math.ceil (rect.bottom));
+  }
+
+  @Override
+  public EmacsDrawable
+  getDrawable ()
+  {
+    return drawable;
+  }
+
+  @Override
+  public EmacsGC
+  getGC ()
+  {
+    return immutableGC;
+  }
+
+  @Override
+  public void
+  paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
+  {
+    int alu;
+    Paint maskPaint;
+    Canvas maskCanvas;
+    Bitmap maskBitmap;
+    Rect rect;
+
+    /* TODO implement stippling.  */
+    if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+      return;
+
+    alu = immutableGC.function;
+    rect = getRect ();
+
+    if (alu == EmacsGC.GC_COPY)
+      paint.setXfermode (null);
+    else
+      paint.setXfermode (xorAlu);
+
+    paint.setStyle (Paint.Style.FILL);
+
+    if (immutableGC.clip_mask == null)
+      {
+       paint.setColor (immutableGC.foreground | 0xff000000);
+       canvas.drawPath (path, paint);
+      }
+    else
+      {
+       maskPaint = new Paint ();
+       maskBitmap = immutableGC.clip_mask.bitmap;
+       maskBitmap = maskBitmap.copy (Bitmap.Config.ARGB_8888,
+                                     true);
+
+       if (maskBitmap == null)
+         return;
+
+       maskPaint.setXfermode (srcInAlu);
+       maskPaint.setColor (immutableGC.foreground | 0xff000000);
+       maskCanvas = new Canvas (maskBitmap);
+       path.offset (-rect.left, -rect.top, null);
+       maskCanvas.drawPath (path, maskPaint);
+       canvas.drawBitmap (maskBitmap, new Rect (0, 0, rect.width (),
+                                                rect.height ()),
+                          rect, paint);
+      }
+  }
+}
diff --git a/java/org/gnu/emacs/EmacsFillRectangle.java 
b/java/org/gnu/emacs/EmacsFillRectangle.java
new file mode 100644
index 0000000000..7246c13de7
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsFillRectangle.java
@@ -0,0 +1,129 @@
+/* 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.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.Xfermode;
+
+import android.util.Log;
+
+public class EmacsFillRectangle implements EmacsPaintReq
+{
+  private int x, y, width, height;
+  private EmacsDrawable drawable;
+  private EmacsGC immutableGC;
+  private static Xfermode xorAlu, srcInAlu;
+
+  static
+  {
+    xorAlu = new PorterDuffXfermode (Mode.XOR);
+    srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
+  };
+
+  public
+  EmacsFillRectangle (EmacsDrawable drawable, int x, int y,
+                     int width, int height,
+                     EmacsGC immutableGC)
+  {
+    this.drawable = drawable;
+    this.x = x;
+    this.y = y;
+    this.width = width;
+    this.height = height;
+    this.immutableGC = immutableGC;
+  }
+
+  @Override
+  public Rect
+  getRect ()
+  {
+    return new Rect (x, y, x + width, y + height);
+  }
+
+  @Override
+  public EmacsDrawable
+  getDrawable ()
+  {
+    return drawable;
+  }
+
+  @Override
+  public EmacsGC
+  getGC ()
+  {
+    return immutableGC;
+  }
+
+  @Override
+  public void
+  paintTo (Canvas canvas, Paint paint, EmacsGC immutableGC)
+  {
+    int alu;
+    Paint maskPaint;
+    Canvas maskCanvas;
+    Bitmap maskBitmap;
+    Rect rect, srcRect;
+
+    /* TODO implement stippling.  */
+    if (immutableGC.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+      return;
+
+    alu = immutableGC.function;
+    rect = getRect ();
+
+    paint.setStyle (Paint.Style.FILL);
+
+    if (alu == EmacsGC.GC_COPY)
+      paint.setXfermode (null);
+    else
+      paint.setXfermode (xorAlu);
+
+    if (immutableGC.clip_mask == null)
+      {
+        paint.setColor (immutableGC.foreground | 0xff000000);
+       canvas.drawRect (rect, paint);
+      }
+    else
+      {
+       maskPaint = new Paint ();
+       maskBitmap
+         = immutableGC.clip_mask.bitmap.copy (Bitmap.Config.ARGB_8888,
+                                              true);
+
+       if (maskBitmap == null)
+         return;
+
+       maskPaint.setXfermode (srcInAlu);
+       maskPaint.setColor (immutableGC.foreground | 0xff000000);
+       maskCanvas = new Canvas (maskBitmap);
+       srcRect = new Rect (0, 0, maskBitmap.getWidth (),
+                           maskBitmap.getHeight ());
+       maskCanvas.drawRect (srcRect, maskPaint);
+       canvas.drawBitmap (maskBitmap, srcRect, rect, paint);
+      }
+
+    paint.setXfermode (null);
+  }
+}
diff --git a/java/org/gnu/emacs/EmacsFontDriver.java 
b/java/org/gnu/emacs/EmacsFontDriver.java
new file mode 100644
index 0000000000..f419e71059
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsFontDriver.java
@@ -0,0 +1,150 @@
+/* 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.util.List;
+
+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 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;
+
+    @Override
+    public String
+    toString ()
+    {
+      return ("foundry: " + foundry
+             + " family: " + family
+             + " adstyle: " + adstyle
+             + " registry: " + registry
+             + " width: " + width
+             + " weight: " + weight
+             + " slant: " + slant
+             + " spacing: " + spacing
+             + " avgwidth: " + avgwidth);
+    }
+  };
+
+  public class FontMetrics
+  {
+    public short lbearing;
+    public short rbearing;
+    public short width;
+    public short ascent;
+    public short descent;
+  }
+
+  public 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 static EmacsFontDriver
+  createFontDriver ()
+  {
+    return new EmacsSdk7FontDriver ();
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsGC.java b/java/org/gnu/emacs/EmacsGC.java
new file mode 100644
index 0000000000..0becb04519
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsGC.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.Rect;
+
+/* X like graphics context structures.  Keep the enums in synch with
+   androidgui.h! */
+
+public 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 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[];
+  public EmacsPixmap clip_mask, stipple;
+  private boolean dirty;
+  private EmacsGC immutableGC;
+
+  /* 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 = 0xffffffff;
+  }
+
+  public
+  EmacsGC (EmacsGC source)
+  {
+    super ((short) 0);
+
+    int i;
+
+    function = source.function;
+    fill_style = source.fill_style;
+    foreground = source.foreground;
+    background = source.background;
+    clip_x_origin = source.clip_x_origin;
+    clip_y_origin = source.clip_y_origin;
+    clip_rects = source.clip_rects;
+    clip_mask = source.clip_mask;
+    stipple = source.stipple;
+    ts_origin_x = source.ts_origin_x;
+    ts_origin_y = source.ts_origin_y;
+
+    /* Offset all the clip rects by ts_origin_x and ts_origin_y.  */
+
+    if ((ts_origin_x != 0 || ts_origin_y != 0)
+       && clip_rects != null)
+      {
+       clip_rects = new Rect[clip_rects.length];
+
+       for (i = 0; i < clip_rects.length; ++i)
+         {
+           clip_rects[i] = new Rect (source.clip_rects[i]);
+           clip_rects[i].offset (ts_origin_x,
+                                 ts_origin_y);
+         }
+      }
+  }
+
+  /* Mark this GC as dirty.  This means immutableGC will return a new
+     copy of this GC the next time it is called.  */
+
+  public void
+  markDirty ()
+  {
+    dirty = true;
+  }
+
+  public EmacsGC
+  immutableGC ()
+  {
+    if (immutableGC == null || dirty)
+      {
+       immutableGC = new EmacsGC (this);
+       dirty = false;
+      }
+
+    return immutableGC;
+  };
+};
diff --git a/java/org/gnu/emacs/EmacsHandleObject.java 
b/java/org/gnu/emacs/EmacsHandleObject.java
new file mode 100644
index 0000000000..a57a3bbdfa
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsHandleObject.java
@@ -0,0 +1,62 @@
+/* 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 java.lang.Object;
+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/EmacsNative.java 
b/java/org/gnu/emacs/EmacsNative.java
new file mode 100644
index 0000000000..6550e6fa2a
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -0,0 +1,72 @@
+/* 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.System;
+
+import android.content.res.AssetManager;
+
+public class EmacsNative
+{
+  /* Set certain parameters before initializing Emacs.  This proves
+     that libemacs.so is being loaded from Java code.
+
+     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.
+
+     emacsService must be the emacsService singleton.  */
+  public static native void setEmacsParams (AssetManager assetManager,
+                                           String filesDir,
+                                           String libDir,
+                                           EmacsService emacsService);
+
+  /* Initialize Emacs with the argument array ARGV.  Each argument
+     must contain a NULL terminated string, or else the behavior is
+     undefined.  */
+  public static native void initEmacs (String argv[]);
+
+  /* Abort and generate a native core dump.  */
+  public static native void emacsAbort ();
+
+  /* Send an ANDROID_CONFIGURE_NOTIFY event.  */
+  public static native void sendConfigureNotify (short window, long time,
+                                                int x, int y, int width,
+                                                int height);
+
+  /* Send an ANDROID_KEY_PRESS event.  */
+  public static native void sendKeyPress (short window, long time, int state,
+                                         int keyCode);
+
+  /* Send an ANDROID_KEY_RELEASE event.  */
+  public static native void sendKeyRelease (short window, long time, int state,
+                                           int keyRelease);
+
+  static
+  {
+    System.loadLibrary ("emacs");
+  };
+};
diff --git a/java/org/gnu/emacs/EmacsPaintQueue.java 
b/java/org/gnu/emacs/EmacsPaintQueue.java
new file mode 100644
index 0000000000..5af5868d3b
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsPaintQueue.java
@@ -0,0 +1,138 @@
+/* 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.LinkedList;
+import java.util.List;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+public class EmacsPaintQueue
+{
+  /* Queue of paint operations.  This is modified from the Emacs
+     thread, and entire paint queues are periodically flushed to the
+     application thread where it is executed.  */
+  private List<EmacsPaintReq> paintOperations;
+
+  /* Number of operations in this queue.  */
+  public int numRequests;
+
+  public
+  EmacsPaintQueue ()
+  {
+    paintOperations = new LinkedList<EmacsPaintReq> ();
+  }
+
+  public void
+  run ()
+  {
+    EmacsDrawable drawable, last;
+    Canvas canvas;
+    EmacsGC gc, lastGC;
+    int i;
+    Paint paint;
+    Rect rect, offsetRect, copyRect;
+
+    canvas = null;
+    last = null;
+    gc = null;
+    paint = new Paint ();
+
+    for (EmacsPaintReq req : paintOperations)
+      {
+       drawable = req.getDrawable ();
+
+       synchronized (req)
+         {
+           /* Ignore graphics requests for drawables that have been
+              destroyed.  */
+           if (drawable.isDestroyed ())
+             continue;
+         }
+
+       canvas = drawable.lockCanvas ();
+
+       if (canvas == null)
+         /* No canvas is currently available.  */
+         continue;
+
+       lastGC = gc;
+       gc = req.getGC ();
+       rect = req.getRect ();
+
+       if (gc.clip_rects == null)
+         {
+           /* No clipping is applied.  Just draw and continue.  */
+           canvas.save ();
+           req.paintTo (canvas, paint, gc);
+           canvas.restore ();
+           drawable.damageRect (rect);
+           continue;
+         }
+
+       if (gc.clip_rects != null && gc.clip_rects.length > 0)
+         {
+           canvas.save ();
+
+           if (gc.clip_rects.length == 1)
+             {
+               /* There is only a single clip rect, which is simple
+                  enough.  */
+               canvas.clipRect (gc.clip_rects[0]);
+               req.paintTo (canvas, paint, gc);
+             }
+           else
+             {
+               /* There are multiple clip rects.  Android doesn't let
+                  programs use RegionOp.UNION on the clip rectangle,
+                  so Emacs must iterate over each intersection and
+                  paint it manually.  This seems inefficient but
+                  thankfully Emacs never seems to use more than one
+                  clip rect.  */
+
+               for (i = 0; i < gc.clip_rects.length; ++i)
+                 {
+                   copyRect = new Rect (gc.clip_rects[i]);
+
+                   if (copyRect.intersect (rect))
+                     {
+                       canvas.save ();
+                       canvas.clipRect (copyRect);
+                       req.paintTo (canvas, paint, gc);
+                       canvas.restore ();
+                     }
+                 }
+             }
+
+           drawable.damageRect (rect);
+           canvas.restore ();
+         }
+      }
+  }
+
+  public void
+  appendPaintOperation (EmacsPaintReq req)
+  {
+    paintOperations.add (req);
+    numRequests++;
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsPaintReq.java 
b/java/org/gnu/emacs/EmacsPaintReq.java
new file mode 100644
index 0000000000..5b14b00509
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsPaintReq.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;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+public interface EmacsPaintReq
+{
+  public EmacsDrawable getDrawable ();
+  public EmacsGC getGC ();
+  public void paintTo (Canvas canvas, Paint paint,
+                      EmacsGC immutableGC);
+  public Rect getRect ();
+};
diff --git a/java/org/gnu/emacs/EmacsPixmap.java 
b/java/org/gnu/emacs/EmacsPixmap.java
new file mode 100644
index 0000000000..ccd5f1e004
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsPixmap.java
@@ -0,0 +1,102 @@
+/* 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;
+
+/* Drawable backed by bitmap.  */
+
+public 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;
+
+  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;
+  }
+
+  @Override
+  public Canvas
+  lockCanvas ()
+  {
+    if (canvas == null)
+      canvas = new Canvas (bitmap);
+
+    return canvas;
+  }
+
+  @Override
+  public void
+  unlockCanvas ()
+  {
+
+  }
+
+  @Override
+  public void
+  damageRect (Rect damageRect)
+  {
+
+  }
+
+  @Override
+  public Bitmap
+  getBitmap ()
+  {
+    return bitmap;
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java 
b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
new file mode 100644
index 0000000000..5a8cdbfc75
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
@@ -0,0 +1,460 @@
+/* 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.io.IOException;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+
+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";
+
+  private 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;
+
+      typefacePaint = new Paint ();
+      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;
+
+      Log.d (TAG, "Initialized new typeface " + familyName);
+    }
+
+    @Override
+    public String
+    toString ()
+    {
+      return ("Sdk7Typeface ("
+             + String.valueOf (familyName) + ", "
+             + String.valueOf (slant) + ", "
+             + String.valueOf (width) + ", "
+             + String.valueOf (weight) + ", "
+             + String.valueOf (spacing) + ")");
+    }
+  };
+
+  private class Sdk7FontEntity extends FontEntity
+  {
+    /* The typeface.  */
+    public Sdk7Typeface typeface;
+
+    public
+    Sdk7FontEntity (Sdk7Typeface typeface)
+    {
+      float width;
+
+      foundry = "Google";
+      family = typeface.familyName;
+      adstyle = null;
+      weight = typeface.weight;
+      slant = typeface.slant;
+      spacing = typeface.spacing;
+      width = typeface.width;
+
+      this.typeface = typeface;
+    }
+  };
+
+  private class Sdk7FontObject extends FontObject
+  {
+    /* The typeface.  */
+    public Sdk7Typeface typeface;
+
+    /* The text size.  */
+    public int pixelSize;
+
+    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;
+
+      /* 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 ();
+    typefaceList = new Sdk7Typeface[fontFamilyList.length];
+
+    /* 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);
+      }
+
+    fallbackTypeface = new Sdk7Typeface ("monospace",
+                                        Typeface.MONOSPACE);
+  }
+
+  private boolean
+  checkMatch (Sdk7Typeface typeface, FontSpec fontSpec)
+  {
+    if (fontSpec.family != null
+       && !fontSpec.family.equals (typeface.familyName))
+      return false;
+
+
+    if (fontSpec.adstyle != null
+       && !fontSpec.adstyle.isEmpty ())
+      /* 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> ();
+
+    Log.d (TAG, ("Looking for fonts matching font spec: "
+                + fontSpec.toString ()));
+
+    for (i = 0; i < typefaceList.length; ++i)
+      {
+       if (checkMatch (typefaceList[i], fontSpec))
+         list.add (new Sdk7FontEntity (typefaceList[i]));
+      }
+
+    Log.d (TAG, "Found font entities: " + list.toString ());
+
+    return (FontEntity[]) 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, emGlyphWidth, 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);
+    emGlyphWidth = paint.measureText (EM_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) ? 1 : 0;
+  }
+
+  private void
+  textExtents1 (Sdk7FontObject font, int code, FontMetrics metrics,
+               Paint paint, Rect bounds)
+  {
+    char[] text;
+    float[] width;
+
+    text = new char[1];
+    text[0] = (char) code;
+
+    paint.getTextBounds (text, 0, 1, bounds);
+    width = new float[1];
+    paint.getTextWidths (text, 0, 1, width);
+
+    /* 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) Math.round (width[0]);
+  }
+
+  @Override
+  public void
+  textExtents (FontObject font, int code[], FontMetrics fontMetrics[])
+  {
+    int i;
+    Paint paintCache;
+    Rect boundsCache;
+    Sdk7FontObject fontObject;
+
+    fontObject = (Sdk7FontObject) font;
+    paintCache = fontObject.typeface.typefacePaint;
+    paintCache.setTextSize (fontObject.pixelSize);
+    boundsCache = new Rect ();
+
+    for (i = 0; i < code.length; ++i)
+      textExtents1 ((Sdk7FontObject) font, code[i], fontMetrics[i],
+                   paintCache, boundsCache);
+  }
+
+  @Override
+  public int
+  encodeChar (FontObject fontObject, char charCode)
+  {
+    return charCode;
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsService.java 
b/java/org/gnu/emacs/EmacsService.java
new file mode 100644
index 0000000000..311226e6f7
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -0,0 +1,384 @@
+/* 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.Runnable;
+import java.io.IOException;
+import java.util.List;
+import java.util.ArrayList;
+
+import android.graphics.Canvas;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+
+import android.annotation.TargetApi;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.AssetManager;
+import android.os.Build;
+import android.os.Looper;
+import android.os.IBinder;
+import android.os.Handler;
+import android.util.Log;
+
+class Holder<T>
+{
+  T thing;
+};
+
+/* EmacsService is the service that starts the thread running Emacs
+   and handles requests by that Emacs instance.  */
+
+public class EmacsService extends Service
+{
+  public static final String TAG = "EmacsService";
+  public static final int MAX_PENDING_REQUESTS = 256;
+  public static volatile EmacsService SERVICE;
+
+  private EmacsThread thread;
+  private Handler handler;
+  private EmacsPaintQueue paintQueue;
+
+  /* List of all EmacsWindows that are available to attach to an
+     activity.  */
+  public static List<EmacsWindow> availableChildren;
+
+  static
+  {
+    availableChildren = new ArrayList<EmacsWindow> ();
+  };
+
+  @Override
+  public int
+  onStartCommand (Intent intent, int flags, int startId)
+  {
+    return START_NOT_STICKY;
+  }
+
+  @Override
+  public IBinder
+  onBind (Intent intent)
+  {
+    return null;
+  }
+
+  @TargetApi (Build.VERSION_CODES.GINGERBREAD)
+  String
+  getLibraryDirectory ()
+  {
+    int apiLevel;
+    Context context;
+
+    context = getApplicationContext ();
+    apiLevel = android.os.Build.VERSION.SDK_INT;
+
+    if (apiLevel >= Build.VERSION_CODES.GINGERBREAD)
+      return context.getApplicationInfo().nativeLibraryDir;
+    else if (apiLevel >= Build.VERSION_CODES.DONUT)
+      return context.getApplicationInfo().dataDir + "/lib";
+
+    return "/data/data/" + context.getPackageName() + "/lib";
+  }
+
+  @Override
+  public void
+  onCreate ()
+  {
+    AssetManager manager;
+    Context app_context;
+    String filesDir, libDir;
+
+    SERVICE = this;
+    handler = new Handler (Looper.getMainLooper ());
+    manager = getAssets ();
+    app_context = getApplicationContext ();
+
+    try
+      {
+       /* Configure Emacs with the asset manager and other necessary
+          parameters.  */
+       filesDir = app_context.getFilesDir ().getCanonicalPath ();
+       libDir = getLibraryDirectory ();
+
+       Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir
+              + " and libDir = " + libDir);
+
+       EmacsNative.setEmacsParams (manager, filesDir, libDir,
+                                   this);
+
+       /* Start the thread that runs Emacs.  */
+       thread = new EmacsThread (this);
+       thread.start ();
+      }
+    catch (IOException exception)
+      {
+       EmacsNative.emacsAbort ();
+       return;
+      }
+  }
+
+
+
+  /* Functions from here on must only be called from the Emacs
+     thread.  */
+
+  void
+  runOnUiThread (Runnable runnable)
+  {
+    handler.post (runnable);
+  }
+
+  EmacsView
+  getEmacsView (final EmacsWindow window)
+  {
+    Runnable runnable;
+    final Holder<EmacsView> view;
+
+    view = new Holder<EmacsView> ();
+
+    runnable = new Runnable () {
+       public void
+       run ()
+       {
+         synchronized (this)
+           {
+             view.thing = new EmacsView (window);
+             notify ();
+           }
+       }
+      };
+
+    synchronized (runnable)
+      {
+       runOnUiThread (runnable);
+
+       try
+         {
+           runnable.wait ();
+         }
+       catch (InterruptedException e)
+         {
+           EmacsNative.emacsAbort ();
+         }
+      }
+
+    return view.thing;
+  }
+
+  /* Notice that a child of the root window named WINDOW is now
+     available for attachment to a specific activity.  */
+
+  public void
+  noticeAvailableChild (final EmacsWindow window)
+  {
+    Log.d (TAG, "A new child is available: " + window);
+
+    handler.post (new Runnable () {
+       public void
+       run ()
+       {
+         for (EmacsActivity activity
+                : EmacsActivity.availableActivities)
+           {
+             /* TODO: check if the activity matches.  */
+             activity.attachChild (window);
+             break;
+           }
+
+         /* Nope, wait for an activity to become available.  */
+         availableChildren.add (window);
+       }
+      });
+  }
+
+  /* Notice that a child of the root window named WINDOW has been
+     destroyed.  */
+
+  public void
+  noticeChildDestroyed (final EmacsWindow child)
+  {
+    handler.post (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         availableChildren.remove (child);
+       }
+      });
+  }
+
+  /* X drawing operations.  These are quite primitive operations.  The
+     drawing queue is kept on the Emacs thread, but is periodically
+     flushed to the application thread, upon buffers swaps and once it
+     gets too big.  */
+
+
+
+  private void
+  ensurePaintQueue ()
+  {
+    if (paintQueue == null)
+      paintQueue = new EmacsPaintQueue ();
+  }
+
+  public void
+  flushPaintQueue ()
+  {
+    final EmacsPaintQueue queue;
+
+    if (paintQueue == null)
+      return;
+
+    if (paintQueue.numRequests < 1)
+      /* No requests to flush.  */
+      return;
+
+    queue = paintQueue;
+
+    handler.post (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         queue.run ();
+       }
+      });
+
+    /* Clear the paint queue.  */
+    paintQueue = null;
+  }
+
+  private void
+  checkFlush ()
+  {
+    if (paintQueue != null
+       && paintQueue.numRequests > MAX_PENDING_REQUESTS)
+      flushPaintQueue ();
+  }
+
+  public void
+  fillRectangle (EmacsDrawable drawable, EmacsGC gc,
+                int x, int y, int width, int height)
+  {
+    EmacsPaintReq req;
+
+    ensurePaintQueue ();
+
+    req = new EmacsFillRectangle (drawable, x, y,
+                                 width, height,
+                                 gc.immutableGC ());
+    paintQueue.appendPaintOperation (req);
+    checkFlush ();
+  }
+
+  public void
+  fillPolygon (EmacsDrawable drawable, EmacsGC gc,
+              Point points[])
+  {
+    EmacsPaintReq req;
+
+    ensurePaintQueue ();
+
+    req = new EmacsFillPolygon (drawable, points,
+                               gc.immutableGC ());
+    paintQueue.appendPaintOperation (req);
+    checkFlush ();
+  }
+
+  public void
+  drawRectangle (EmacsDrawable drawable, EmacsGC gc,
+                int x, int y, int width, int height)
+  {
+    EmacsPaintReq req;
+
+    ensurePaintQueue ();
+
+    if (gc.clip_rects != null && gc.clip_rects.length >= 1)
+      android.util.Log.d ("drawRectangle",
+                         gc.clip_rects[0].toString ()
+                         + " " + gc.toString ());
+
+    req = new EmacsDrawRectangle (drawable, x, y,
+                                 width, height,
+                                 gc.immutableGC ());
+    paintQueue.appendPaintOperation (req);
+    checkFlush ();
+  }
+
+  public void
+  drawLine (EmacsDrawable drawable, EmacsGC gc,
+           int x, int y, int x2, int y2)
+  {
+    EmacsPaintReq req;
+
+    ensurePaintQueue ();
+
+    req = new EmacsDrawLine (drawable, x, y,
+                            x2, y2,
+                            gc.immutableGC ());
+    paintQueue.appendPaintOperation (req);
+    checkFlush ();
+  }
+
+  public void
+  drawPoint (EmacsDrawable drawable, EmacsGC gc,
+            int x, int y)
+  {
+    EmacsPaintReq req;
+
+    ensurePaintQueue ();
+
+    req = new EmacsDrawPoint (drawable, x, y,
+                             gc.immutableGC ());
+    paintQueue.appendPaintOperation (req);
+    checkFlush ();
+  }
+
+  public void
+  copyArea (EmacsDrawable srcDrawable, EmacsDrawable dstDrawable,
+           EmacsGC gc,
+           int srcX, int srcY, int width, int height, int destX,
+           int destY)
+  {
+    EmacsPaintReq req;
+
+    ensurePaintQueue ();
+
+    req = new EmacsCopyArea (srcDrawable, dstDrawable,
+                            srcX, srcY, width, height, destX,
+                            destY, gc.immutableGC ());
+    paintQueue.appendPaintOperation (req);
+    checkFlush ();
+  }
+
+  public void
+  clearWindow (EmacsWindow window)
+  {
+    window.clearWindow ();
+  }
+
+  public void
+  clearArea (EmacsWindow window, int x, int y, int width,
+            int height)
+  {
+    window.clearArea (x, y, width, height);
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java 
b/java/org/gnu/emacs/EmacsSurfaceView.java
new file mode 100644
index 0000000000..194f6ad37a
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSurfaceView.java
@@ -0,0 +1,83 @@
+/* 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.SurfaceView;
+import android.view.SurfaceHolder;
+
+import android.graphics.Canvas;
+import android.graphics.Rect;
+
+public class EmacsSurfaceView extends SurfaceView
+{
+  private boolean created;
+
+  public
+  EmacsSurfaceView (final EmacsView view)
+  {
+    super (view.getContext ());
+
+    getHolder ().addCallback (new SurfaceHolder.Callback () {
+       @Override
+       public void
+       surfaceChanged (SurfaceHolder holder, int format,
+                       int width, int height)
+       {
+
+       }
+
+       @Override
+       public void
+       surfaceCreated (SurfaceHolder holder)
+       {
+         created = true;
+
+         /* Force a buffer swap now to get the contents of the Emacs
+            view on screen.  */
+         view.swapBuffers ();
+       }
+
+       @Override
+       public void
+       surfaceDestroyed (SurfaceHolder holder)
+       {
+         created = false;
+       }
+      });
+  }
+
+  public boolean
+  isCreated ()
+  {
+    return created;
+  }
+
+  public Canvas
+  lockCanvas (Rect damage)
+  {
+    return getHolder ().lockCanvas (damage);
+  }
+
+  public void
+  unlockCanvasAndPost (Canvas canvas)
+  {
+    getHolder ().unlockCanvasAndPost (canvas);
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsThread.java 
b/java/org/gnu/emacs/EmacsThread.java
new file mode 100644
index 0000000000..e9281a1339
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsThread.java
@@ -0,0 +1,44 @@
+/* Communication module for Android terminals.  -*- c-file-style: "GNU" -*-
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+package org.gnu.emacs;
+
+import java.lang.Thread;
+
+public class EmacsThread extends Thread
+{
+  EmacsService context;
+
+  public
+  EmacsThread (EmacsService service)
+  {
+    context = service;
+  }
+
+  public void
+  run ()
+  {
+    String args[];
+
+    args = new String[] { "android-emacs", };
+
+    /* Run the native code now.  */
+    EmacsNative.initEmacs (args);
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsView.java 
b/java/org/gnu/emacs/EmacsView.java
new file mode 100644
index 0000000000..237946d636
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -0,0 +1,211 @@
+/* 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.view.KeyEvent;
+import android.view.ViewGroup;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Paint;
+import android.util.Log;
+
+import android.os.Build;
+
+/* 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 class EmacsView extends ViewGroup
+{
+  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 paint.  */
+  public Paint paint;
+
+  /* The associated surface view.  */
+  private EmacsSurfaceView surfaceView;
+
+  public
+  EmacsView (EmacsWindow window)
+  {
+    super (EmacsService.SERVICE);
+
+    this.window = window;
+    this.damageRegion = new Region ();
+    this.paint = new Paint ();
+
+    /* Create the surface view.  */
+    this.surfaceView = new EmacsSurfaceView (this);
+
+    setFocusable (FOCUSABLE);
+    addView (this.surfaceView);
+  }
+
+  @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);
+  }
+
+  @Override
+  protected void
+  onLayout (boolean changed, int left, int top, int right,
+           int bottom)
+  {
+    int count, i;
+    View child;
+    Rect windowRect;
+
+    if (changed)
+      {
+       window.viewLayout (left, top, right, bottom);
+
+       /* Recreate the front and back buffer bitmaps.  */
+       bitmap
+         = Bitmap.createBitmap (right - left, bottom - top,
+                                Bitmap.Config.ARGB_8888);
+
+       /* And canvases.  */
+       canvas = new Canvas (bitmap);
+      }
+
+    count = getChildCount ();
+
+    for (i = 0; i < count; ++i)
+      {
+       child = getChildAt (i);
+
+       if (child == surfaceView)
+         /* The child is the surface view, so give it the entire
+            view.  */
+         child.layout (left, top, right, bottom);
+       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);
+         }
+      }
+  }
+
+  public void
+  damageRect (Rect damageRect)
+  {
+    damageRegion.union (damageRect);
+  }
+
+  public void
+  swapBuffers ()
+  {
+    Bitmap back;
+    Canvas canvas;
+    Rect damageRect;
+
+    if (damageRegion.isEmpty ())
+      return;
+
+    if (!surfaceView.isCreated ())
+      return;
+
+    if (bitmap == null)
+      return;
+
+    /* Lock the canvas with the specified damage.  */
+    damageRect = damageRegion.getBounds ();
+    canvas = surfaceView.lockCanvas (damageRect);
+
+    /* Return if locking the canvas failed.  */
+    if (canvas == null)
+      return;
+
+    /* Copy from the back buffer to the canvas.  */
+    canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
+
+    /* Unlock the canvas and clear the damage.  */
+    surfaceView.unlockCanvasAndPost (canvas);
+    damageRegion.setEmpty ();
+  }
+
+  @Override
+  public boolean
+  onKeyDown (int keyCode, KeyEvent event)
+  {
+    window.onKeyDown (keyCode, event);
+    return true;
+  }
+
+  @Override
+  public boolean
+  onKeyUp (int keyCode, KeyEvent event)
+  {
+    window.onKeyUp (keyCode, event);
+    return true;
+  }
+};
diff --git a/java/org/gnu/emacs/EmacsWindow.java 
b/java/org/gnu/emacs/EmacsWindow.java
new file mode 100644
index 0000000000..28db04a261
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -0,0 +1,336 @@
+/* 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 android.graphics.Rect;
+import android.graphics.Canvas;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.KeyEvent;
+
+/* 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 class EmacsWindow extends EmacsHandleObject
+  implements EmacsDrawable
+{
+  /* 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.  */
+  private EmacsWindow parent;
+
+  /* List of all children in stacking order.  This must be kept
+     consistent!  */
+  private ArrayList<EmacsWindow> children;
+
+  /* The EmacsActivity currently attached, if it exists.  */
+  private EmacsActivity attached;
+
+  /* The window background scratch GC.  foreground is always the
+     window background.  */
+  private EmacsGC scratchGC;
+
+  public
+  EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
+              int width, int height)
+  {
+    super (handle);
+
+    rect = new Rect (x, y, x + width, y + height);
+
+    /* Create the view from the context's UI thread.  */
+    view = EmacsService.SERVICE.getEmacsView (this);
+    this.parent = parent;
+    children = new ArrayList<EmacsWindow> ();
+
+    /* The window is unmapped by default.  */
+    view.setVisibility (View.GONE);
+
+    /* If parent is the root window, notice that there are new
+       children available for interested activites to pick up.  */
+    if (parent == null)
+      EmacsService.SERVICE.noticeAvailableChild (this);
+    else
+      {
+       /* Otherwise, directly add this window as a child of that
+          window's view.  */
+       synchronized (parent)
+         {
+           parent.children.add (this);
+           parent.view.post (new Runnable () {
+               @Override
+               public void
+               run ()
+               {
+                 parent.view.addView (view);
+               }
+             });
+         }
+      }
+
+    scratchGC = new EmacsGC ((short) 0);
+  }
+
+  public void
+  changeWindowBackground (int pixel)
+  {
+    /* scratchGC is used as the argument to a FillRectangles req.  */
+    scratchGC.foreground = pixel;
+    scratchGC.markDirty ();
+  }
+
+  public Rect
+  getGeometry ()
+  {
+    synchronized (this)
+      {
+       /* Huh, this is it.  */
+       return rect;
+      }
+  }
+
+  @Override
+  public void
+  destroyHandle () throws IllegalStateException
+  {
+    synchronized (this)
+      {
+       if (!children.isEmpty ())
+         throw new IllegalStateException ("Trying to destroy window with "
+                                          + "children!");
+      }
+
+    /* Notice that the child has been destroyed.  */
+    EmacsService.SERVICE.noticeChildDestroyed (this);
+
+    /* Remove the view from its parent and make it invisible.  */
+    view.post (new Runnable () {
+       public void
+       run ()
+       {
+         view.setVisibility (View.GONE);
+
+         if (view.getParent () != null)
+           ((ViewGroup) view.getParent ()).removeView (view);
+
+         if (attached != null)
+           attached.makeAvailable ();
+       }
+      });
+
+    super.destroyHandle ();
+  }
+
+  public void
+  setActivity (EmacsActivity activity)
+  {
+    synchronized (this)
+      {
+       activity = activity;
+      }
+  }
+
+  public void
+  viewLayout (int left, int top, int right, int bottom)
+  {
+    synchronized (this)
+      {
+       rect.left = left;
+       rect.top = top;
+       rect.right = right;
+       rect.bottom = bottom;
+
+       EmacsNative.sendConfigureNotify (this.handle,
+                                        System.currentTimeMillis (),
+                                        left, top, rect.width (),
+                                        rect.height ());
+      }
+  }
+
+  public void
+  requestViewLayout ()
+  {
+    view.post (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         view.requestLayout ();
+       }
+      });
+  }
+
+  public void
+  resizeWindow (int width, int height)
+  {
+    synchronized (this)
+      {
+       rect.right = rect.left + width;
+       rect.bottom = rect.top + height;
+      }
+  }
+
+  public void
+  moveWindow (int x, int y)
+  {
+    int width, height;
+
+    synchronized (this)
+      {
+       width = rect.width ();
+       height = rect.height ();
+
+       rect.left = x;
+       rect.top = y;
+       rect.right = x + width;
+       rect.bottom = y + height;
+
+       requestViewLayout ();
+      }
+  }
+
+  public void
+  mapWindow ()
+  {
+    view.post (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         view.setVisibility (View.VISIBLE);
+       }
+      });
+  }
+
+  public void
+  unmapWindow ()
+  {
+    view.post (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         view.setVisibility (View.GONE);
+       }
+      });
+  }
+
+  @Override
+  public Canvas
+  lockCanvas ()
+  {
+    if (view.canvas != null)
+      return view.canvas;
+
+    return null;
+  }
+
+  @Override
+  public void
+  unlockCanvas ()
+  {
+
+  }
+
+  @Override
+  public void
+  damageRect (Rect damageRect)
+  {
+    view.damageRect (damageRect);
+  }
+
+  public void
+  swapBuffers ()
+  {
+    /* Before calling swapBuffers, make sure to flush the paint
+       queue.  */
+    EmacsService.SERVICE.flushPaintQueue ();
+    view.post (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         view.swapBuffers ();
+       }
+      });
+  }
+
+  public void
+  clearWindow ()
+  {
+    synchronized (this)
+      {
+       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.bitmap;
+  }
+
+  public void
+  onKeyDown (int keyCode, KeyEvent event)
+  {
+    EmacsNative.sendKeyPress (this.handle,
+                             event.getEventTime (),
+                             event.getModifiers (),
+                             keyCode);
+  }
+
+  public void
+  onKeyUp (int keyCode, KeyEvent event)
+  {
+    EmacsNative.sendKeyRelease (this.handle,
+                               event.getEventTime (),
+                               event.getModifiers (),
+                               keyCode);
+  }
+};
diff --git a/lib-src/Makefile.in b/lib-src/Makefile.in
index cfad3fc394..e3e4fefe65 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.
diff --git a/lib/Makefile.in b/lib/Makefile.in
index 8b95013624..caf8e0fbec 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -20,6 +20,130 @@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
+# This is not empty if this is a Makefile that will be copied to
+# xcompile/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_CFLAGS = @ANDROID_CFLAGS@
+
+ifneq ($(XCONFIGURE),)
+
+# Set vpath.  Only look for C files and some headers in srcdir:
+# Headers that are generated by gnulib must be spared, or otherwise
+# the versions previously built on the host will be used, if builddir
+# is the same as srcdir.  Following this is a list of files in lib/
+# that are not generated during the gnulib build process.  Please keep
+# it up to date!
+
+vpath _Noreturn.h $(srcdir)
+vpath acl-internal.h $(srcdir)
+vpath acl.h $(srcdir)
+vpath af_alg.h $(srcdir)
+vpath alloca.in.h $(srcdir)
+vpath allocator.h $(srcdir)
+vpath arg-nonnull.h $(srcdir)
+vpath assert.in.h $(srcdir)
+vpath attribute.h $(srcdir)
+vpath binary-io.h $(srcdir)
+vpath byteswap.in.h $(srcdir)
+vpath c++defs.h $(srcdir)
+vpath c-ctype.h $(srcdir)
+vpath c-strcase.h $(srcdir)
+vpath careadlinkat.h $(srcdir)
+vpath cdefs.h $(srcdir)
+vpath cloexec.h $(srcdir)
+vpath close-stream.h $(srcdir)
+vpath count-leading-zeros.h $(srcdir)
+vpath count-one-bits.h $(srcdir)
+vpath count-trailing-zeros.h $(srcdir)
+vpath diffseq.h $(srcdir)
+vpath dirent.h $(srcdir)
+vpath dirent.in.h $(srcdir)
+vpath dynarray.h $(srcdir)
+vpath eloop-threshold.h $(srcdir)
+vpath errno.in.h $(srcdir)
+vpath execinfo.in.h $(srcdir)
+vpath fcntl.in.h $(srcdir)
+vpath filemode.h $(srcdir)
+vpath filename.h $(srcdir)
+vpath filevercmp.h $(srcdir)
+vpath fingerprint.h $(srcdir)
+vpath flexmember.h $(srcdir)
+vpath fpending.h $(srcdir)
+vpath fsusage.h $(srcdir)
+vpath ftoastr.h $(srcdir)
+vpath getopt-cdefs.in.h $(srcdir)
+vpath getopt-core.h $(srcdir)
+vpath getopt-ext.h $(srcdir)
+vpath getopt-pfx-core.h $(srcdir)
+vpath getopt-pfx-ext.h $(srcdir)
+vpath getopt.in.h $(srcdir)
+vpath getopt_int.h $(srcdir)
+vpath gettext.h $(srcdir)
+vpath idx.h $(srcdir)
+vpath ieee754.in.h $(srcdir)
+vpath ignore-value.h $(srcdir)
+vpath intprops-internal.h $(srcdir)
+vpath intprops.h $(srcdir)
+vpath inttypes.in.h $(srcdir)
+vpath libc-config.h $(srcdir)
+vpath limits.in.h $(srcdir)
+vpath malloc/dynarray.h $(srcdir)
+vpath malloc/scratch_buffer.h $(srcdir)
+vpath md5.h $(srcdir)
+vpath min-max.h $(srcdir)
+vpath mini-gmp.h $(srcdir)
+vpath minmax.h $(srcdir)
+vpath mktime-internal.h $(srcdir)
+vpath nproc.h $(srcdir)
+vpath openat-priv.h $(srcdir)
+vpath openat.h $(srcdir)
+vpath pathmax.h $(srcdir)
+vpath regex.h $(srcdir)
+vpath regex_internal.h $(srcdir)
+vpath root-uid.h $(srcdir)
+vpath save-cwd.h $(srcdir)
+vpath scratch_buffer.h $(srcdir)
+vpath sha1.h $(srcdir)
+vpath sha256.h $(srcdir)
+vpath sha512.h $(srcdir)
+vpath sig2str.h $(srcdir)
+vpath signal.in.h $(srcdir)
+vpath stat-time.h $(srcdir)
+vpath stdalign.in.h $(srcdir)
+vpath stdckdint.in.h $(srcdir)
+vpath stddef.in.h $(srcdir)
+vpath stdint.in.h $(srcdir)
+vpath stdio-impl.h $(srcdir)
+vpath stdio.h $(srcdir)
+vpath stdio.in.h $(srcdir)
+vpath stdlib.in.h $(srcdir)
+vpath str-two-way.h $(srcdir)
+vpath strftime.h $(srcdir)
+vpath string.in.h $(srcdir)
+vpath sys_random.in.h $(srcdir)
+vpath sys_select.in.h $(srcdir)
+vpath sys_stat.in.h $(srcdir)
+vpath sys_time.in.h $(srcdir)
+vpath sys_types.in.h $(srcdir)
+vpath tempname.h $(srcdir)
+vpath time-internal.h $(srcdir)
+vpath time.in.h $(srcdir)
+vpath timespec.h $(srcdir)
+vpath u64.h $(srcdir)
+vpath unistd.in.h $(srcdir)
+vpath unlocked-io.h $(srcdir)
+vpath utimens.h $(srcdir)
+vpath verify.h $(srcdir)
+vpath vla.h $(srcdir)
+vpath warn-on-use.h $(srcdir)
+vpath xalloc-oversized.h $(srcdir)
+vpath %.c $(srcdir)
+endif
+
 # 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 +157,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)
+  $(if $(patsubst e-%,,$(notdir $<)),,-Demacs) $(ANDROID_CFLAGS)
 
 ifeq ($(HAVE_NATIVE_COMP),yes)
 ALL_CFLAGS += -DGL_COMPILE_CRYPTO_STREAM
@@ -52,6 +176,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)/xcompile -I.
+endif
+
 DEPDIR = deps
 ifeq ($(AUTO_DEPEND),yes)
   DEPFLAGS = -MMD -MF $(DEPDIR)/$*.d -MP
@@ -60,11 +190,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),
diff --git a/lib/faccessat.c b/lib/faccessat.c
index c1737d03a1..30a22da7eb 100644
--- a/lib/faccessat.c
+++ b/lib/faccessat.c
@@ -43,7 +43,11 @@ orig_faccessat (int fd, char const *name, int mode, int flag)
 /* Write "unistd.h" here, not <unistd.h>, otherwise OSF/1 5.1 DTK cc
    eliminates this include because of the preliminary #include <unistd.h>
    above.  */
+#ifdef __ANROID__
+#include <unistd.h>
+#else
 #include "unistd.h"
+#endif
 
 #ifndef HAVE_ACCESS
 /* Mingw lacks access, but it also lacks real vs. effective ids, so
diff --git a/lib/fpending.c b/lib/fpending.c
index 6408cff464..45daa2586a 100644
--- a/lib/fpending.c
+++ b/lib/fpending.c
@@ -33,13 +33,15 @@
 size_t
 __fpending (FILE *fp)
 {
+#if defined __ANDROID__
+  return 0;
   /* Most systems provide FILE as a struct and the necessary bitmask in
      <stdio.h>, because they need it for implementing getc() and putc() as
      fast macros.  */
-#if defined _IO_EOF_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1
+#elif defined _IO_EOF_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1
   /* GNU libc, BeOS, Haiku, Linux libc5 */
   return fp->_IO_write_ptr - fp->_IO_write_base;
-#elif defined __sferror || defined __DragonFly__ || defined __ANDROID__
+#elif defined __sferror || defined __DragonFly__
   /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin < 1.7.34, Minix 3, 
Android */
   return fp->_p - fp->_bf._base;
 #elif defined __EMX__                /* emx+gcc */
diff --git a/lib/open.c b/lib/open.c
index 170bff108e..dd7c90b3bd 100644
--- a/lib/open.c
+++ b/lib/open.c
@@ -40,7 +40,11 @@ orig_open (const char *filename, int flags, mode_t mode)
 /* Specification.  */
 /* Write "fcntl.h" here, not <fcntl.h>, otherwise OSF/1 5.1 DTK cc eliminates
    this include because of the preliminary #include <fcntl.h> above.  */
+#ifdef __ANDROID__
+#include <fnctl.h>
+#else
 #include "fcntl.h"
+#endif
 
 #include "cloexec.h"
 
diff --git a/lib/unistd.c b/lib/unistd.c
index 95978e6ad0..dcc7009115 100644
--- a/lib/unistd.c
+++ b/lib/unistd.c
@@ -18,5 +18,5 @@
 #include <config.h>
 
 #define _GL_UNISTD_INLINE _GL_EXTERN_INLINE
-#include "unistd.h"
+#include <unistd.h>
 typedef int dummy;
diff --git a/lisp/frame.el b/lisp/frame.el
index e4cd2cd8ae..62cc4d7a38 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -2137,7 +2137,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.
@@ -2202,7 +2203,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))))
@@ -2222,7 +2223,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)))))))
@@ -2242,7 +2243,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)))))))
@@ -2280,7 +2281,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))
@@ -2301,7 +2302,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))
@@ -2319,7 +2320,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))))
@@ -2332,7 +2333,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))))
@@ -2345,7 +2346,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)
@@ -2360,7 +2361,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)
@@ -2377,7 +2378,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))
diff --git a/lisp/image/wallpaper.el b/lisp/image/wallpaper.el
index f083477ddf..bc76f1a108 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/loadup.el b/lisp/loadup.el
index 2a9aff4c1f..16ad531cb6 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -306,6 +306,11 @@
       (load "term/common-win")
       (load "term/haiku-win")))
 
+(if (featurep 'android)
+    (progn
+      (load "term/common-win")
+      (load "term/android-win")))
+
 (if (or (eq system-type 'windows-nt)
         (featurep 'w32))
     (progn
diff --git a/lisp/net/eww.el b/lisp/net/eww.el
index a8a985b8de..9ffaa5cd36 100644
--- a/lisp/net/eww.el
+++ b/lisp/net/eww.el
@@ -240,7 +240,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."
@@ -248,7 +248,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."
@@ -256,7 +256,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."
@@ -264,7 +264,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/term/android-win.el b/lisp/term/android-win.el
new file mode 100644
index 0000000000..8aeabee567
--- /dev/null
+++ b/lisp/term/android-win.el
@@ -0,0 +1,62 @@
+;;; 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)
+
+(add-to-list 'display-format-alist '(".*" . android))
+
+;; 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."
+  ;; 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 (_ignored &context (window-system android))
+  ;; Nothing to do here: Android has no command line to provide
+  ;; arguments on.
+  (ignore))
+
+(provide 'android-win)
+;; android-win.el ends here.
diff --git a/src/Makefile.in b/src/Makefile.in
index da11e130b2..b0ba9825a6 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -33,6 +33,16 @@ 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
+# xcompile/src.
+XCONFIGURE = @XCONFIGURE@
+
+ifneq ($(XCONFIGURE),)
+vpath %.c := $(srcdir)
+vpath %.h := $(srcdir)
+endif
+
 CC = @CC@
 CXX = @CXX@
 CFLAGS = @CFLAGS@
@@ -48,6 +58,7 @@ LIBOBJS = @LIBOBJS@
 
 lispsource = $(top_srcdir)/lisp
 lib = ../lib
+hostlib = $(top_builddir)/lib
 libsrc = ../lib-src
 etc = ../etc
 oldXMenudir = ../oldXMenu
@@ -326,7 +337,7 @@ 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@
@@ -370,6 +381,10 @@ HAIKU_CXX_OBJ = @HAIKU_CXX_OBJ@
 HAIKU_LIBS = @HAIKU_LIBS@
 HAIKU_CFLAGS = @HAIKU_CFLAGS@
 
+ANDROID_OBJ = @ANDROID_OBJ@
+ANDROID_LIBS = @ANDROID_LIBS@
+ANDROID_CFLAGS = @ANDROID_CFLAGS@
+
 DUMPING=@DUMPING@
 CHECK_STRUCTS = @CHECK_STRUCTS@
 HAVE_PDUMPER = @HAVE_PDUMPER@
@@ -411,7 +426,8 @@ 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_CFLAGS)
 ALL_CFLAGS = $(EMACS_CFLAGS) $(WARN_CFLAGS) $(CFLAGS)
 ALL_OBJC_CFLAGS = $(EMACS_CFLAGS) \
   $(filter-out $(NON_OBJC_CFLAGS),$(WARN_CFLAGS)) $(CFLAGS) \
@@ -449,7 +465,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)
 
@@ -466,7 +482,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
 
 ## gmalloc.o if !SYSTEM_MALLOC && !DOUG_LEA_MALLOC, else empty.
 GMALLOC_OBJ=@GMALLOC_OBJ@
@@ -569,7 +586,8 @@ 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,
@@ -658,7 +676,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
@@ -719,6 +737,27 @@ ifeq ($(DUMPING),unexec)
   endif
 endif
 
+ifeq ($(XCONFIGURE),android)
+## The Android package internally links to and communicates with a
+## shared library named `libemacs.so' at startup.  This is built
+## almost the same way temacs is.  But it is position independent.  It
+## is not dumped here.  Instead, it dumps itself the first time it
+## starts on the user's device.
+
+libemacs.so: $(ALLOBJS) $(LIBEGNU_ARCHIVE) $(EMACSRES) \
+            $(MAKE_PDUMPER_FINGERPRINT)
+       $(AM_V_CCLD)$(CC) -o $@ $(ALL_CFLAGS) $(TEMACS_LDFLAGS) \
+         $(LDFLAGS) -shared $(ALLOBJS) $(LIBEGNU_ARCHIVE) $(LIBES)
+       $(AM_V_at)$(MAKE_PDUMPER_FINGERPRINT) $@
+
+# There is also a binary named `android-emacs' which simply calls
+# emacs.so.
+
+android-emacs: libemacs.so android-emacs.o
+       $(AM_V_CCLD)$(CC) -o $@ $(ALL_CFLAGS) $(LDFLAGS)        \
+         -L. "-l:libemacs.so" android-emacs.o
+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
@@ -747,6 +786,7 @@ ns-app: emacs$(EXEEXT) $(pdmp)
 .PHONY: versionclean
 
 mostlyclean:
+       rm -f aemacs emacs.so
        rm -f temacs$(EXEEXT) core ./*.core \#* ./*.o
        rm -f dmpstruct.h
        rm -f emacs.pdmp
diff --git a/src/alloc.c b/src/alloc.c
index e7edc0595b..b8d326f440 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -3342,6 +3342,14 @@ 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)
+       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));
@@ -6467,6 +6475,10 @@ garbage_collect (void)
   mark_xselect ();
 #endif
 
+#ifdef HAVE_ANDROID
+  mark_androidterm ();
+#endif
+
 #ifdef HAVE_NS
   mark_nsterm ();
 #endif
diff --git a/src/android-emacs.c b/src/android-emacs.c
new file mode 100644
index 0000000000..d4fa14e39f
--- /dev/null
+++ b/src/android-emacs.c
@@ -0,0 +1,30 @@
+/* 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 "android.h"
+
+/* android-emacs is a wrapper around libemacs.  It simply calls
+   android_emacs_init with the argv and argc given to it.  */
+
+int
+main (int argc, char **argv)
+{
+  return android_emacs_init (argc, argv);
+}
diff --git a/src/android.c b/src/android.c
new file mode 100644
index 0000000000..dd841cf383
--- /dev/null
+++ b/src/android.c
@@ -0,0 +1,2335 @@
+/* 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 <sys/stat.h>
+#include <sys/mman.h>
+
+#include <assert.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/asset_manager.h>
+#include <android/asset_manager_jni.h>
+#include <android/log.h>
+
+#include <linux/ashmem.h>
+
+#define ANDROID_THROW(env, class, msg)                                 \
+  ((*(env))->ThrowNew ((env), (*(env))->FindClass ((env), class), msg))
+
+#define ANDROID_MAX_ASSET_FD 65535
+
+struct android_fd_table_entry
+{
+  /* Various flags associated with this table.  */
+  short flags;
+
+  /* The stat buffer associated with this entry.  */
+  struct stat statb;
+};
+
+enum android_fd_table_entry_flags
+  {
+    ANDROID_FD_TABLE_ENTRY_IS_VALID = 1,
+  };
+
+struct android_emacs_service
+{
+  jclass class;
+  jmethodID fill_rectangle;
+  jmethodID fill_polygon;
+  jmethodID draw_rectangle;
+  jmethodID draw_line;
+  jmethodID draw_point;
+  jmethodID copy_area;
+  jmethodID clear_window;
+  jmethodID clear_area;
+};
+
+struct android_emacs_pixmap
+{
+  jclass class;
+  jmethodID constructor;
+};
+
+struct android_graphics_point
+{
+  jclass class;
+  jmethodID constructor;
+};
+
+/* The asset manager being used.  */
+static AAssetManager *asset_manager;
+
+/* Whether or not Emacs has been initialized.  */
+static int emacs_initialized;
+
+/* The path used to store site-lisp.  */
+char *android_site_load_path;
+
+/* The path used to store native libraries.  */
+char *android_lib_dir;
+
+/* The Android application data directory.  */
+static char *android_files_dir;
+
+/* Array of structures used to hold asset information corresponding to
+   a file descriptor.  This assumes Emacs does not do funny things
+   with dup.  It currently does not.  */
+static struct android_fd_table_entry android_table[ANDROID_MAX_ASSET_FD];
+
+/* 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.  */
+static jobject emacs_service;
+
+/* Various methods associated with the EmacsService.  */
+static 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;
+
+
+
+/* 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 *volatile 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 variable for the writing side.  */
+  pthread_cond_t write_var;
+
+  /* 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.  */
+  volatile int num_events;
+
+  /* Circular queue of events.  */
+  struct android_event_container events;
+};
+
+/* Arguments to pselect used by the select thread.  */
+static volatile int android_pselect_nfds;
+static fd_set *volatile android_pselect_readfds;
+static fd_set *volatile android_pselect_writefds;
+static fd_set *volatile android_pselect_exceptfds;
+static struct timespec *volatile android_pselect_timeout;
+static const sigset_t *volatile android_pselect_sigset;
+
+/* Value of pselect.  */
+static int android_pselect_rc;
+
+/* Whether or not pselect finished.  */
+static volatile bool android_pselect_completed;
+
+/* The global event queue.  */
+static struct android_event_queue event_queue;
+
+/* Semaphore used to signal select completion.  */
+static sem_t android_pselect_sem;
+
+static void *
+android_run_select_thread (void *data)
+{
+  sigset_t signals;
+  int sig, rc;
+
+  sigfillset (&signals);
+
+  if (pthread_sigmask (SIG_BLOCK, &signals, NULL))
+    __android_log_print (ANDROID_LOG_FATAL, __func__,
+                        "pthread_sigmask: %s",
+                        strerror (errno));
+
+  sigemptyset (&signals);
+  sigaddset (&signals, SIGUSR1);
+
+  if (pthread_sigmask (SIG_UNBLOCK, &signals, NULL))
+    __android_log_print (ANDROID_LOG_FATAL, __func__,
+                        "pthread_sigmask: %s",
+                        strerror (errno));
+
+  sigemptyset (&signals);
+  sigaddset (&signals, SIGUSR2);
+
+  while (true)
+    {
+      /* Keep waiting for SIGUSR2, ignoring EINTR in the meantime.  */
+
+      while (sigwait (&signals, &sig))
+       /* Spin.  */;
+
+      /* 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,
+                   android_pselect_sigset);
+      android_pselect_rc = rc;
+      pthread_mutex_unlock (&event_queue.select_mutex);
+
+      /* 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);
+
+      pthread_mutex_lock (&event_queue.mutex);
+      android_pselect_completed = true;
+      pthread_cond_signal (&event_queue.read_var);
+      pthread_mutex_unlock (&event_queue.mutex);
+    }
+}
+
+static void
+android_handle_sigusr1 (int sig, siginfo_t *siginfo, void *arg)
+{
+  /* Nothing to do here, this signal handler is only installed to make
+     sure the disposition of SIGUSR1 is enough.  */
+}
+
+/* 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.write_var, NULL))
+    __android_log_print (ANDROID_LOG_FATAL, __func__,
+                        "pthread_cond_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);
+
+  event_queue.events.next = &event_queue.events;
+  event_queue.events.last = &event_queue.events;
+
+  /* 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;
+
+  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;
+}
+
+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);
+
+  /* Signal that events can now be written.  */
+  pthread_cond_signal (&event_queue.write_var);
+  pthread_mutex_unlock (&event_queue.mutex);
+}
+
+static void
+android_write_event (union android_event *event)
+{
+  struct android_event_container *container;
+
+  container = malloc (sizeof *container);
+
+  if (!container)
+    return;
+
+  pthread_mutex_lock (&event_queue.mutex);
+
+  /* The event queue is full, wait for events to be read.  */
+  if (event_queue.num_events >= 1024)
+    pthread_cond_wait (&event_queue.write_var,
+                      &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_signal (&event_queue.read_var);
+  pthread_mutex_unlock (&event_queue.mutex);
+}
+
+int
+android_select (int nfds, fd_set *readfds, fd_set *writefds,
+               fd_set *exceptfds, struct timespec *timeout,
+               const sigset_t *sigset)
+{
+  int nfds_return;
+
+  pthread_mutex_lock (&event_queue.mutex);
+
+  if (event_queue.num_events)
+    {
+      pthread_mutex_unlock (&event_queue.mutex);
+      return 1;
+    }
+
+  nfds_return = 0;
+  android_pselect_completed = false;
+
+  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;
+  android_pselect_sigset = sigset;
+  pthread_mutex_unlock (&event_queue.select_mutex);
+
+  pthread_kill (event_queue.select_thread, SIGUSR2);
+  pthread_cond_wait (&event_queue.read_var, &event_queue.mutex);
+
+  /* Interrupt the select thread now, in case it's still in
+     pselect.  */
+  pthread_kill (event_queue.select_thread, SIGUSR1);
+
+  /* Wait for pselect to return in any case.  */
+  sem_wait (&android_pselect_sem);
+
+  /* If there are now events in the queue, return 1.  */
+  if (event_queue.num_events)
+    nfds_return = 1;
+
+  /* Add the return value of pselect.  */
+  if (android_pselect_rc >= 0)
+    nfds_return += android_pselect_rc;
+
+  if (!nfds_return && android_pselect_rc < 0)
+    nfds_return = android_pselect_rc;
+
+  /* Unlock the event queue mutex.  */
+  pthread_mutex_unlock (&event_queue.mutex);
+
+  return nfds_return;
+}
+
+
+
+static void *
+android_run_debug_thread (void *data)
+{
+  FILE *file;
+  int fd;
+  char *line;
+  size_t n;
+
+  fd = (int) 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)
+{
+  if (!pw->pw_gecos)
+    return (char *) "Android user";
+
+  return pw->pw_gecos;
+}
+
+/* Given a real file name, return the part that describes its asset
+   path, or NULL if it is not an asset.  */
+
+static const char *
+android_get_asset_name (const char *filename)
+{
+  if (!strcmp (filename, "/assets") || !strcmp (filename, "/assets/"))
+    return "/";
+
+  if (!strncmp (filename, "/assets/", sizeof "/assets/" - 1))
+    return filename + (sizeof "/assets/" - 1);
+
+  return NULL;
+}
+
+/* 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)
+{
+  if (fd < ANDROID_MAX_ASSET_FD
+      && (android_table[fd].flags
+         & ANDROID_FD_TABLE_ENTRY_IS_VALID))
+    {
+      memcpy (statb, &android_table[fd].statb,
+             sizeof *statb);
+      return 0;
+    }
+
+  return fstat (fd, statb);
+}
+
+/* Like fstatat.  However, if dirfd is AT_FDCWD and PATHNAME is an
+   asset, find the information for the corresponding asset.  */
+
+int
+android_fstatat (int dirfd, const char *restrict pathname,
+                struct stat *restrict statbuf, int flags)
+{
+  AAsset *asset_desc;
+  const char *asset;
+
+  if (dirfd == AT_FDCWD
+      && asset_manager
+      && (asset = android_get_asset_name (pathname)))
+    {
+      /* AASSET_MODE_STREAMING is fastest here.  */
+      asset_desc = AAssetManager_open (asset_manager, asset,
+                                      AASSET_MODE_STREAMING);
+
+      if (!asset_desc)
+       return ENOENT;
+
+      memset (statbuf, 0, sizeof *statbuf);
+
+      /* Fill in the stat buffer.  */
+      statbuf->st_mode = S_IFREG;
+      statbuf->st_size = AAsset_getLength (asset_desc);
+
+      /* Close the asset.  */
+      AAsset_close (asset_desc);
+      return 0;
+    }
+
+  return fstatat (dirfd, pathname, statbuf, flags);
+}
+
+/* Return if NAME is a file that is actually an asset and is
+   accessible, as long as !(amode & W_OK).  */
+
+bool
+android_file_access_p (const char *name, int amode)
+{
+  AAsset *asset;
+  AAssetDir *directory;
+
+  if (!asset_manager)
+    return false;
+
+  if (!(amode & W_OK) && (name = android_get_asset_name (name)))
+    {
+      /* Check if the asset exists by opening it.  Suboptimal! */
+      asset = AAssetManager_open (asset_manager, name,
+                                 AASSET_MODE_UNKNOWN);
+
+      if (!asset)
+       {
+         /* See if it's a directory also.  */
+         directory = AAssetManager_openDir (asset_manager, name);
+
+         if (directory)
+           {
+             AAssetDir_close (directory);
+             return true;
+           }
+
+         return false;
+       }
+
+      AAsset_close (asset);
+      return true;
+    }
+
+  return false;
+}
+
+/* Get a file descriptor backed by a temporary in-memory file for the
+   given asset.  */
+
+static int
+android_hack_asset_fd (AAsset *asset)
+{
+  int fd, rc;
+  unsigned char *mem;
+  size_t size;
+
+  fd = open ("/dev/ashmem", O_RDWR);
+
+  if (fd < 0)
+    return -1;
+
+  /* Assets must be small enough to fit in size_t, if off_t is
+     larger.  */
+  size = AAsset_getLength (asset);
+
+  /* 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;
+}
+
+/* `open' and such are modified even though they exist on Android,
+   because Emacs treats "/assets/" as a special directory that must
+   contain all assets in the application package.  */
+
+int
+android_open (const char *filename, int oflag, int mode)
+{
+  const char *name;
+  AAsset *asset;
+  int fd;
+  off_t out_start, out_length;
+  bool fd_hacked;
+
+  /* This flag means whether or not fd should not be duplicated.  */
+  fd_hacked = false;
+
+  if (asset_manager && (name = android_get_asset_name (filename)))
+    {
+      /* If Emacs is trying to write to the file, return NULL.  */
+
+      if (oflag & O_WRONLY || oflag & O_RDWR)
+       {
+         errno = EROFS;
+         return -1;
+       }
+
+      if (oflag & O_DIRECTORY)
+       {
+         errno = EINVAL;
+         return -1;
+       }
+
+      asset = AAssetManager_open (asset_manager, name,
+                                 AASSET_MODE_BUFFER);
+
+      if (!asset)
+       {
+         errno = ENOENT;
+         return -1;
+       }
+
+      /* Try to obtain the file descriptor corresponding to this
+        asset.  */
+      fd = AAsset_openFileDescriptor (asset, &out_start,
+                                     &out_length);
+
+      if (fd == -1)
+       {
+         /* The asset can't be accessed for some reason.  Try to
+            create a shared memory file descriptor.  */
+         fd = android_hack_asset_fd (asset);
+
+         if (fd == -1)
+           {
+             AAsset_close (asset);
+             errno = ENXIO;
+             return -1;
+           }
+
+         fd_hacked = true;
+       }
+
+      /* Duplicate the file descriptor and then close the asset,
+        which will close the original file descriptor.  */
+
+      if (!fd_hacked)
+       fd = fcntl (fd, F_DUPFD_CLOEXEC);
+
+      if (fd >= ANDROID_MAX_ASSET_FD || fd < 0)
+       {
+         /* Too bad.  You lose.  */
+         errno = ENOMEM;
+
+         if (fd >= 0)
+           close (fd);
+
+         fd = -1;
+       }
+      else
+       {
+         assert (!(android_table[fd].flags
+                   & ANDROID_FD_TABLE_ENTRY_IS_VALID));
+         android_table[fd].flags = ANDROID_FD_TABLE_ENTRY_IS_VALID;
+         memset (&android_table[fd].statb, 0,
+                 sizeof android_table[fd].statb);
+
+         /* Fill in some information that will be reported to
+            callers of android_fstat, among others.  */
+         android_table[fd].statb.st_mode = S_IFREG;
+
+         /* Owned by root.  */
+         android_table[fd].statb.st_uid = 0;
+         android_table[fd].statb.st_gid = 0;
+
+         /* Size of the file.  */
+         android_table[fd].statb.st_size
+           = AAsset_getLength (asset);
+       }
+
+      AAsset_close (asset);
+      return fd;
+    }
+
+  return open (filename, oflag, mode);
+}
+
+/* Like close.  However, remove the file descriptor from the asset
+   table as well.  */
+
+int
+android_close (int fd)
+{
+  if (fd < ANDROID_MAX_ASSET_FD
+      && (android_table[fd].flags & ANDROID_FD_TABLE_ENTRY_IS_VALID))
+    {
+      __android_log_print (ANDROID_LOG_INFO, __func__,
+                          "closing android file descriptor %d",
+                          fd);
+      android_table[fd].flags = 0;
+    }
+
+  return close (fd);
+}
+
+
+
+/* JNI functions called by Java.  */
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
+                             jobject local_asset_manager,
+                             jobject files_dir, jobject libs_dir,
+                             jobject emacs_service_object)
+{
+  int pipefd[2];
+  pthread_t thread;
+  const char *java_string;
+
+  /* This may be called from multiple threads.  setEmacsParams should
+     only ever be called once.  */
+  if (__atomic_fetch_add (&emacs_initialized, -1, __ATOMIC_RELAXED))
+    {
+      ANDROID_THROW (env, "java/lang/IllegalArgumentException",
+                    "Emacs was already initialized!");
+      return;
+    }
+
+  __android_log_print (ANDROID_LOG_INFO, __func__,
+                      "Initializing "PACKAGE_STRING"...\nPlease report bugs to 
"
+                      PACKAGE_BUGREPORT".  Thanks.\n");
+
+  /* Set the asset manager.  */
+  asset_manager = AAssetManager_fromJava (env, local_asset_manager);
+
+  /* Hold a VM reference to the asset manager to prevent the native
+     object from being deleted.  */
+  (*env)->NewGlobalRef (env, local_asset_manager);
+
+  /* Create a pipe and duplicate it to stdout and stderr.  Next, make
+     a thread that prints stderr to the system log.  */
+
+  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 *) 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);
+
+  /* Calculate the site-lisp path.  */
+
+  android_site_load_path = malloc (PATH_MAX + 1);
+
+  if (!android_site_load_path)
+    emacs_abort ();
+
+  snprintf (android_site_load_path, PATH_MAX, "%s/site-lisp",
+           android_files_dir);
+  __android_log_print (ANDROID_LOG_INFO, __func__,
+                      "Site-lisp directory: %s\n"
+                      "Files directory: %s\n"
+                      "Native code directory: %s",
+                      android_site_load_path,
+                      android_files_dir,
+                      android_lib_dir);
+
+  /* Make a reference to the Emacs service.  */
+  emacs_service = (*env)->NewGlobalRef (env, emacs_service_object);
+
+  if (!emacs_service)
+    emacs_abort ();
+
+  /* Set up events.  */
+  android_init_events ();
+
+  /* OK, setup is now complete.  The caller may start the Emacs thread
+     now.  */
+}
+
+/* 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 (copy_area, "copyArea",
+              "(Lorg/gnu/emacs/EmacsDrawable;"
+              "Lorg/gnu/emacs/EmacsDrawable;"
+              "Lorg/gnu/emacs/EmacsGC;IIIIII)V");
+  FIND_METHOD (clear_window, "clearWindow",
+              "(Lorg/gnu/emacs/EmacsWindow;)V");
+  FIND_METHOD (clear_area, "clearArea",
+              "(Lorg/gnu/emacs/EmacsWindow;IIII)V");
+
+#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");
+
+#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
+}
+
+extern JNIEXPORT void JNICALL
+NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv)
+{
+  char **c_argv;
+  jsize nelements, i;
+  jobject argument;
+  const char *c_argument;
+
+  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 ();
+
+  /* Initialize the Android GUI.  */
+  android_init_gui = true;
+  android_emacs_init (nelements, c_argv);
+  /* android_emacs_init should never return.  */
+  emacs_abort ();
+}
+
+extern JNIEXPORT void JNICALL
+NATIVE_NAME (emacsAbort) (JNIEnv *env, jobject object)
+{
+  emacs_abort ();
+}
+
+extern JNIEXPORT void JNICALL
+NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject object,
+                                  jshort window, jlong time,
+                                  jint x, jint y, jint width,
+                                  jint height)
+{
+  union android_event event;
+
+  event.xconfigure.type = ANDROID_CONFIGURE_NOTIFY;
+  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);
+}
+
+extern JNIEXPORT void JNICALL
+NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object,
+                           jshort window, jlong time,
+                           jint state, jint keycode)
+{
+  union android_event event;
+
+  event.xkey.type = ANDROID_KEY_PRESS;
+  event.xkey.window = window;
+  event.xkey.time = time;
+  event.xkey.state = state;
+  event.xkey.keycode = keycode;
+
+  android_write_event (&event);
+}
+
+extern JNIEXPORT void JNICALL
+NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object,
+                             jshort window, jlong time,
+                             jint state, jint keycode)
+{
+  union android_event event;
+
+  event.xkey.type = ANDROID_KEY_RELEASE;
+  event.xkey.window = window;
+  event.xkey.time = time;
+  event.xkey.state = state;
+  event.xkey.keycode = keycode;
+
+  android_write_event (&event);
+}
+
+#pragma clang diagnostic pop
+
+
+
+/* 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.  */
+
+#define MAX_HANDLE 65535
+
+enum android_handle_type
+  {
+    ANDROID_HANDLE_WINDOW,
+    ANDROID_HANDLE_GCONTEXT,
+    ANDROID_HANDLE_PIXMAP,
+  };
+
+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++;
+
+  if (android_handles[max_handle].handle)
+    {
+      handle = max_handle + 1;
+
+      while (max_handle < handle)
+       {
+         ++max_handle;
+
+         if (!max_handle)
+           ++max_handle;
+
+         if (!android_handles[max_handle].handle)
+           return 0;
+       }
+
+      return 0;
+    }
+
+  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_java_env)->ExceptionClear (android_java_env);
+      ANDROID_DELETE_LOCAL_REF (old);
+
+      if (!class)
+       memory_full (0);
+    }
+
+  (*android_java_env)->CallVoidMethod (android_java_env,
+                                      android_handles[handle].handle,
+                                      method);
+  (*android_java_env)->DeleteGlobalRef (android_java_env,
+                                       android_handles[handle].handle);
+  android_handles[handle].handle = NULL;
+}
+
+static jobject
+android_resolve_handle (android_handle handle,
+                       enum android_handle_type type)
+{
+  if (!handle)
+    /* ANDROID_NONE.  */
+    return NULL;
+
+  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 ();
+    }
+
+  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;
+
+  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 ();
+    }
+
+  return android_handles[handle].handle;
+}
+
+static jmethodID android_lookup_method (const char *, const char *,
+                                       const char *);
+
+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;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+  if (value_mask & ANDROID_CW_BACK_PIXEL)
+    {
+      method = android_lookup_method ("org/gnu/emacs/EmacsWindow",
+                                     "changeWindowBackground", "(I)V");
+      (*android_java_env)->CallVoidMethod (android_java_env,
+                                          window, method,
+                                          (jint) attrs->background_pixel);
+    }
+}
+
+/* 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;
+
+  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;IIII)V");
+      assert (constructor != NULL);
+
+      old = class;
+      class = (*android_java_env)->NewGlobalRef (android_java_env, class);
+      (*android_java_env)->ExceptionClear (android_java_env);
+      ANDROID_DELETE_LOCAL_REF (old);
+
+      if (!class)
+       memory_full (0);
+    }
+
+  object = (*android_java_env)->NewObject (android_java_env, class,
+                                          constructor, (jshort) window,
+                                          parent_object, (jint) x, (jint) y,
+                                          (jint) width, (jint) height);
+  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_java_env)->ExceptionClear (android_java_env);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!android_rect_class)
+    memory_full (0);
+}
+
+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", "()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_java_env)->ExceptionClear (android_java_env);
+  ANDROID_DELETE_LOCAL_REF (old);
+  if (!emacs_gc_class)
+    memory_full (0);
+
+  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 ();
+
+  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);
+}
+
+void
+android_change_gc (struct android_gc *gc,
+                  enum android_gc_value_mask mask,
+                  struct android_gc_values *values)
+{
+  jobject what, gcontext;
+
+  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);
+
+  if (mask & ANDROID_GC_BACKGROUND)
+    (*android_java_env)->SetIntField (android_java_env,
+                                     gcontext,
+                                     emacs_gc_background,
+                                     values->background);
+
+  if (mask & ANDROID_GC_FUNCTION)
+    (*android_java_env)->SetIntField (android_java_env,
+                                     gcontext,
+                                     emacs_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);
+
+  if (mask & ANDROID_GC_CLIP_Y_ORIGIN)
+    (*android_java_env)->SetIntField (android_java_env,
+                                     gcontext,
+                                     emacs_gc_clip_y_origin,
+                                     values->clip_y_origin);
+
+  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_stipple,
+                                          what);
+
+      /* Clearing GCClipMask also clears the clip rectangles.  */
+      (*android_java_env)->SetObjectField (android_java_env,
+                                          gcontext,
+                                          emacs_gc_clip_rects,
+                                          NULL);
+    }
+
+  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);
+    }
+
+  if (mask & ANDROID_GC_FILL_STYLE)
+    (*android_java_env)->SetIntField (android_java_env,
+                                     gcontext,
+                                     emacs_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);
+
+  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);
+
+  if (mask)
+    (*android_java_env)->CallVoidMethod (android_java_env,
+                                        gcontext,
+                                        emacs_gc_mark_dirty);
+}
+
+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);
+
+  if (!array)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      memory_full (0);
+    }
+
+  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));
+
+      if (!rect)
+       {
+         (*android_java_env)->ExceptionClear (android_java_env);
+         ANDROID_DELETE_LOCAL_REF (array);
+         memory_full (0);
+       }
+
+      (*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)->CallVoidMethod (android_java_env,
+                                      gcontext,
+                                      emacs_gc_mark_dirty);
+}
+
+void
+android_reparent_window (android_window w, android_window parent,
+                        int x, int y)
+{
+  /* TODO */
+}
+
+/* Look up the method with SIGNATURE by NAME in CLASS.  Abort if it
+   could not be found.  This should be used for functions which are
+   not called very often.
+
+   CLASS must never be unloaded, or the behavior is undefined.  */
+
+static jmethodID
+android_lookup_method (const char *class, const char *name,
+                      const char *signature)
+{
+  jclass java_class;
+  jmethodID method;
+
+  java_class
+    = (*android_java_env)->FindClass (android_java_env, class);
+
+  if (!java_class)
+    {
+      __android_log_print (ANDROID_LOG_ERROR, __func__,
+                          "Failed to find class %s", class);
+      emacs_abort ();
+    }
+
+  method
+    = (*android_java_env)->GetMethodID (android_java_env,
+                                       java_class, name,
+                                       signature);
+
+  if (!method)
+    {
+      __android_log_print (ANDROID_LOG_ERROR, __func__,
+                          "Failed to find method %s in class %s"
+                          " with signature %s",
+                          name, class, signature);
+      emacs_abort ();
+    }
+
+  ANDROID_DELETE_LOCAL_REF (java_class);
+  return method;
+}
+
+void
+android_clear_window (android_window handle)
+{
+  jobject window;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+  (*android_java_env)->CallVoidMethod (android_java_env,
+                                      emacs_service,
+                                      service_class.clear_window,
+                                      window);
+}
+
+void
+android_map_window (android_window handle)
+{
+  jobject window;
+  jmethodID map_window;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  map_window = android_lookup_method ("org/gnu/emacs/EmacsWindow",
+                                     "mapWindow", "()V");
+
+  (*android_java_env)->CallVoidMethod (android_java_env,
+                                      window, map_window);
+}
+
+void
+android_unmap_window (android_window handle)
+{
+  jobject window;
+  jmethodID unmap_window;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  unmap_window = android_lookup_method ("org/gnu/emacs/EmacsWindow",
+                                       "unmapWindow", "()V");
+
+  (*android_java_env)->CallVoidMethod (android_java_env,
+                                      window, unmap_window);
+}
+
+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 = android_lookup_method ("org/gnu/emacs/EmacsWindow",
+                                        "resizeWindow", "(II)V");
+
+  (*android_java_env)->CallVoidMethod (android_java_env,
+                                      window, resize_window,
+                                      (jint) width, (jint) height);
+}
+
+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 = android_lookup_method ("org/gnu/emacs/EmacsWindow",
+                                      "moveWindow", "(II)V");
+
+  (*android_java_env)->CallVoidMethod (android_java_env,
+                                      window, move_window,
+                                      (jint) x, (jint) y);
+}
+
+void
+android_swap_buffers (struct android_swap_info *swap_info,
+                     int num_windows)
+{
+  jobject window;
+  jmethodID swap_buffers;
+  int i;
+
+  swap_buffers = android_lookup_method ("org/gnu/emacs/EmacsWindow",
+                                       "swapBuffers", "()V");
+
+  for (i = 0; i < num_windows; ++i)
+    {
+      window = android_resolve_handle (swap_info[i].swap_window,
+                                      ANDROID_HANDLE_WINDOW);
+      (*android_java_env)->CallVoidMethod (android_java_env,
+                                          window, swap_buffers);
+    }
+}
+
+void
+android_get_gc_values (struct android_gc *gc,
+                      enum android_gc_value_mask mask,
+                      struct android_gc_values *values)
+{
+  jobject gcontext;
+
+  gcontext = android_resolve_handle (gc->gcontext,
+                                    ANDROID_HANDLE_GCONTEXT);
+
+  if (mask & ANDROID_GC_FOREGROUND)
+    /* GCs never have 32 bit colors, so we don't have to worry about
+       sign extension here.  */
+    values->foreground
+      = (*android_java_env)->GetIntField (android_java_env,
+                                         gcontext,
+                                         emacs_gc_foreground);
+
+  if (mask & ANDROID_GC_BACKGROUND)
+    values->background
+      = (*android_java_env)->GetIntField (android_java_env,
+                                         gcontext,
+                                         emacs_gc_background);
+
+  if (mask & ANDROID_GC_FUNCTION)
+    values->function
+      = (*android_java_env)->GetIntField (android_java_env,
+                                         gcontext,
+                                         emacs_gc_function);
+
+  if (mask & ANDROID_GC_CLIP_X_ORIGIN)
+    values->clip_x_origin
+      = (*android_java_env)->GetIntField (android_java_env,
+                                         gcontext,
+                                         emacs_gc_clip_x_origin);
+
+  if (mask & ANDROID_GC_CLIP_Y_ORIGIN)
+    values->clip_y_origin
+      = (*android_java_env)->GetIntField (android_java_env,
+                                         gcontext,
+                                         emacs_gc_clip_y_origin);
+
+  if (mask & ANDROID_GC_FILL_STYLE)
+    values->fill_style
+      = (*android_java_env)->GetIntField (android_java_env,
+                                         gcontext,
+                                         emacs_gc_fill_style);
+
+  if (mask & ANDROID_GC_TILE_STIP_X_ORIGIN)
+    values->ts_x_origin
+      = (*android_java_env)->GetIntField (android_java_env,
+                                         gcontext,
+                                         emacs_gc_ts_origin_x);
+
+  if (mask & ANDROID_GC_TILE_STIP_Y_ORIGIN)
+    values->ts_y_origin
+      = (*android_java_env)->GetIntField (android_java_env,
+                                         gcontext,
+                                         emacs_gc_ts_origin_y);
+
+  /* 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)->CallVoidMethod (android_java_env,
+                                      emacs_service,
+                                      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);
+
+  if (!colors)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      memory_full (0);
+    }
+
+  SAFE_NALLOCA (region, sizeof *region, width);
+
+  for (y = 0; y < height; ++y)
+    {
+      for (x = 0; x < width; ++x)
+       {
+         if (data[y / 8] & (1 << (x % 8)))
+           region[x] = foreground;
+         else
+           region[x] = background;
+       }
+
+      (*android_java_env)->SetIntArrayRegion (android_java_env,
+                                             colors,
+                                             width * y, width,
+                                             region);
+    }
+
+  /* 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);
+}
+
+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, gcontext;
+
+  src_object = android_resolve_handle2 (src, ANDROID_HANDLE_WINDOW,
+                                       ANDROID_HANDLE_PIXMAP);
+  dest_object = android_resolve_handle2 (dest, ANDROID_HANDLE_WINDOW,
+                                        ANDROID_HANDLE_PIXMAP);
+  gcontext = android_resolve_handle (gc->gcontext,
+                                    ANDROID_HANDLE_GCONTEXT);
+
+  (*android_java_env)->CallVoidMethod (android_java_env,
+                                      emacs_service,
+                                      service_class.copy_area,
+                                      src_object,
+                                      dest_object,
+                                      gcontext,
+                                      (jint) src_x, (jint) src_y,
+                                      (jint) width, (jint) height,
+                                      (jint) dest_x, (jint) dest_y);
+}
+
+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);
+
+  if (!array)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      memory_full (0);
+    }
+
+  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);
+
+      if (!point)
+       {
+         (*android_java_env)->ExceptionClear (android_java_env);
+         ANDROID_DELETE_LOCAL_REF (array);
+         memory_full (0);
+       }
+
+      (*android_java_env)->SetObjectArrayElement (android_java_env,
+                                                 array, i, point);
+      ANDROID_DELETE_LOCAL_REF (point);
+    }
+
+  (*android_java_env)->CallVoidMethod (android_java_env,
+                                      emacs_service,
+                                      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)->CallVoidMethod (android_java_env,
+                                      emacs_service,
+                                      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)->CallVoidMethod (android_java_env,
+                                      emacs_service,
+                                      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)->CallVoidMethod (android_java_env,
+                                      emacs_service,
+                                      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;
+  jintArray colors;
+  android_pixmap pixmap;
+
+  /* Create the color array holding the data.  */
+  colors = (*android_java_env)->NewIntArray (android_java_env,
+                                            width * height);
+
+  if (!colors)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      memory_full (0);
+    }
+
+  /* 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_DELETE_LOCAL_REF ((jobject) colors);
+
+  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)->CallVoidMethod (android_java_env,
+                                      emacs_service,
+                                      service_class.clear_area,
+                                      window, (jint) x, (jint) y,
+                                      (jint) width, (jint) height);
+}
+
+#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 ();
+}
+
+#endif
diff --git a/src/android.h b/src/android.h
new file mode 100644
index 0000000000..6bdcd38ed6
--- /dev/null
+++ b/src/android.h
@@ -0,0 +1,70 @@
+/* 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>
+#endif
+
+/* This must be used in every symbol declaration to export it to the
+   JNI Emacs wrapper.  */
+#define ANDROID_EXPORT __attribute__ ((visibility ("default")))
+
+extern bool ANDROID_EXPORT android_init_gui;
+extern int ANDROID_EXPORT android_emacs_init (int, char **);
+
+#ifndef ANDROID_STUBIFY
+
+extern int android_select (int, fd_set *, fd_set *, fd_set *,
+                          struct timespec *, const sigset_t *);
+
+extern bool android_file_access_p (const char *, int);
+extern int android_open (const char *, int, int);
+extern char *android_user_full_name (struct passwd *);
+extern int android_fstat (int, struct stat *);
+extern int android_fstatat (int, const char *restrict,
+                           struct stat *restrict, int);
+extern int android_close (int);
+
+#endif
+
+/* 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>
+
+extern JNIEnv *android_java_env;
+
+#define ANDROID_DELETE_LOCAL_REF(ref)                          \
+  ((*android_java_env)->DeleteLocalRef (android_java_env,      \
+                                       (ref)))
+
+#define NATIVE_NAME(name) Java_org_gnu_emacs_EmacsNative_##name
+
+#endif
+#endif /* _ANDROID_H_ */
diff --git a/src/androidfns.c b/src/androidfns.c
new file mode 100644
index 0000000000..e9e1ae3d52
--- /dev/null
+++ b/src/androidfns.c
@@ -0,0 +1,1779 @@
+/* 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 "androidterm.h"
+#include "blockinput.h"
+#include "keyboard.h"
+
+#ifndef ANDROID_STUBIFY
+
+/* Some kind of reference count for the image cache.  */
+static ptrdiff_t image_cache_refcount;
+
+#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);
+}
+
+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));
+}
+
+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[] = {
+       /* This will find the normal font.  */
+       "DroidSansMono",
+       "monospace",
+       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;
+
+    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));
+  }
+
+  /* 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);
+
+  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);
+
+  /* gui_default_parameter (f, parms, Qvertical_scroll_bars, */
+  /*                        Qleft, */
+  /*                        "verticalScrollBars", "ScrollBars", */
+  /*                        RES_TYPE_SYMBOL); */
+  /* gui_default_parameter (f, parms, Qhorizontal_scroll_bars, Qnil, */
+  /*                        "horizontalScrollBars", "ScrollBars", */
+  /*                        RES_TYPE_SYMBOL);  TODO */
+
+  /* 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 /* TODO */
+
+  /* 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);
+  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.  */
+  gui_default_parameter (f, parms, Qfullscreen, Qnil,
+                         "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
+  error ("Not implemented");
+  return Qnil;
+#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
+  error ("Not implemented");
+  return Qnil;
+#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-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
+  error ("Not implemented");
+  return Qnil;
+#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
+  error ("Not implemented");
+  return Qnil;
+#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;
+}
+
+DEFUN ("x-display-monitor-attributes-list", Fx_display_monitor_attributes_list,
+       Sx_display_monitor_attributes_list,
+       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
+  error ("Not implemented");
+  return Qnil;
+#endif
+}
+
+DEFUN ("x-frame-geometry", Fx_frame_geometry, Sx_frame_geometry,
+       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
+  error ("Not implemented");
+  return Qnil;
+#endif
+}
+
+DEFUN ("x-frame-list-z-order", Fx_frame_list_z_order,
+       Sx_frame_list_z_order, 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
+  error ("Not implemented");
+  return Qnil;
+#endif
+}
+
+DEFUN ("x-frame-restack", Fx_frame_restack, Sx_frame_restack, 2, 3, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object frame1, Lisp_Object frame2, Lisp_Object frame3)
+{
+#ifdef ANDROID_STUBIFY
+  error ("Android cross-compilation stub called!");
+  return Qnil;
+#else
+  error ("Not implemented");
+  return Qnil;
+#endif
+}
+
+DEFUN ("x-mouse-absolute-pixel-position", Fx_mouse_absolute_pixel_position,
+       Sx_mouse_absolute_pixel_position, 0, 0, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (void)
+{
+  /* TODO: figure out how to implement this.  */
+  return Qnil;
+}
+
+DEFUN ("x-set-mouse-absolute-pixel-position",
+       Fx_set_mouse_absolute_pixel_position,
+       Sx_set_mouse_absolute_pixel_position, 2, 2, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object x, Lisp_Object y)
+{
+  /* TODO: figure out how to implement this.  */
+  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;
+}
+
+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
+  error ("Not implemented");
+  return 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
+  error ("Not implemented");
+  return Qnil;
+#endif
+}
+
+
+
+#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_WINDOW (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_WINDOW (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);
+}
+
+static void
+android_set_mouse_color (struct frame *f, Lisp_Object arg,
+                        Lisp_Object oldval)
+{
+  /* Changing the mouse color is unsupported under Android, so this is
+     left intact.  */
+  android_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
+}
+
+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 ();
+    }
+}
+
+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,
+  NULL,
+  NULL,
+  NULL, /* x_set_undecorated, */
+  NULL, /* x_set_parent_frame, */
+  NULL, /* x_set_skip_taskbar, */
+  NULL, /* x_set_no_focus_on_map, */
+  NULL, /* x_set_no_accept_focus, */
+  NULL, /* x_set_z_group, */
+  NULL, /* x_set_override_redirect, */
+  gui_set_no_special_glyphs,
+  NULL, /* x_set_alpha_background, */
+  NULL, /* x_set_use_frame_synchronization, */
+};
+
+#endif
+
+
+
+void
+syms_of_androidfns (void)
+{
+  /* Miscellaneous symbols used by some functions here.  */
+  DEFSYM (Qtrue_color, "true-color");
+  DEFSYM (Qalways, "always");
+
+  DEFVAR_LISP ("x-cursor-fore-pixel", Vx_cursor_fore_pixel,
+    doc: /* SKIP: real doc in xfns.c.  */);
+  Vx_cursor_fore_pixel = Qnil;
+
+  /* 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 (&Sx_display_monitor_attributes_list);
+  defsubr (&Sx_frame_geometry);
+  defsubr (&Sx_frame_list_z_order);
+  defsubr (&Sx_frame_restack);
+  defsubr (&Sx_mouse_absolute_pixel_position);
+  defsubr (&Sx_set_mouse_absolute_pixel_position);
+  defsubr (&Sandroid_get_connection);
+  defsubr (&Sx_display_list);
+  defsubr (&Sx_show_tip);
+  defsubr (&Sx_hide_tip);
+}
diff --git a/src/androidfont.c b/src/androidfont.c
new file mode 100644
index 0000000000..e312e55c54
--- /dev/null
+++ b/src/androidfont.c
@@ -0,0 +1,955 @@
+/* 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 "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;
+
+  /* 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;
+};
+
+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;
+};
+
+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;[I[Lorg/gnu/emacs/EmacsFontDriver$FontMetrics;)V");
+  FIND_METHOD (encode_char, "encodeChar", "(Lorg/gnu/emacs/EmacsFontDriver"
+              "$FontObject;C)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;");
+#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;
+}
+
+/* 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);
+
+  if (!spec)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      memory_full (0);
+    }
+
+#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)));  
\
+      if (!string)                                                             
\
+       {                                                                       
\
+         (*android_java_env)->ExceptionClear (android_java_env);               
\
+         memory_full (0);                                                      
\
+       }                                                                       
\
+                                                                               
\
+      (*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);                  
\
+      if (!integer)                                                            
\
+       {                                                                       
\
+         (*android_java_env)->ExceptionClear (android_java_env);               
\
+         memory_full (0);                                                      
\
+       }                                                                       
\
+                                                                               
\
+      (*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));
+
+#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);
+#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;
+
+  spec = androidfont_from_lisp (font_spec);
+  array = (*android_java_env)->CallObjectMethod (android_java_env,
+                                                font_driver,
+                                                font_driver_class.list,
+                                                spec);
+  (*android_java_env)->ExceptionClear (android_java_env);
+  ANDROID_DELETE_LOCAL_REF (spec);
+
+  if (!array)
+    memory_full (0);
+
+  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_java_env)->ExceptionClear (android_java_env);
+      ANDROID_DELETE_LOCAL_REF (tem);
+
+      if (!info->object)
+       memory_full (0);
+
+      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;
+
+  spec = androidfont_from_lisp (font_spec);
+  result = (*android_java_env)->CallObjectMethod (android_java_env,
+                                                 font_driver,
+                                                 font_driver_class.match,
+                                                 spec);
+  (*android_java_env)->ExceptionClear (android_java_env);
+  ANDROID_DELETE_LOCAL_REF (spec);
+
+  if (!result)
+    memory_full (0);
+
+  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_java_env)->ExceptionClear (android_java_env);
+  ANDROID_DELETE_LOCAL_REF (result);
+
+  if (!info->object)
+    memory_full (0);
+
+  return entity;
+}
+
+static int
+androidfont_draw (struct glyph_string *s, int from, int to,
+                 int x, int y, bool with_background)
+{
+  return 0;
+}
+
+static Lisp_Object
+androidfont_open_font (struct frame *f, Lisp_Object font_entity, int x)
+{
+  struct androidfont_info *font_info;
+  struct androidfont_entity *entity;
+  struct font *font;
+  Lisp_Object font_object, tem;
+  jobject old;
+  jint value;
+
+  if (x <= 0)
+    {
+      /* Get pixel size from frame instead.  */
+      tem = get_frame_param (f, Qfontsize);
+      x = NILP (tem) ? 0 : XFIXNAT (tem);
+    }
+
+  __android_log_print (ANDROID_LOG_DEBUG, __func__,
+                      "opening font entity %"pI"x:%d",
+                      (EMACS_INT) font_entity, x);
+
+  entity = (struct androidfont_entity *) XFONT_ENTITY (font_entity);
+
+  block_input ();
+  font_object = font_make_object (VECSIZE (struct androidfont_info),
+                                 font_entity, x);
+  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 early in case GC happens later on! */
+  font_info->object = NULL;
+  unblock_input ();
+
+  font_info->object
+    = (*android_java_env)->CallObjectMethod (android_java_env,
+                                            font_driver,
+                                            font_driver_class.open_font,
+                                            entity->object, (jint) x);
+  if (!font_info->object)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      return Qnil;
+    }
+
+  old = font_info->object;
+  font_info->object
+    = (*android_java_env)->NewGlobalRef (android_java_env, old);
+  (*android_java_env)->ExceptionClear (android_java_env);
+  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;
+
+  info = (struct androidfont_info *) font;
+
+  /* If info->object is NULL, then FONT was unsuccessfully created,
+     and there is no global reference that has to be deleted.  */
+
+  if (!info->object)
+    return;
+
+  (*android_java_env)->DeleteGlobalRef (android_java_env,
+                                       info->object);
+}
+
+static int
+androidfont_has_char (Lisp_Object font, int c)
+{
+  struct androidfont_info *info;
+  struct androidfont_entity *entity;
+
+  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;
+
+  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_text_extents (struct font *font, const unsigned int *code,
+                         int nglyphs, struct font_metrics *metrics)
+{
+  struct androidfont_info *info;
+  jarray codepoint_array, metrics_array;
+  jobject metrics_object;
+  int i;
+  short value;
+
+  info = (struct androidfont_info *) font;
+
+  /* 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);
+    }
+
+  metrics_array
+    = (*android_java_env)->NewObjectArray (android_java_env,
+                                          nglyphs,
+                                          font_metrics_class.class,
+                                          NULL);
+  if (!metrics_array)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      ANDROID_DELETE_LOCAL_REF (metrics_array);
+      memory_full (0);
+    }
+
+  if (sizeof (unsigned int) == sizeof (jint))
+    /* Always true on every Android device.  */
+    (*android_java_env)->SetIntArrayRegion (android_java_env,
+                                           codepoint_array,
+                                           0, nglyphs,
+                                           (jint *) code);
+  else
+    emacs_abort ();
+
+  for (i = 0; i < nglyphs; ++i)
+    {
+      metrics_object
+       = (*android_java_env)->AllocObject (android_java_env,
+                                           font_metrics_class.class);
+
+      if (!metrics_object)
+       {
+         (*android_java_env)->ExceptionClear (android_java_env);
+         ANDROID_DELETE_LOCAL_REF (metrics_array);
+         ANDROID_DELETE_LOCAL_REF (codepoint_array);
+         memory_full (0);
+       }
+
+      (*android_java_env)->SetObjectArrayElement (android_java_env,
+                                                 metrics_array, i,
+                                                 metrics_object);
+      ANDROID_DELETE_LOCAL_REF (metrics_object);
+    }
+
+  (*android_java_env)->CallVoidMethod (android_java_env,
+                                      font_driver,
+                                      font_driver_class.text_extents,
+                                      info->object, codepoint_array,
+                                      metrics_array);
+
+  if ((*android_java_env)->ExceptionCheck (android_java_env))
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      ANDROID_DELETE_LOCAL_REF (metrics_array);
+      ANDROID_DELETE_LOCAL_REF (codepoint_array);
+      memory_full (0);
+    }
+
+  for (i = 0; i < nglyphs; ++i)
+    {
+      metrics_object
+       = (*android_java_env)->GetObjectArrayElement (android_java_env,
+                                                     metrics_array, i);
+#define DO_CARDINAL_FIELD(field)                                       \
+  value                                                                        
\
+    = (*android_java_env)->GetShortField (android_java_env,            \
+                                         metrics_object,               \
+                                         font_metrics_class.field);    \
+  metrics[i].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 (metrics_array);
+  ANDROID_DELETE_LOCAL_REF (codepoint_array);
+}
+
+static Lisp_Object
+androidfont_list_family (struct frame *f)
+{
+  return Qnil;
+}
+
+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)
+{
+  jmethodID method;
+  jobject old;
+
+  android_init_font_driver ();
+  android_init_font_spec ();
+  android_init_font_metrics ();
+  android_init_font_object ();
+  android_init_integer ();
+
+  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);
+
+  if (!font_driver)
+    memory_full (0);
+
+  old = font_driver;
+  font_driver
+    = (*android_java_env)->NewGlobalRef (android_java_env, font_driver);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!font_driver)
+    memory_full (0);
+}
+
+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 0000000000..43ccc86e5c
--- /dev/null
+++ b/src/androidgui.h
@@ -0,0 +1,315 @@
+/* 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 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),
+  };
+
+struct android_set_window_attributes
+{
+  /* The background pixel.  */
+  unsigned long background_pixel;
+};
+
+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
+{
+  /* The Java-side handle.  */
+  android_gcontext gcontext;
+};
+
+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;
+};
+
+/* Android doesn't support cursors, so define this to something
+   unused.  */
+typedef char Emacs_Cursor;
+
+#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;
+
+/* 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,
+  };
+
+struct android_any_event
+{
+  enum android_event_type type;
+  android_window window;
+};
+
+struct android_key_event
+{
+  enum android_event_type type;
+  android_window window;
+  android_time time;
+  unsigned int state;
+  unsigned int keycode;
+};
+
+struct android_configure_event
+{
+  enum android_event_type type;
+  android_window window;
+  android_time time;
+  int x, y;
+  int width, height;
+};
+
+union android_event
+{
+  enum android_event_type type;
+  struct android_any_event xany;
+  struct android_key_event xkey;
+  struct android_configure_event xconfigure;
+};
+
+extern int android_pending (void);
+extern void android_next_event (union android_event *);
+
+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);
+
+#endif
+
+/* 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/androidterm.c b/src/androidterm.c
new file mode 100644
index 0000000000..bb3c2a2973
--- /dev/null
+++ b/src/androidterm.c
@@ -0,0 +1,3161 @@
+/* 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 "lisp.h"
+#include "androidterm.h"
+#include "keyboard.h"
+#include "blockinput.h"
+#include "android.h"
+#include "buffer.h"
+#include "window.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
+
+enum
+  {
+    ANDROID_EVENT_NORMAL,
+    ANDROID_EVENT_GOTO_OUT,
+    ANDROID_EVENT_DROP,
+  };
+
+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) || FRAME_DISPLAY_INFO (f) != dpyinfo)
+       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_WINDOW (f));
+}
+
+static void
+android_ring_bell (struct frame *f)
+{
+  /* TODO */
+}
+
+static void
+android_toggle_invisible_pointer (struct frame *f, bool invisible)
+{
+  /* TODO */
+}
+
+/* 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);
+}
+
+/* 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))
+    return;
+
+  show_back_buffer (f);
+}
+
+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;
+
+  hlinfo = &dpyinfo->mouse_highlight;
+  *finish = ANDROID_EVENT_NORMAL;
+  any = android_window_to_frame (dpyinfo, event->xany.window);
+
+  switch (event->type)
+    {
+    case ANDROID_CONFIGURE_NOTIFY:
+      configureEvent = *event;
+
+      f = android_window_to_frame (dpyinfo,
+                                  configureEvent.xconfigure.window);
+
+      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);
+       }
+
+      goto OTHER;
+
+    case ANDROID_KEY_PRESS:
+
+      /* If mouse-highlight is an integer, input clears out
+        mouse highlighting.  */
+      if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight)
+         && (any == 0
+             || !EQ (f->tool_bar_window, hlinfo->mouse_face_window)
+             || !EQ (f->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);
+       }
+
+    default:
+      goto OTHER;
+    }
+
+ OTHER:
+
+  return 0;
+}
+
+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);
+        TODO */
+      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 ())
+    show_back_buffer (f);
+
+  /* The frame is now complete, as its contents have been drawn.  */
+  FRAME_ANDROID_COMPLETE_P (f) = true;
+  unblock_input ();
+}
+
+static void
+android_buffer_flipping_unblocked_hook (struct frame *f)
+{
+  block_input ();
+  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)
+{
+  /* TODO */
+}
+
+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)
+{
+  /* TODO */
+}
+
+static void
+android_frame_rehighlight (struct frame *f)
+{
+  /* TODO */
+}
+
+static void
+android_frame_raise_lower (struct frame *f, bool raise_flag)
+{
+  /* TODO */
+}
+
+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)
+{
+  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)
+{
+  /* TODO */
+}
+
+void
+android_iconify_frame (struct frame *f)
+{
+  /* TODO */
+}
+
+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);
+
+  /* We've set {FRAME,PIXEL}_{WIDTH,HEIGHT} to the values we hope to
+     receive in the ConfigureNotify event; if we get what we asked
+     for, then the event won't cause the screen to become garbaged, so
+     we have to make sure to do it here.  */
+  SET_FRAME_GARBAGED (f);
+}
+
+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)
+{
+  /* TODO */
+}
+
+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)
+{
+  /* TODO */
+  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;
+
+  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 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->highlight_frame)
+    dpyinfo->highlight_frame = 0;
+  if (f == hlinfo->mouse_face_mouse_frame)
+    reset_mouse_highlight (hlinfo);
+
+  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);
+
+  android_copy_area (FRAME_ANDROID_WINDOW (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 ();
+  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_WINDOW (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_WINDOW (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_WINDOW (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, 0);
+
+         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_WINDOW (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_WINDOW (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_WINDOW (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_WINDOW (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_WINDOW (f), gc, left_x, top_y,
+                           right_x - left_x + 1, hwidth);
+
+  if (left_p)
+    android_fill_rectangle (FRAME_ANDROID_WINDOW (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_WINDOW (f), gc, left_x,
+                           bottom_y - hwidth + 1,
+                           right_x - left_x + 1, hwidth);
+
+  if (right_p)
+    android_fill_rectangle (FRAME_ANDROID_WINDOW (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_WINDOW (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_WINDOW (s->f), s->gc, left_x,
+                         left_x, right_x - left_x + 1, hwidth);
+
+  /* Left.  */
+  if (left_p)
+    android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, left_x,
+                           top_y, vwidth, bottom_y - top_y + 1);
+
+  /* Bottom.  */
+  android_fill_rectangle (FRAME_ANDROID_WINDOW (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_WINDOW (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_WINDOW (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)
+    {
+      if (s->img->mask)
+       {
+         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_WINDOW (s->f),
+                              s->gc, s->slice.x + r.x - x,
+                              s->slice.y + r.y - y,
+                               r.x, r.y, r.width, r.height);
+       }
+      else
+       {
+         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_WINDOW (s->f),
+                              s->gc, s->slice.x + r.x - x,
+                              s->slice.y + r.y - y,
+                               r.x, r.y, r.width, r.height);
+
+         /* 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)
+           {
+             int relief = eabs (s->img->relief);
+             android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc,
+                                     x - relief, y - relief,
+                                     s->slice.width + relief*2 - 1,
+                                     s->slice.height + relief*2 - 1);
+           }
+       }
+    }
+  else
+    /* Draw a rectangle if image could not be loaded.  */
+    android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, x, y,
+                           s->slice.width - 1, s->slice.height - 1);
+}
+
+/* Draw the foreground of image glyph string S to PIXMAP.  */
+
+static void
+android_draw_image_foreground_1 (struct glyph_string *s,
+                                android_pixmap pixmap)
+{
+  int x = 0;
+  int y = s->ybase - s->y - 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)
+    {
+      if (s->img->mask)
+       {
+         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, pixmap, s->gc,
+                              s->slice.x + r.x - x,
+                              s->slice.y + r.y - y,
+                               r.x, r.y, r.width, r.height);
+
+         android_set_clip_mask (s->gc, ANDROID_NONE);
+       }
+      else
+       {
+         android_copy_area (s->img->pixmap, pixmap, s->gc,
+                            s->slice.x, s->slice.y,
+                            s->slice.width, s->slice.height, x, 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)
+           {
+             int r = eabs (s->img->relief);
+             android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f),
+                                     s->gc, x - r, y - r,
+                                     s->slice.width + r*2 - 1,
+                                     s->slice.height + r*2 - 1);
+           }
+       }
+    }
+  else
+    /* Draw a rectangle if image could not be loaded.  */
+    android_draw_rectangle (FRAME_ANDROID_WINDOW (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;
+  android_pixmap pixmap = ANDROID_NONE;
+
+  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;
+
+      if (s->img->mask)
+       {
+         /* Create a pixmap as large as the glyph string.  Fill it
+            with the background color.  Copy the image to it, using
+            its mask.  Copy the temporary pixmap to the display.  */
+         int depth = FRAME_DISPLAY_INFO (s->f)->n_planes;
+
+         /* Create a pixmap as large as the glyph string.  */
+          pixmap = android_create_pixmap (s->background_width,
+                                         s->height, depth);
+
+         /* Don't clip in the following because we're working on the
+            pixmap.  */
+         android_set_clip_mask (s->gc, ANDROID_NONE);
+
+         /* Fill the pixmap with the background color/stipple.  */
+         if (s->stippled_p)
+           {
+             /* Fill background with a stipple pattern.  */
+             android_set_fill_style (s->gc, ANDROID_FILL_OPAQUE_STIPPLED);
+             android_set_ts_origin (s->gc, - s->x, - s->y);
+             android_fill_rectangle (pixmap, s->gc,
+                                     0, 0, s->background_width, s->height);
+             android_set_fill_style (s->gc, ANDROID_FILL_OPAQUE_STIPPLED);
+             android_set_ts_origin (s->gc, 0, 0);
+           }
+         else
+           {
+             struct android_gc_values xgcv;
+
+             android_get_gc_values (s->gc, (ANDROID_GC_FOREGROUND
+                                            | ANDROID_GC_BACKGROUND),
+                                    &xgcv);
+             android_set_foreground (s->gc, xgcv.background);
+             android_fill_rectangle (pixmap, s->gc,
+                                     0, 0, s->background_width, s->height);
+             android_set_background (s->gc, xgcv.foreground);
+           }
+       }
+      else
+       {
+         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.  */
+  if (pixmap != ANDROID_NONE)
+    {
+      android_draw_image_foreground_1 (s, pixmap);
+      android_set_glyph_string_clipping (s);
+      android_copy_area (pixmap, FRAME_ANDROID_WINDOW (s->f), s->gc,
+                        0, 0, s->background_width, s->height, s->x,
+                        s->y);
+      android_free_pixmap (pixmap);
+    }
+  else
+    android_draw_image_foreground (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_WINDOW (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_WINDOW (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_draw_underwave (struct glyph_string *s, int decoration_width)
+{
+  int wave_height = 3, wave_length = 2;
+  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_WINDOW (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_WINDOW (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_WINDOW (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_WINDOW (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_WINDOW (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_WINDOW (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_WINDOW (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_WINDOW (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_WINDOW (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)
+{
+  /* Not supported, because cursors are not supported on Android.  */
+}
+
+static void
+android_clear_frame_area (struct frame *f, int x, int y,
+                         int width, int height)
+{
+  android_clear_area (FRAME_ANDROID_WINDOW (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 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_WINDOW (f), gc, 0, margin,
+                                 width, border);
+         android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, 0, 0,
+                                 border, height);
+         android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, width - border,
+                                 0, border, height);
+         android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, 0,
+                                 height - border, width, border);
+         android_set_foreground (gc, FRAME_FOREGROUND_PIXEL (f));
+       }
+      else
+       {
+         android_clear_area (FRAME_ANDROID_WINDOW (f), 0, 0,
+                             border, height);
+         android_clear_area (FRAME_ANDROID_WINDOW (f), 0,
+                             margin, width, border);
+         android_clear_area (FRAME_ANDROID_WINDOW (f), width - border,
+                             0, border, height);
+         android_clear_area (FRAME_ANDROID_WINDOW (f), 0,
+                             height - 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_WINDOW (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_WINDOW (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_WINDOW (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)
+{
+  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 ();
+           }
+       }
+    }
+}
+
+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_WINDOW (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_WINDOW (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_WINDOW (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_WINDOW (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_WINDOW (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_WINDOW (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_WINDOW (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_WINDOW (f),
+                             f->output_data.android->normal_gc,
+                             x0, y0, x1 - x0, y1 - y0);
+    }
+}
+
+
+
+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,
+    NULL,
+    NULL,
+    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)
+{
+  return (char *) "UNKNOWN KEYSYM";
+}
+
+
+
+/* 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;
+  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; XXX */
+  terminal->change_tab_bar_height_hook = android_change_tab_bar_height;
+  terminal->change_tool_bar_height_hook = android_change_tool_bar_height;
+  /* terminal->set_vertical_scroll_bar_hook */
+  /*   = android_set_vertical_scroll_bar; */
+  /* terminal->set_horizontal_scroll_bar_hook */
+  /*   = android_set_horizontal_scroll_bar; */
+  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->condemn_scroll_bars_hook = android_condemn_scroll_bars; */
+  /* terminal->redeem_scroll_bars_hook = android_redeem_scroll_bars; */
+  /* terminal->judge_scroll_bars_hook = android_judge_scroll_bars; */
+  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;
+
+  /* TODO! */
+  dpyinfo->resx = 96.0;
+  dpyinfo->resy = 96.0;
+
+  /* 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;
+}
+
+
+
+void
+syms_of_androidterm (void)
+{
+  Fprovide (Qandroid, Qnil);
+
+  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;
+  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 0000000000..3a0c9f6055
--- /dev/null
+++ b/src/androidterm.h
@@ -0,0 +1,366 @@
+/* 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.  */
+  Emacs_Pixmap img;
+
+  /* The file from which it comes.  */
+  char *file;
+
+  /* The number of references to it.  */
+  int refcount;
+
+  /* The height and width.  */
+  int height, width;
+};
+
+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;
+
+  /* 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 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;
+};
+
+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 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;
+
+  /* Unused stuff (cursors).  */
+  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;
+
+  /* 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 we've modified the back buffer and
+     need to publish our modifications to the front buffer at a
+     convenient time.  */
+  bool_bf need_buffer_flip : 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;
+
+  /* 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;
+};
+
+/* 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 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);
+
+/* 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 *);
+
+
+
+#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/dired.c b/src/dired.c
index ef729df5d2..e7d4c75092 100644
--- a/src/dired.c
+++ b/src/dired.c
@@ -979,14 +979,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 e6c4270beb..e521dffc37 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -53,8 +53,15 @@ 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
+/* Android doesn't support cursors and also uses handles.  */
+#define No_Cursor 0
+#endif
+
+#ifndef HAVE_ANDROID
 
 /* XRectangle-like struct used by non-X GUI code.  */
 typedef struct
@@ -63,6 +70,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 +157,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 Emacs_Pixmap Emacs_Pix_Container;
+typedef Emacs_Pixmap Emacs_Pix_Context;
+#endif
+
 #ifdef HAVE_WINDOW_SYSTEM
 # include <time.h>
 # include "fontset.h"
@@ -1401,6 +1421,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 +1703,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
@@ -3502,9 +3526,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.  */
@@ -3566,7 +3592,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
 
@@ -3647,6 +3673,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 5a9ba8909e..ed10accac7 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.  */
   {
@@ -2296,7 +2302,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))
@@ -3234,7 +3240,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))
@@ -5059,6 +5065,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;
@@ -5149,6 +5159,7 @@ scrolling (struct frame *frame)
                 free_at_end_vpos - unchanged_at_top);
 
   SAFE_FREE ();
+#endif
   return false;
 }
 
@@ -5190,7 +5201,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.  */
@@ -5395,7 +5408,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.
@@ -5405,7 +5421,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;
@@ -6528,6 +6547,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)
     {
@@ -6582,6 +6610,7 @@ init_display_interactive (void)
       exit (1);
     }
 
+#ifndef HAVE_ANDROID
   {
     struct terminal *t;
     struct frame *f = XFRAME (selected_frame);
@@ -6624,6 +6653,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/editfns.c b/src/editfns.c
index 8d56ef21d9..f061199705 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>
@@ -1264,7 +1268,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.c b/src/emacs.c
index d8a2863fd9..b6686f8809 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -33,6 +33,10 @@ 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
+
 #ifdef WINDOWSNT
 #include <fcntl.h>
 #include <sys/socket.h>
@@ -137,6 +141,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"
@@ -1123,7 +1131,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 +1233,18 @@ 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)
+#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;
 
   /* First, check whether we should apply a seccomp filter.  This
      should come at the very beginning to allow the filter to protect
@@ -1425,8 +1439,10 @@ main (int argc, char **argv)
 
   bool only_version = false;
   sort_args (argc, argv);
-  argc = 0;
-  while (argv[argc]) argc++;
+  old_argc = argc, argc = 0;
+  while (argv[argc]
+        /* Don't allow going past argv.  */
+        && argc < old_argc) argc++;
 
   skip_args = 0;
   if (argmatch (argv, argc, "-version", "--version", 3, NULL, &skip_args))
@@ -2375,6 +2391,14 @@ 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_fontset ();
+#if !defined ANDROID_STUBIFY
+      syms_of_androidfont ();
+#endif /* !ANDROID_STUBIFY */
+#endif /* HAVE_ANDROID */
 
       syms_of_gnutls ();
 
@@ -2436,6 +2460,10 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
   init_haiku_select ();
 #endif
 
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  init_androidfont ();
+#endif
+
   init_charset ();
 
   /* This calls putenv and so must precede init_process_emacs.  */
@@ -2950,7 +2978,7 @@ 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 ())
     {
@@ -3469,6 +3497,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 2eccd0ac60..7ab1b4b3d8 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 66ce6b3088..1d22c51eba 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"
 
+#if defined HAVE_ANDROID
+#include "android.h"
+#endif
+
 #ifdef HAVE_LINUX_FS_H
 # include <sys/ioctl.h>
 # include <linux/fs.h>
@@ -135,7 +139,6 @@ 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,
@@ -160,6 +163,12 @@ file_access_p (char const *file, int amode)
     }
 #endif
 
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  /* FILE may be some kind of special Android file.  */
+  if (android_file_access_p (file, amode))
+    return true;
+#endif
+
   if (faccessat (AT_FDCWD, file, amode, AT_EACCESS) == 0)
     return true;
 
@@ -370,7 +379,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);
@@ -2227,7 +2236,7 @@ permissions.  */)
 
   record_unwind_protect_int (close_file_unwind, ifd);
 
-  if (fstat (ifd, &st) != 0)
+  if (sys_fstat (ifd, &st) != 0)
     report_file_error ("Input file status", file);
 
   if (!NILP (preserve_permissions))
@@ -2273,7 +2282,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",
@@ -3078,7 +3087,7 @@ file_directory_p (Lisp_Object file)
     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);
@@ -4002,7 +4011,7 @@ by calling `format-decode', which see.  */)
                             XCAR (XCAR (window_markers)));
     }
 
-  if (fstat (fd, &st) != 0)
+  if (sys_fstat (fd, &st) != 0)
     report_file_error ("Input file status", orig_filename);
   mtime = get_stat_mtime (&st);
 
@@ -5394,7 +5403,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;
@@ -5432,7 +5441,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
diff --git a/src/filelock.c b/src/filelock.c
index a657cc4582..e70c68501d 100644
--- a/src/filelock.c
+++ b/src/filelock.c
@@ -120,6 +120,12 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
    * Non-forced locks on non-MS-Windows systems that support neither
      hard nor symbolic links.  */
 
+/* Boot time is not available on Android.  */
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#undef BOOT_TIME
+#endif
+
 
 /* Return the time of the last system boot.  */
 
diff --git a/src/fns.c b/src/fns.c
index eeb65cadf3..b63f5c4966 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -3519,6 +3519,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 6e720bc285..4ea18f4ce7 100644
--- a/src/font.c
+++ b/src/font.c
@@ -180,6 +180,22 @@ font_make_entity (void)
   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));
+  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.  */
diff --git a/src/font.h b/src/font.h
index d36c45a53c..c1ab26b87c 100644
--- a/src/font.h
+++ b/src/font.h
@@ -823,6 +823,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/frame.c b/src/frame.c
index 7d902dabd4..e843af257a 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.
 
@@ -1228,6 +1232,7 @@ make_initial_frame (void)
   return f;
 }
 
+#ifndef HAVE_ANDROID
 
 static struct frame *
 make_terminal_frame (struct terminal *terminal)
@@ -1317,6 +1322,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.
@@ -1336,6 +1343,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;
@@ -1436,6 +1447,7 @@ affects all frames on the same terminal device.  */)
   f->after_make_frame = true;
 
   return frame;
+#endif
 }
 
 
@@ -5303,16 +5315,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
 }
 
 
@@ -6218,6 +6237,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");
diff --git a/src/frame.h b/src/frame.h
index dcd32036b8..e0b47d26d6 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -181,7 +181,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;
@@ -377,7 +377,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.  */
@@ -586,12 +586,13 @@ 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;
 
@@ -713,7 +714,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 +873,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 +896,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
@@ -917,11 +926,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/image.c b/src/image.c
index b881e43e95..986dd7aada 100644
--- a/src/image.c
+++ b/src/image.c
@@ -175,6 +175,28 @@ typedef struct haiku_bitmap_record Bitmap_Record;
 
 #endif
 
+#ifdef HAVE_ANDROID
+#include "androidterm.h"
+typedef struct android_bitmap_record Bitmap_Record;
+
+/* TODO: implement images on Android.  */
+#define GET_PIXEL(ximg, x, y)          0
+#define PUT_PIXEL(ximg, x, y, pixel)   ((void) (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);
@@ -831,6 +853,18 @@ 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
+  /* you lose, not yet implemented TODO */
+  return 0;
+#endif
+#endif
 }
 
 /* Free bitmap B.  */
@@ -3338,6 +3372,16 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int 
width, int height, int d
                                    Emacs_Pix_Container *pimg,
                                    Emacs_Pixmap *pixmap, Picture *picture)
 {
+#ifdef HAVE_ANDROID
+#ifdef ANDROID_STUBIFY
+  /* This function should never be called when building stubs.  */
+  emacs_abort ();
+#else
+  /* you lose, not yet implemented TODO */
+  return false;
+#endif
+#endif
+
 #ifdef USE_CAIRO
   eassert (input_blocked_p ());
 
@@ -3646,6 +3690,16 @@ image_unget_x_image_or_dc (struct image *img, bool 
mask_p,
 static Emacs_Pix_Container
 image_get_x_image (struct frame *f, struct image *img, bool mask_p)
 {
+#ifdef HAVE_ANDROID
+#ifdef ANDROID_STUBIFY
+  /* This function should never be called when building stubs.  */
+  emacs_abort ();
+#else
+  /* you lose, not yet implemented TODO */
+  return 0;
+#endif
+#endif
+
 #if defined USE_CAIRO || defined (HAVE_HAIKU)
   return !mask_p ? img->pixmap : img->mask;
 #elif defined HAVE_X_WINDOWS
@@ -3756,7 +3810,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.
@@ -6074,7 +6128,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,
@@ -6147,7 +6202,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);
@@ -6155,7 +6211,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);
@@ -6166,7 +6222,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);
@@ -6231,7 +6287,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 ();
 
@@ -6393,7 +6453,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,
@@ -6429,8 +6491,16 @@ 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
+#ifdef ANDROID_STUBIFY
+  /* This function should never be called when building stubs.  */
+  emacs_abort ();
+#else
+  /* you lose, not yet implemented TODO */
+#endif
 #endif
 }
+
 #endif /* HAVE_X_WINDOWS || USE_CAIRO || HAVE_HAIKU */
 
 /* Transform image IMG on frame F so that it looks disabled.  */
@@ -6474,7 +6544,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 */
@@ -6482,7 +6552,7 @@ image_disable_image (struct frame *f, struct image *img)
 #define MaskForeground(f)  PIX_MASK_DRAW
 #endif /* USE_CAIRO || HAVE_HAIKU */
 
-#if !defined USE_CAIRO && !defined HAVE_HAIKU
+#if !defined USE_CAIRO && !defined HAVE_HAIKU && !defined HAVE_ANDROID
       image_sync_to_pixmaps (f, img);
 #endif /* !USE_CAIRO && !HAVE_HAIKU */
       image_pixmap_draw_cross (f, img->pixmap, 0, 0, img->width, img->height,
@@ -9099,7 +9169,7 @@ gif_load (struct frame *f, struct image *img)
             `image-cache-size'.  */
          struct stat st;
          FILE *fp = fopen (SSDATA (encoded_file), "rb");
-         if (fstat (fileno (fp), &st) == 0)
+         if (sys_fstat (fileno (fp), &st) == 0)
            byte_size = st.st_size;
          fclose (fp);
        }
diff --git a/src/lisp.h b/src/lisp.h
index be511a0eb9..1f1d47f2a9 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -30,6 +30,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <inttypes.h>
 #include <limits.h>
 
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
 #include <attribute.h>
 #include <intprops.h>
 #include <verify.h>
@@ -4726,6 +4730,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 *);
@@ -5063,7 +5068,12 @@ 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
+#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]);
@@ -5101,7 +5111,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);
 
diff --git a/src/lread.c b/src/lread.c
index d838a18de5..eb029f0162 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -1047,7 +1047,7 @@ 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 (sys_fstat (fd, &st) == 0 && !S_ISREG (st.st_mode))
     return 0;
 
   /* Read the first few bytes from the file, and look for a line
@@ -1676,7 +1676,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
        {
@@ -1998,7 +1998,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)
                      {
diff --git a/src/pdumper.c b/src/pdumper.c
index e1c55d07ac..87c652da52 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -5620,7 +5620,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;
diff --git a/src/process.c b/src/process.c
index 5144c5d6c9..de1b07a81c 100644
--- a/src/process.c
+++ b/src/process.c
@@ -119,6 +119,10 @@ static struct rlimit nofile_limit;
 #include "gnutls.h"
 #endif
 
+#ifdef HAVE_ANDROID
+#include "android.h"
+#endif
+
 #ifdef HAVE_WINDOW_SYSTEM
 #include TERM_HEADER
 #endif /* HAVE_WINDOW_SYSTEM */
@@ -5679,7 +5683,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, NULL);
+#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 +5709,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. */
diff --git a/src/scroll.c b/src/scroll.c
index c643730965..6d4e0d062b 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/sysdep.c b/src/sysdep.c
index 8402ffe308..808c4af85f 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -134,6 +134,10 @@ int _cdecl _spawnlp (int, const char *, const char *, ...);
 # include <sys/socket.h>
 #endif
 
+#ifdef HAVE_ANDROID
+#include "android.h"
+#endif
+
 /* Declare here, including term.h is problematic on some systems.  */
 extern void tputs (const char *, int, int (*)(int));
 
@@ -790,6 +794,7 @@ init_sigio (int fd)
 #endif
 }
 
+#ifndef HAVE_ANDROID
 #ifndef DOS_NT
 #ifdef F_SETOWN
 static void
@@ -801,6 +806,7 @@ reset_sigio (int fd)
 }
 #endif /* F_SETOWN */
 #endif
+#endif
 
 void
 request_sigio (void)
@@ -972,6 +978,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 +987,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 +1507,8 @@ reset_sys_modes (struct tty_display_info *tty_out)
       fflush (stdout);
       return;
     }
+
+#ifndef HAVE_ANDROID
   if (!tty_out->term_initted)
     return;
 
@@ -1552,6 +1565,7 @@ reset_sys_modes (struct tty_display_info *tty_out)
 #endif
 
   widen_foreground_group (fileno (tty_out->input));
+#endif
 }
 
 #ifdef HAVE_PTYS
@@ -1802,7 +1816,11 @@ handle_arith_signal (int sig)
   xsignal0 (Qarith_error);
 }
 
-#if defined HAVE_STACK_OVERFLOW_HANDLING && !defined WINDOWSNT
+/* 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 +1932,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
@@ -2020,12 +2042,17 @@ init_signals (void)
       sigaction (SIGFPE, &action, 0);
     }
 
+  /* 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);
@@ -2051,8 +2078,10 @@ init_signals (void)
 #ifdef SIGBUS
   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
@@ -2328,11 +2357,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)
 {
@@ -2347,6 +2385,18 @@ 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
+}
+
 /* 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.
@@ -2355,6 +2405,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)
 {
@@ -2367,10 +2419,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.  */
@@ -2382,9 +2447,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;
 }
 
@@ -2434,6 +2505,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
@@ -2460,6 +2533,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
@@ -2472,9 +2547,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)
@@ -2729,6 +2812,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 f8104e0304..05c54accde 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;
 
@@ -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");
 
@@ -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
@@ -4447,6 +4536,7 @@ use the Bourne shell command 'TERM=...; export TERM' 
(C-shell:\n\
   init_sys_modes (tty);
 
   return terminal;
+#endif /* !HAVE_ANDROID */
 }
 
 
@@ -4471,8 +4561,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);
@@ -4490,6 +4585,8 @@ fatal (const char *str, ...)
 
 
 
+#ifndef HAVE_ANDROID
+
 /* Delete the given tty terminal, closing all frames on it.  */
 
 static void
@@ -4547,6 +4644,8 @@ delete_tty (struct terminal *terminal)
   xfree (tty);
 }
 
+#endif
+
 void
 syms_of_term (void)
 {
@@ -4594,21 +4693,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 c5f1e286e9..ea2a1cc330 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.  */
@@ -516,12 +517,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;
 
 
@@ -595,7 +597,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.  */
 
@@ -930,6 +933,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 d366e9d243..a0fe0fe5d3 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/verbose.mk.in b/src/verbose.mk.in
index 4ec7788442..8d89c88c27 100644
--- a/src/verbose.mk.in
+++ b/src/verbose.mk.in
@@ -32,6 +32,10 @@ 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 =
 else
 
 # Whether $(info ...) works.  This is to work around a bug in GNU Make
@@ -76,4 +80,8 @@ 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_DX                = @$(info $   DX       $@)
 endif
diff --git a/src/xdisp.c b/src/xdisp.c
index e8df230ef8..cc4c60f02d 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -16415,7 +16415,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;
@@ -26320,7 +26320,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);
 
@@ -26390,7 +26390,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)
@@ -26431,6 +26435,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)
@@ -26551,6 +26560,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
@@ -33432,7 +33444,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.  */
@@ -36104,14 +36118,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
diff --git a/src/xfaces.c b/src/xfaces.c
index 663386dc25..0f85a75339 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
  ***********************************************************************/
@@ -6951,19 +6988,21 @@ 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);
-         }
+             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);
     }
   unblock_input ();
diff --git a/xcompile/Makefile.in b/xcompile/Makefile.in
new file mode 100644
index 0000000000..a63dbc2bb8
--- /dev/null
+++ b/xcompile/Makefile.in
@@ -0,0 +1,209 @@
+### @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@
+top_builddir = @top_builddir@
+
+# 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.
+
+# Finally, the following commands are executed in order, to produce
+# libgnu.a, various binaries in lib-src, and src/aemacs:
+#   make -C lib libgnu.a
+#   make -C lib-src src/aemacs
+#   make -C src aemacs
+
+# This is possibly the ugliest Makefile ever written!
+
+LIB_SRCDIR = $(realpath $(top_srcdir)/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=lib src lib-src
+
+.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 trick lib/gnulib.mk, it is not actually useful.
+config.status:
+       touch config.status
+
+src/verbose.mk: verbose.mk.android
+       mkdir -p src
+       cp -f verbose.mk.android src/verbose.mk
+
+UGLY_HOST_HEADERS = dirent.h stdlib.h sys/random.h limits.h            \
+                   string.h signal.h time.h inttypes.h assert.h        \
+                   stdint.h unistd.h stdlib.h sys/types.h sys/time.h   \
+                   sys/stat.h getopt.h fcntl.h sys/select.h alloca.h   \
+                   stdio.h sys/random.h
+
+# 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:
+       make -C $(top_builddir)/lib-src make-fingerprint
+
+.PHONY: $(top_builddir)/lib-src/make-docfile
+$(top_builddir)/lib-src/make-docfile:
+       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
+
+.PHONY: lib/libgnu.a
+lib/libgnu.a: src/verbose.mk config.status $(PRE_BUILD_DEPS)
+       mkdir -p lib/deps lib/deps/malloc
+# Temporarily move config.h to config.h.bak and config.h.android to
+# config.h
+       cp -f -p $(top_builddir)/src/config.h.android lib/config.h
+# And the Makefiles.
+       cp -f -p $(top_builddir)/lib/gnulib.mk.android lib/gnulib.mk
+       cp -f -p $(top_builddir)/lib/Makefile.android lib/Makefile
+# Next, move srcdir and top_srcdir in the Makefiles copied.
+       sed -i 's/srcdir =.*$$/srcdir = $(subst /,\/,$(LIB_SRCDIR))/g' 
lib/Makefile
+       sed -i 's/top_srcdir =.*$$/top_srcdir = $(subst 
/,\/,$(LIB_TOP_SRCDIR))/g' \
+         lib/Makefile
+       sed -i 's/srcdir =.*$$/srcdir = $(subst /,\/,$(LIB_SRCDIR))/g' 
lib/gnulib.mk
+# Ugly hack: hide some troublesome headers in $(top_builddir)/lib
+# while building lib.  Otherwise, they will end up overriding the
+# system headers used on Android through #include_next and cause
+# trouble.
+       mkdir -p sys
+       for ugly_header in $(UGLY_HOST_HEADERS); do                     \
+         if [ -e "$(top_builddir)/lib/$$ugly_header" ]; then           \
+           mv -f $(top_builddir)/lib/$$ugly_header $$ugly_header.bak;  \
+         fi                                                            \
+       done
+# And make libgnu.a.  Restore config.h if it fails.
+       -make -C lib libgnu.a
+# Move the hiden headers back
+       for ugly_header in $(UGLY_HOST_HEADERS); do                     \
+         if [ -e "$$ugly_header.bak" ]; then                           \
+           mv -f $$ugly_header.bak $(top_builddir)/lib/$$ugly_header;  \
+         fi                                                            \
+       done
+
+src/Makefile src/config.h &: $(top_builddir)/src/config.h.android      \
+  $(top_builddir)/src/Makefile.android $(PRE_BUILD_DEPS)
+       mkdir -p src src/deps
+# Copy config.h to src/
+       cp -f -p $(top_builddir)/src/config.h.android src/config.h
+# And the Makefile.
+       cp -f -p $(top_builddir)/src/Makefile.android src/Makefile
+# Next, edit srcdir and top_srcdir to the right location.
+       sed -i 's/srcdir =.*$$/srcdir = $(subst /,\/,$(SRC_SRCDIR))/g' 
src/Makefile
+       sed -i 's/top_srcdir =.*$$/top_srcdir = $(subst 
/,\/,$(LIB_TOP_SRCDIR))/g' \
+         src/Makefile
+# Edit references to ../admin/unidata to read ../../admin/unidata.
+       sed -i 's/\.\.\/admin\/unidata/..\/..\/admin\/unidata/g' src/Makefile
+       sed -i 's/\.\.\/admin\/charsets/..\/..\/admin\/charsets/g' src/Makefile
+# 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.
+       sed -i 's/libsrc =.*$$/libsrc = \.\.\/\.\.\/lib-src/g' src/Makefile
+
+.PHONY: src/android-emacs src/libemacs.so
+src/android-emacs src/libemacs.so &: src/Makefile src/config.h \
+  src/verbose.mk lib/libgnu.a $(PRE_BUILD_DEPS)
+# Ugly hack: hide some troublesome headers in $(top_builddir)/lib
+# while building lib.  Otherwise, they will end up overriding the
+# system headers used on Android through #include_next and cause
+# trouble.
+       mkdir -p sys
+       for ugly_header in $(UGLY_HOST_HEADERS); do                     \
+         if [ -e "$(top_builddir)/lib/$$ugly_header" ]; then           \
+           mv -f $(top_builddir)/lib/$$ugly_header $$ugly_header.bak;  \
+         fi                                                            \
+       done
+# Finally, go into src and make
+       +make -C src android-emacs libemacs.so
+# Move the hidden headers back
+       for ugly_header in $(UGLY_HOST_HEADERS); do                     \
+         if [ -e "$$ugly_header.bak" ]; then                           \
+           mv -f $$ugly_header.bak $(top_builddir)/lib/$$ugly_header;  \
+         fi                                                            \
+       done
+
+lib-src/Makefile: $(top_builddir)/lib-src/Makefile.android
+       mkdir -p lib-src
+       cp -f -p $< $@
+
+.PHONY: $(LIBSRC_BINARIES)
+$(LIBSRC_BINARIES) &: src/verbose.mk $(top_builddir)/$@ lib/libgnu.a   \
+  src/config.h lib-src/Makefile $(PRE_BUILD_DEPS)
+       mkdir -p src lib-src
+# Next, edit srcdir and top_srcdir to the right location.
+       sed -i 's/srcdir=.*$$/srcdir = $(subst /,\/,$(LIB_SRC_SRCDIR))/g' \
+         lib-src/Makefile
+       sed -i 's/top_srcdir=.*$$/top_srcdir = $(subst 
/,\/,$(LIB_SRC_TOP_SRCDIR))/g' \
+         lib-src/Makefile
+# Edit out SCRIPTS, it interferes with the build.
+       sed -i 's/^SCRIPTS=.*$$/SCRIPTS=/g' lib-src/Makefile
+# Ugly hack: hide some troublesome headers in $(top_builddir)/lib
+# while building lib.  Otherwise, they will end up overriding the
+# system headers used on Android through #include_next and cause
+# trouble.
+       mkdir -p sys
+       for ugly_header in $(UGLY_HOST_HEADERS); do                     \
+         if [ -e "$(top_builddir)/lib/$$ugly_header" ]; then           \
+           mv -f $(top_builddir)/lib/$$ugly_header $$ugly_header.bak;  \
+         fi                                                            \
+       done
+# Finally, go into lib-src and make everything being built
+       +make -C lib-src $(foreach bin,$(LIBSRC_BINARIES),$(notdir $(bin)))
+# Move the hidden headers back
+       for ugly_header in $(UGLY_HOST_HEADERS); do                     \
+         if [ -e "$$ugly_header.bak" ]; then                           \
+           mv -f $$ugly_header.bak $(top_builddir)/lib/$$ugly_header;  \
+         fi                                                            \
+       done
+
+.PHONY: clean maintainer-clean
+clean:
+       rm -rf $(CLEAN_SUBDIRS) *.bak sys
+
+maintainer-clean: clean
diff --git a/xcompile/README b/xcompile/README
new file mode 100644
index 0000000000..a631b978da
--- /dev/null
+++ b/xcompile/README
@@ -0,0 +1,7 @@
+This directory holds Makefiles and other required assets to build an
+Emacs binary independently for another toolchain, which is currently
+required when building for Android.
+
+The files here are extremely ugly, and contain rules that cannot be
+interrupted without leaving the build tree in an unsafe state.  At
+some point that should be fixed!
diff --git a/xcompile/langinfo.h b/xcompile/langinfo.h
new file mode 100644
index 0000000000..b296ba8db8
--- /dev/null
+++ b/xcompile/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"



reply via email to

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