emacs-diffs
[Top][All Lists]
Advanced

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

feature/android f9732131cf 1/6: Update Android port


From: Po Lu
Subject: feature/android f9732131cf 1/6: Update Android port
Date: Fri, 13 Jan 2023 03:09:25 -0500 (EST)

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

    Update Android port
    
    * configure.ac (ANDROID_MIN_SDK): New variable.
    (DX): Remove and replace with D8.
    (XCONFIGURE): Check for the minimum version of Android the cross
    compiler compiles for.  Generate java/AndroidManifest.xml from
    java/AndroidManifest.xml.in.  Allow using Zlib on Android.
    
    * java/AndroidManifest.xml.in: New file.  Use the minimum SDK
    detected by configure.
    
    * java/Makefile.in (top_srcdir, version): New variables.
    (DX, D8): Replace with D8.
    (ANDROID_MIN_SDK, APK_NAME): New variables.
    (.PHONY):
    (.PRECIOUS):
    (classes.dex):
    (emacs.apk): Generate $(APK_NAME) instead of `emacs.apk'.
    
    * java/debug.sh: New option --attach-existing.  Attach to an
    existing Emacs instance when specified.
    
    * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): New
    field `isPaused'.
    (invalidateFocus1): Fix infinite recursion.
    (detachWindow): Deiconify window.
    (attachWindow): Iconify the window if the activity is paused.
    (onCreate): Use the ``no title bar'' theme.
    (onPause, onResume): New functions.
    * java/org/gnu/emacs/EmacsNative.java (sendTouchUp, sendTouchDown)
    (sendTouchMove, sendWheel, sendIconified, sendDeiconified): New
    functions.
    * java/org/gnu/emacs/EmacsSdk7FontDriver.java (Sdk7Typeface):
    (list): Remove logging for code that is mostly going to be unused.
    * java/org/gnu/emacs/EmacsService.java (ringBell, queryTree)
    (getScreenWidth, getScreenHeight, detectMouse): New functions.
    * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView)
    (surfaceChanged, surfaceCreated, surfaceDestroyed): Add extra
    debug logging.  Avoid deadlock in surfaceCreated.
    
    * java/org/gnu/emacs/EmacsView.java (EmacsView): Try very hard
    to make the SurfaceView respect Z order.  It didn't work.
    (handleDirtyBitmap): Copy over the contents from the old bitmap.
    (explicitlyDirtyBitmap): New function.
    (onLayout): Don't dirty bitmap if unnecessary.
    (damageRect, swapBuffers): Don't synchronize so hard.
    (onTouchEvent): Call window.onTouchEvent instead.
    (moveChildToBack, raise, lower): New functions.
    
    * java/org/gnu/emacs/EmacsWindow.java (Coordinate): New
    subclass.
    (pointerMap, isMapped, isIconified, dontFocusOnMap)
    (dontAcceptFocus): New fields.
    (EmacsWindow): Don't immediately register unmapped window.
    (viewLayout): Send configure event outside the lock.
    (requestViewLayout): Explicitly dirty the bitmap.
    (mapWindow): Register the window now.  Respect dontFocusOnMap.
    (unmapWindow): Unregister the window now.
    (figureChange, onTouchEvent): New functions.
    (onSomeKindOfMotionEvent): Handle scroll wheel events.
    (reparentTo, makeInputFocus, raise, lower, getWindowGeometry)
    (noticeIconified, noticeDeiconified, setDontAcceptFocus)
    (setDontFocusOnMap, getDontFocusOnMap): New functions.
    * java/org/gnu/emacs/EmacsWindowAttachmentManager.java
    (registerWindow, detachWindow): Synchronize.
    (noticeIconified, noticeDeiconified): New functions.
    (copyWindows): New function.
    
    * lisp/frame.el (frame-geometry, frame-edges)
    (mouse-absolute-pixel-position, set-mouse-absolute-pixel-position)
    (frame-list-z-order, frame-restack, display-mouse-p)
    (display-monitor-attributes-list): Implement on Android.
    
    * lisp/mwheel.el (mouse-wheel-down-event):
    (mouse-wheel-up-event):
    (mouse-wheel-left-event):
    (mouse-wheel-right-event): Define on Android.
    
    * src/android.c (struct android_emacs_service): New methods
    `ringBell', `queryTree', `getScreenWidth', `getScreenHeight',
    and `detectMouse'.
    (struct android_event_queue, android_init_events)
    (android_next_event, android_write_event): Remove write limit.
    (android_file_access_p): Handle directories correcty.
    (android_close): Fix coding style.
    (android_fclose): New function.
    (android_init_emacs_service): Initialize new methods.
    (android_reparent_window): Implement function.
    (android_bell, android_set_input_focus, android_raise_window)
    (android_lower_window, android_query_tree, android_get_geometry)
    (android_get_screen_width, android_get_screen_height)
    (android_get_mm_width, android_get_mm_height, android_detect_mouse)
    (android_set_dont_focus_on_map, android_set_dont_accept_focus):
    New functions.
    (struct android_dir): New structure.
    (android_opendir, android_readdir, android_closedir): New
    functions.
    (emacs_abort): Implement here on Android and poke debuggerd into
    generating a tombstone.
    
    * src/android.h: Update prototypes.
    
    * src/androidfns.c (android_set_parent_frame): New function.
    (android_default_font_parameter): Use sane font size by default.
    (Fx_display_pixel_width, Fx_display_pixel_height)
    (Fx_display_mm_width, Fx_display_mm_height)
    (Fx_display_monitor_attributes_list): Rename to start with
    `android-'.  Implement.  Fiddle with documentation to introduce
    Android specific nuances.
    (Fandroid_display_monitor_attributes_list): New function.
    (Fx_frame_geometry, frame_geometry): New function.
    (Fandroid_frame_geometry): Implement correctly.
    (Fx_frame_list_z_order): Rename to start with `android-'.
    (android_frame_list_z_order, Fandroid_frame_list_z_order):
    Implement.
    (Fx_frame_restack): Rename to start with `android-'.
    (Fandroid_frame_restack): ``Implement''.
    (Fx_mouse_absolute_pixel_position): Rename to start with
    `android-'.
    (Fandroid_mouse_absolute_pixel_position): ``Implement''.
    (Fx_set_mouse_absolute_pixel_position): Rename to start with
    `android-'.
    (Fandroid_set_mouse_absolute_pixel_position): ``Implement''.
    (Fandroid_detect_mouse): New function.
    (android_set_menu_bar_lines): Use FRAME_ANDROID_DRAWABLE when
    clearing area.
    (android_set_no_focus_on_map, android_set_no_accept_focus): New
    functions.
    (android_frame_parm_handlers): Register new frame parameter
    handlers.
    (syms_of_androidfns): Update appropriately.
    
    * src/androidfont.c (androidfont_draw): Use
    FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW.
    
    * src/androidgui.h (enum android_event_type): New events.
    (struct android_touch_event, struct android_wheel_event)
    (struct android_iconify_event): New structures.
    (union android_event): Add new events.
    
    * src/androidterm.c (android_clear_frame): Use
    FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW.
    (android_flash, android_ring_bell): Implement bell ringing.
    (android_toggle_invisible_pointer): Don't TODO function that
    can't be implemented.
    (show_back_buffer, android_flush_dirty_back_buffer_on): Check if
    a buffer flip is required before doing the flip.
    (android_lower_frame, android_raise_frame): Implement functions.
    (android_update_tools, android_find_tool): New functions.
    (handle_one_android_event): Handle new iconification, wheel and
    touch events.
    (android_read_socket): Implement pending-autoraise-frames.
    (android_frame_up_to_date): Implement bell ringing.
    (android_buffer_flipping_unblocked_hook): Check if a buffer flip
    is required before doing the flip.
    (android_focus_frame, android_frame_highlight)
    (android_frame_unhighlight): New function.
    (android_frame_rehighlight): Implement functions.
    (android_iconify_frame): Always display error.
    (android_set_alpha): Update commentary.
    (android_free_frame_resources): Free frame touch points.
    (android_scroll_run, android_flip_and_flush)
    (android_clear_rectangle, android_draw_fringe_bitmap)
    (android_draw_glyph_string_background, android_fill_triangle)
    (android_clear_point, android_draw_relief_rect)
    (android_draw_box_rect, android_draw_glyph_string_bg_rect)
    (android_draw_image_foreground, android_draw_stretch_glyph_string)
    (android_draw_underwave, android_draw_glyph_string_foreground)
    (android_draw_composite_glyph_string_foreground)
    (android_draw_glyphless_glyph_string_foreground)
    (android_draw_glyph_string, android_clear_frame_area)
    (android_clear_under_internal_border, android_draw_hollow_cursor)
    (android_draw_bar_cursor, android_draw_vertical_window_border)
    (android_draw_window_divider): Use FRAME_ANDROID_DRAWABLE
    instead of FRAME_ANDROID_WINDOW for drawing operations.
    
    * src/androidterm.h (struct android_touch_point): New structure.
    (struct android_output): New fields.
    (FRAME_ANDROID_NEED_BUFFER_FLIP): New macro.
    
    * src/dired.c (emacs_readdir, open_directory)
    (directory_files_internal_unwind, read_dirent)
    (directory_files_internal, file_name_completion): Add
    indirection over readdir and opendir.  Use android variants on
    Android.
    
    * src/dispnew.c (Fopen_termscript):
    * src/fileio.c (fclose_unwind): Use emacs_fclose.
    (Faccess_file): Call android_file_access_p.
    (file_accessible_directory_p): Append right suffix to Android
    assets directory.
    (do_auto_save_unwind): Use emacs_fclose.
    * src/keyboard.c (lispy_function_keys): Use right function key
    for page up and page down.
    (Fopen_dribble_file): Use emacs_fclose.
    
    * src/lisp.h: New prototype emacs_fclose.
    
    * src/lread.c (close_infile_unwind): Use emacs_fclose.
    
    * src/sfnt.c (sfnt_curve_is_flat): Fix area-squared computation.
    (sfnt_prepare_raster): Compute raster width and height
    consistently with outline building.
    (sfnt_build_outline_edges): Use the same offsets used to set
    offy and offx.
    (main): Adjust debug code.
    
    * src/sfntfont-android.c (sfntfont_android_saturate32): Delete
    function.
    (sfntfont_android_blend, sfntfont_android_blendrgb): Remove
    unnecessary debug code.
    (sfntfont_android_composite_bitmap): Prevent out of bounds
    write.
    (sfntfont_android_put_glyphs): Use FRAME_ANDROID_DRAWABLE.
    (init_sfntfont_android): Initialize Monospace Serif font to
    something sensible.
    * src/sfntfont.c (sfntfont_text_extents): Clear glyph metrics
    before summing up pcm.
    (sfntfont_draw): Use s->font instead of s->face->font.
    
    * src/sysdep.c (emacs_fclose): Wrap around android_fclose on
    android.
    
    * src/term.c (Fsuspend_tty):
    (delete_tty): Use emacs_fclose.
    * src/verbose.mk.in (AM_V_DX): Replace with D8 version.
---
 configure.ac                                       |  35 +-
 java/AndroidManifest.xml.in                        |  84 +++
 java/Makefile.in                                   |  30 +-
 java/debug.sh                                      |  45 +-
 java/org/gnu/emacs/EmacsActivity.java              |  37 +-
 java/org/gnu/emacs/EmacsNative.java                |  25 +-
 java/org/gnu/emacs/EmacsSdk7FontDriver.java        |   7 -
 java/org/gnu/emacs/EmacsService.java               | 116 ++++
 java/org/gnu/emacs/EmacsSurfaceView.java           |  22 +-
 java/org/gnu/emacs/EmacsView.java                  | 189 +++++--
 java/org/gnu/emacs/EmacsWindow.java                | 498 +++++++++++++++--
 .../gnu/emacs/EmacsWindowAttachmentManager.java    |  54 +-
 lisp/frame.el                                      |  32 +-
 lisp/mwheel.el                                     |  12 +-
 src/android.c                                      | 551 ++++++++++++++++++-
 src/android.h                                      |  23 +
 src/androidfns.c                                   | 471 ++++++++++++++--
 src/androidfont.c                                  |   8 +-
 src/androidgui.h                                   |  83 +++
 src/androidterm.c                                  | 589 ++++++++++++++++++---
 src/androidterm.h                                  |  28 +
 src/dired.c                                        |  50 +-
 src/dispnew.c                                      |   3 +-
 src/fileio.c                                       |  42 +-
 src/keyboard.c                                     |   6 +-
 src/lisp.h                                         |   2 +
 src/lread.c                                        |   2 +-
 src/sfnt.c                                         |  35 +-
 src/sfntfont-android.c                             |  41 +-
 src/sfntfont.c                                     |   5 +-
 src/sysdep.c                                       |  17 +-
 src/term.c                                         |  10 +-
 src/verbose.mk.in                                  |   2 +-
 33 files changed, 2809 insertions(+), 345 deletions(-)

diff --git a/configure.ac b/configure.ac
index 68de5f02b9..333a28cc14 100644
--- a/configure.ac
+++ b/configure.ac
@@ -816,8 +816,8 @@ a valid path to android.jar.  See config.log for more 
details.])
 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_PATH_PROGS([D8], [d8], [], "${SDK_BUILD_TOOLS}:$PATH")
+  if test "D8" = ""; then
     AC_MSG_ERROR([The Android dexer was not found.
 Please verify that the path to the SDK build tools you specified is correct])
   fi
@@ -867,6 +867,25 @@ in the ANDROID_CC variable when you ran configure.])
 
   ANDROID_ABI=$android_abi
 
+  dnl Obtain the minimum SDK version of the resulting Emacs binary
+  dnl built with this NDK.
+
+  ANDROID_MIN_SDK=8
+  AC_MSG_CHECKING([for the lowest Android version Emacs can run on])
+  [android_sdk=`echo "$cc_target" | grep -oE 'android([0-9][0-9]?)'`]
+
+  if test -n "$android_sdk"; then
+    android_sdk=`echo "$android_sdk" | sed -n 's/android//p'`
+    AC_MSG_RESULT([$android_sdk])
+    ANDROID_MIN_SDK=$android_sdk
+  else
+    AC_MSG_RESULT([unknown ($cc_target); assuming 8])
+    AC_MSG_WARN([configure could not determine the versions of Android \
+a binary built with this compiler will run on.  The generated application \
+package will likely install on older systems but crash on startup.])
+  fi
+  AC_SUBST([ANDROID_MIN_SDK])
+
   # Save confdefs.h and config.log for now.
   mv -f confdefs.h _confdefs.h
   mv -f config.log _config.log
@@ -899,7 +918,7 @@ fi
 AC_SUBST([ANDROID])
 AC_SUBST([JAVAC])
 AC_SUBST([AAPT])
-AC_SUBST([DX])
+AC_SUBST([D8])
 AC_SUBST([ZIPALIGN])
 AC_SUBST([ANDROID_JAR])
 AC_SUBST([ANDROID_ABI])
@@ -914,7 +933,7 @@ fi
 AC_SUBST([XCONFIGURE])
 
 if test "$ANDROID" = "yes"; then
-  # When --with-android is specified, all build options must be
+  # When --with-android is specified, almost all build options must be
   # disabled, both within the recursive invocation of configure and
   # outside.
   with_xpm=no
@@ -938,11 +957,12 @@ if test "$ANDROID" = "yes"; then
   with_gpm=no
   with_dbus=no
   with_gsettings=no
-  with_selinx=no
+  with_selinux=no
   with_gnutls=no
-  with_zlib=no
   with_modules=no
   with_threads=no
+
+  # zlib is available in android.
 fi
 
 dnl This used to use changequote, but, apart from 'changequote is evil'
@@ -7211,6 +7231,9 @@ fi
 ARCH_INDEPENDENT_CONFIG_FILES([java/Makefile])
 ARCH_INDEPENDENT_CONFIG_FILES([xcompile/Makefile])
 
+# Make java/AndroidManifest.xml
+ARCH_INDEPENDENT_CONFIG_FILES([java/AndroidManifest.xml])
+
 AC_OUTPUT
 
 if test ! "$with_mailutils"; then
diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in
new file mode 100644
index 0000000000..b680137a9d
--- /dev/null
+++ b/java/AndroidManifest.xml.in
@@ -0,0 +1,84 @@
+<!-- @configure_input@
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>. -->
+
+<!-- targetSandboxVersion must be 1.  Otherwise, fascist security
+     restrictions prevent Emacs from making HTTP connections.  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android";
+         package="org.gnu.emacs"
+         android:targetSandboxVersion="1"
+         android:installLocation="auto"
+         android:versionName="@version@">
+
+  <!-- Paste in every permission in existence so Emacs can do
+       anything.  -->
+
+  <uses-permission android:name="android.permission.READ_CONTACTS" />
+  <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+  <uses-permission android:name="android.permission.VIBRATE" />
+  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+  <uses-permission android:name="android.permission.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="@ANDROID_MIN_SDK@"
+           android:targetSdkVersion="28"/>
+
+  <application android:name="org.gnu.emacs.EmacsApplication"
+              android:label="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"
+             android:launchMode="singleTop"
+             
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
+      <intent-filter>
+        <action android:name="android.intent.action.MAIN" />
+        <category android:name="android.intent.category.DEFAULT" />
+        <category android:name="android.intent.category.LAUNCHER" />
+      </intent-filter>
+    </activity>
+
+    <activity android:name="org.gnu.emacs.EmacsMultitaskActivity"
+             
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"/>
+
+    <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
index 31bd22b5a2..05e61dede8 100644
--- a/java/Makefile.in
+++ b/java/Makefile.in
@@ -18,13 +18,15 @@
 # along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
 
 top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+version = @version@
 
 -include ${top_builddir}/src/verbose.mk
 
 SHELL = @SHELL@
 JAVAC = @JAVAC@
 AAPT = @AAPT@
-DX = @DX@
+D8 = @D8@
 ZIPALIGN = @ZIPALIGN@
 JARSIGNER = @JARSIGNER@
 ANDROID_JAR = @ANDROID_JAR@
@@ -39,6 +41,12 @@ SIGN_EMACS = -keystore emacs.keystore -storepass emacs1
 JAVA_FILES = $(shell find . -type f -name *.java)
 CLASS_FILES = $(foreach file,$(JAVA_FILES),$(basename $(file)).class)
 
+# Compute the name for the Emacs application package.  This should be:
+# emacs-<version>-<min-sdk>-<abi>.apk
+
+ANDROID_MIN_SDK = @ANDROID_MIN_SDK@
+APK_NAME = emacs-$(version)-$(ANDROID_MIN_SDK)-$(ANDROID_ABI).apk
+
 # How this stuff works.
 
 # emacs.apk depends on emacs.apk-in, which is simply a ZIP archive
@@ -55,7 +63,7 @@ CLASS_FILES = $(foreach file,$(JAVA_FILES),$(basename 
$(file)).class)
 #  assets/lisp/
 
 .PHONY: emacs.apk-in all
-all: emacs.apk
+all: $(APK_NAME)
 
 # Binaries to cross-compile.
 CROSS_BINS = ../xcompile/src/android-emacs ../xcompile/lib-src/ctags   \
@@ -118,6 +126,18 @@ emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) 
AndroidManifest.xml
        pushd install_temp; $(AAPT) add ../$@ `find assets -type f`; popd
        rm -rf install_temp
 
+# Makefile itself.
+.PRECIOUS: ../config.status Makefile
+../config.status: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4
+       $(MAKE) -C $(dir $@) $(notdir $@)
+Makefile: ../config.status $(top_builddir)/java/Makefile.in
+       $(MAKE) -C .. java/$@
+
+# AndroidManifest.xml:
+AndroidManifest.xml: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4 \
+  AndroidManifest.xml.in
+       pushd ..; ./config.status java/AndroidManifest.xml; popd
+
 .SUFFIXES: .java .class
 .java.class &:
        $(AM_V_JAVAC) $(JAVAC) $(JAVAFLAGS) $<
@@ -126,7 +146,7 @@ emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) 
AndroidManifest.xml
 # nested classes.
 
 classes.dex: $(CLASS_FILES)
-       $(AM_V_DX) $(DX) --classpath $(ANDROID_JAR) \
+       $(AM_V_D8) $(D8) --classpath $(ANDROID_JAR) \
          $(subst $$,\$$,$(shell find . -type f -name *.class))
 
 # When emacs.keystore expires, regenerate it with:
@@ -136,7 +156,7 @@ classes.dex: $(CLASS_FILES)
 
 .PHONY: clean maintainer-clean
 
-emacs.apk: classes.dex emacs.apk-in emacs.keystore
+$(APK_NAME): classes.dex emacs.apk-in emacs.keystore
        cp -f emacs.apk-in $@.unaligned
        $(AAPT) add $@.unaligned classes.dex
        $(JARSIGNER) $(SIGN_EMACS) $@.unaligned "Emacs keystore"
@@ -144,7 +164,7 @@ emacs.apk: classes.dex emacs.apk-in emacs.keystore
        rm -f $@.unaligned
 
 clean:
-       rm -f emacs.apk emacs.apk-in *.dex *.unaligned *.class
+       rm -f *.apk emacs.apk-in *.dex *.unaligned *.class
        rm -rf install-temp
        find . -name '*.class' -delete
 
diff --git a/java/debug.sh b/java/debug.sh
index dd710dc31a..3e3e3d9c28 100755
--- a/java/debug.sh
+++ b/java/debug.sh
@@ -30,6 +30,7 @@ activity=org.gnu.emacs.EmacsActivity
 gdb_port=5039
 jdb_port=64013
 jdb=no
+attach_existing=no
 
 while [ $# -gt 0 ]; do
     case "$1" in
@@ -48,6 +49,7 @@ while [ $# -gt 0 ]; do
            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 "  --attach-existing   attach to an existing process"
            echo "  --help              print this message"
            echo ""
            echo "Available devices:"
@@ -63,6 +65,9 @@ while [ $# -gt 0 ]; do
        "--port" )
            gdb_port=$1
            ;;
+       "--attach-existing" )
+           attach_existing=yes
+           ;;
        "--" )
            shift
            gdbargs=$@
@@ -120,30 +125,32 @@ package_pids=`awk -- '{
     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
+if [ "$attach_existing" != "yes" ]; then
+    # Finally, kill each existing process.
+    for pid in $package_pids; do
+       echo "Killing existing process $pid..."
+       adb -s $device shell run-as $package kill -9 $pid &> /dev/null
+    done
+
+    # Now run the main activity.  This must be done as the adb user and
+    # not as the package user.
+    echo "Starting activity $activity and attaching debugger"
+
+    # Exit if the activity could not be started.
+    adb -s $device shell am start -D "$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`
+    # 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 -- '{
+    # Next, remove lines matching "ps" itself.
+    package_pids=`awk -- '{
   if (!match ($0, /(PID|ps)/))
     print $1
 }' <<< $package_pids`
+fi
 
 pid=$package_pids
 num_pids=`wc -w <<< "$package_pids"`
diff --git a/java/org/gnu/emacs/EmacsActivity.java 
b/java/org/gnu/emacs/EmacsActivity.java
index 67f411a38c..2b66102484 100644
--- a/java/org/gnu/emacs/EmacsActivity.java
+++ b/java/org/gnu/emacs/EmacsActivity.java
@@ -48,6 +48,9 @@ public class EmacsActivity extends Activity
   /* The currently focused window.  */
   public static EmacsWindow focusedWindow;
 
+  /* Whether or not this activity is paused.  */
+  private boolean isPaused;
+
   static
   {
     focusedActivities = new ArrayList<EmacsActivity> ();
@@ -60,7 +63,7 @@ public class EmacsActivity extends Activity
       focusedWindow = window;
 
     for (EmacsWindow child : window.children)
-      invalidateFocus1 (window);
+      invalidateFocus1 (child);
   }
 
   public static void
@@ -103,6 +106,9 @@ public class EmacsActivity extends Activity
        /* Clear the window's pointer to this activity and remove the
           window's view.  */
        window.setConsumer (null);
+
+       /* The window can't be iconified any longer.  */
+       window.noticeDeiconified ();
        layout.removeView (window.view);
        window = null;
 
@@ -114,6 +120,8 @@ public class EmacsActivity extends Activity
   public void
   attachWindow (EmacsWindow child)
   {
+    Log.d (TAG, "attachWindow: " + child);
+
     if (window != null)
       throw new IllegalStateException ("trying to attach window when one"
                                       + " already exists");
@@ -124,6 +132,10 @@ public class EmacsActivity extends Activity
     layout.addView (window.view);
     child.setConsumer (this);
 
+    /* If the activity is iconified, send that to the window.  */
+    if (isPaused)
+      window.noticeIconified ();
+
     /* Invalidate the focus.  */
     invalidateFocus ();
   }
@@ -148,6 +160,9 @@ public class EmacsActivity extends Activity
   {
     FrameLayout.LayoutParams params;
 
+    /* Set the theme to one without a title bar.  */
+    setTheme (android.R.style.Theme_NoTitleBar);
+
     params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT,
                                           LayoutParams.MATCH_PARENT);
 
@@ -192,4 +207,24 @@ public class EmacsActivity extends Activity
 
     invalidateFocus ();
   }
+
+  @Override
+  public void
+  onPause ()
+  {
+    isPaused = true;
+
+    EmacsWindowAttachmentManager.MANAGER.noticeIconified (this);
+    super.onResume ();
+  }
+
+  @Override
+  public void
+  onResume ()
+  {
+    isPaused = false;
+
+    EmacsWindowAttachmentManager.MANAGER.noticeDeiconified (this);
+    super.onResume ();
+  }
 };
diff --git a/java/org/gnu/emacs/EmacsNative.java 
b/java/org/gnu/emacs/EmacsNative.java
index ae48ce3040..a11e509cd7 100644
--- a/java/org/gnu/emacs/EmacsNative.java
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -96,11 +96,34 @@ public class EmacsNative
                                             long time, int state,
                                             int button);
 
-    /* Send an ANDROID_BUTTON_RELEASE event.  */
+  /* Send an ANDROID_BUTTON_RELEASE event.  */
   public static native void sendButtonRelease (short window, int x, int y,
                                               long time, int state,
                                               int button);
 
+  /* Send an ANDROID_TOUCH_DOWN event.  */
+  public static native void sendTouchDown (short window, int x, int y,
+                                          long time, int pointerID);
+
+  /* Send an ANDROID_TOUCH_UP event.  */
+  public static native void sendTouchUp (short window, int x, int y,
+                                        long time, int pointerID);
+
+  /* Send an ANDROID_TOUCH_MOVE event.  */
+  public static native void sendTouchMove (short window, int x, int y,
+                                          long time, int pointerID);
+
+  /* Send an ANDROID_WHEEL event.  */
+  public static native void sendWheel (short window, int x, int y,
+                                      long time, int state,
+                                      float xDelta, float yDelta);
+
+  /* Send an ANDROID_ICONIFIED event.  */
+  public static native void sendIconified (short window);
+
+  /* Send an ANDROID_DEICONIFIED event.  */
+  public static native void sendDeiconified (short window);
+
   static
   {
     System.loadLibrary ("emacs");
diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java 
b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
index 8a9426050a..c0f24c7433 100644
--- a/java/org/gnu/emacs/EmacsSdk7FontDriver.java
+++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
@@ -149,8 +149,6 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
        }
       else
        familyName = fileName;
-
-      Log.d (TAG, "Initialized new typeface " + familyName);
     }
 
     @Override
@@ -321,17 +319,12 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
 
     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]);
   }
 
diff --git a/java/org/gnu/emacs/EmacsService.java 
b/java/org/gnu/emacs/EmacsService.java
index 4444b7f1c5..01a1695f38 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -28,20 +28,27 @@ import android.graphics.Bitmap;
 import android.graphics.Point;
 
 import android.view.View;
+import android.view.InputDevice;
 
 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.os.Vibrator;
+import android.os.VibratorManager;
+import android.os.VibrationEffect;
 
 import android.util.Log;
 import android.util.DisplayMetrics;
 
+import android.hardware.input.InputManager;
+
 class Holder<T>
 {
   T thing;
@@ -250,4 +257,113 @@ public class EmacsService extends Service
   {
     window.clearArea (x, y, width, height);
   }
+
+  @SuppressWarnings ("deprecation")
+  public void
+  ringBell ()
+  {
+    Vibrator vibrator;
+    VibrationEffect effect;
+    VibratorManager vibratorManager;
+    Object tem;
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
+      {
+       tem = getSystemService (Context.VIBRATOR_MANAGER_SERVICE);
+       vibratorManager = (VibratorManager) tem;
+        vibrator = vibratorManager.getDefaultVibrator ();
+      }
+    else
+      vibrator
+       = (Vibrator) getSystemService (Context.VIBRATOR_SERVICE);
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+      {
+       effect
+         = VibrationEffect.createOneShot (50,
+                                          VibrationEffect.DEFAULT_AMPLITUDE);
+       vibrator.vibrate (effect);
+      }
+    else
+      vibrator.vibrate (50);
+  }
+
+  public short[]
+  queryTree (EmacsWindow window)
+  {
+    short[] array;
+    List<EmacsWindow> windowList;
+    int i;
+
+    if (window == null)
+      /* Just return all the windows without a parent.  */
+      windowList = EmacsWindowAttachmentManager.MANAGER.copyWindows ();
+    else
+      windowList = window.children;
+
+    array = new short[windowList.size () + 1];
+    i = 1;
+
+    array[0] = window.parent != null ? 0 : window.parent.handle;
+
+    for (EmacsWindow treeWindow : windowList)
+      array[i++] = treeWindow.handle;
+
+    return array;
+  }
+
+  public int
+  getScreenWidth (boolean mmWise)
+  {
+    DisplayMetrics metrics;
+
+    metrics = getResources ().getDisplayMetrics ();
+
+    if (!mmWise)
+      return metrics.widthPixels;
+    else
+      return (int) ((metrics.widthPixels / metrics.xdpi) * 2540.0);
+  }
+
+  public int
+  getScreenHeight (boolean mmWise)
+  {
+    DisplayMetrics metrics;
+
+    metrics = getResources ().getDisplayMetrics ();
+
+    if (!mmWise)
+      return metrics.heightPixels;
+    else
+      return (int) ((metrics.heightPixels / metrics.ydpi) * 2540.0);
+  }
+
+  public boolean
+  detectMouse ()
+  {
+    InputManager manager;
+    InputDevice device;
+    int[] ids;
+    int i;
+
+    if (Build.VERSION.SDK_INT
+       < Build.VERSION_CODES.JELLY_BEAN)
+      return false;
+
+    manager = (InputManager) getSystemService (Context.INPUT_SERVICE);
+    ids = manager.getInputDeviceIds ();
+
+    for (i = 0; i < ids.length; ++i)
+      {
+       device = manager.getInputDevice (ids[i]);
+
+       if (device == null)
+         continue;
+
+       if (device.supportsSource (InputDevice.SOURCE_MOUSE))
+         return true;
+      }
+
+    return false;
+  }
 };
diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java 
b/java/org/gnu/emacs/EmacsSurfaceView.java
index 5efb888226..f713818d4b 100644
--- a/java/org/gnu/emacs/EmacsSurfaceView.java
+++ b/java/org/gnu/emacs/EmacsSurfaceView.java
@@ -27,8 +27,12 @@ import android.os.Build;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 
+import android.util.Log;
+
 public class EmacsSurfaceView extends SurfaceView
 {
+  private static final String TAG = "EmacsSurfaceView";
+
   public Object surfaceChangeLock;
   private boolean created;
 
@@ -45,6 +49,7 @@ public class EmacsSurfaceView extends SurfaceView
        surfaceChanged (SurfaceHolder holder, int format,
                        int width, int height)
        {
+         Log.d (TAG, "surfaceChanged: " + view);
          view.swapBuffers ();
        }
 
@@ -54,9 +59,13 @@ public class EmacsSurfaceView extends SurfaceView
        {
          synchronized (surfaceChangeLock)
            {
+             Log.d (TAG, "surfaceCreated: " + view);
              created = true;
-             view.swapBuffers ();
            }
+
+         /* Drop the lock when doing this, or a deadlock can
+            result.  */
+         view.swapBuffers ();
        }
 
        @Override
@@ -65,6 +74,7 @@ public class EmacsSurfaceView extends SurfaceView
        {
          synchronized (surfaceChangeLock)
            {
+             Log.d (TAG, "surfaceDestroyed: " + view);
              created = false;
            }
        }
@@ -93,6 +103,16 @@ public class EmacsSurfaceView extends SurfaceView
     return holder.lockCanvas (damage);
   }
 
+  @Override
+  protected void
+  onLayout (boolean changed, int left, int top, int right,
+           int bottom)
+  {
+    Log.d (TAG, ("onLayout: " + left + " " + top + " " + right
+                + " " + bottom + " -- " + changed + " visibility "
+                + getVisibility ()));
+  }
+
   /* This method is only used during debugging when it seems damage
      isn't working correctly.  */
 
diff --git a/java/org/gnu/emacs/EmacsView.java 
b/java/org/gnu/emacs/EmacsView.java
index 169a1e42ee..41acabab97 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -87,12 +87,27 @@ public class EmacsView extends ViewGroup
 
     /* Create the surface view.  */
     this.surfaceView = new EmacsSurfaceView (this);
+    this.surfaceView.setZOrderMediaOverlay (true);
     addView (this.surfaceView);
+
+    /* Not sure exactly what this does but it makes things magically
+       work.  Why is something as simple as XRaiseWindow so involved
+       on Android? */
+    setChildrenDrawingOrderEnabled (true);
+
+    /* Get rid of the foreground and background tint.  */
+    setBackgroundTintList (null);
+    setForegroundTintList (null);
   }
 
   private void
   handleDirtyBitmap ()
   {
+    Bitmap oldBitmap;
+
+    /* Save the old bitmap.  */
+    oldBitmap = bitmap;
+
     /* Recreate the front and back buffer bitmaps.  */
     bitmap
       = Bitmap.createBitmap (bitmapDirty.width (),
@@ -103,12 +118,23 @@ public class EmacsView extends ViewGroup
     /* And canvases.  */
     canvas = new Canvas (bitmap);
 
-    /* If Emacs is drawing to the bitmap right now from the
-       main thread, the image contents are lost until the next
-       ConfigureNotify and complete garbage.  Sorry! */
+    /* Copy over the contents of the old bitmap.  */
+    if (oldBitmap != null)
+      canvas.drawBitmap (oldBitmap, 0f, 0f, new Paint ());
+
     bitmapDirty = null;
   }
 
+  public synchronized void
+  explicitlyDirtyBitmap (Rect rect)
+  {
+    if (bitmapDirty == null
+       && (bitmap == null
+           || rect.width () != bitmap.getWidth ()
+           || rect.height () != bitmap.getHeight ()))
+      bitmapDirty = rect;
+  }
+
   public synchronized Bitmap
   getBitmap ()
   {
@@ -168,25 +194,31 @@ public class EmacsView extends ViewGroup
     View child;
     Rect windowRect;
 
+    count = getChildCount ();
+
     if (changed || mustReportLayout)
       {
        mustReportLayout = false;
        window.viewLayout (left, top, right, bottom);
       }
 
-    if (changed)
+    if (changed
+       /* Check that a change has really happened.  */
+       && (bitmapDirty == null
+           || bitmapDirty.width () != right - left
+           || bitmapDirty.height () != bottom - top))
       bitmapDirty = new Rect (left, top, right, bottom);
 
-    count = getChildCount ();
-
     for (i = 0; i < count; ++i)
       {
        child = getChildAt (i);
 
+       Log.d (TAG, "onLayout: " + child);
+
        if (child == surfaceView)
          /* The child is the surface view, so give it the entire
             view.  */
-         child.layout (left, top, right, bottom);
+         child.layout (0, 0, right - left, bottom - top);
        else if (child.getVisibility () != GONE)
          {
            if (!(child instanceof EmacsView))
@@ -201,59 +233,68 @@ public class EmacsView extends ViewGroup
       }
   }
 
-  public synchronized void
+  public void
   damageRect (Rect damageRect)
   {
-    damageRegion.union (damageRect);
+    synchronized (damageRegion)
+      {
+       damageRegion.union (damageRect);
+      }
   }
 
   /* This method is called from both the UI thread and the Emacs
      thread.  */
 
-  public synchronized void
+  public void
   swapBuffers (boolean force)
   {
     Canvas canvas;
     Rect damageRect;
     Bitmap bitmap;
 
-    if (damageRegion.isEmpty ())
-      return;
+    /* Code must always take damageRegion, and then surfaceChangeLock,
+       never the other way around! */
 
-    bitmap = getBitmap ();
+    synchronized (damageRegion)
+      {
+       if (damageRegion.isEmpty ())
+         return;
 
-    /* Emacs must take the following lock to ensure the access to the
-       canvas occurs with the surface created.  Otherwise, Android
-       will throttle calls to lockCanvas.  */
+       bitmap = getBitmap ();
 
-    synchronized (surfaceView.surfaceChangeLock)
-      {
-       damageRect = damageRegion.getBounds ();
+       /* Emacs must take the following lock to ensure the access to the
+          canvas occurs with the surface created.  Otherwise, Android
+          will throttle calls to lockCanvas.  */
 
-       if (!surfaceView.isCreated ())
-         return;
+       synchronized (surfaceView.surfaceChangeLock)
+         {
+           damageRect = damageRegion.getBounds ();
 
-       if (bitmap == null)
-         return;
+           if (!surfaceView.isCreated ())
+             return;
 
-       /* Lock the canvas with the specified damage.  */
-       canvas = surfaceView.lockCanvas (damageRect);
+           if (bitmap == null)
+             return;
 
-       /* Return if locking the canvas failed.  */
-       if (canvas == null)
-         return;
+           /* Lock the canvas with the specified damage.  */
+           canvas = surfaceView.lockCanvas (damageRect);
 
-       /* Copy from the back buffer to the canvas.  If damageRect was
-          made empty, then draw the entire back buffer.  */
+           /* Return if locking the canvas failed.  */
+           if (canvas == null)
+             return;
 
-       if (damageRect.isEmpty ())
-         canvas.drawBitmap (bitmap, 0f, 0f, paint);
-       else
-         canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
+           /* Copy from the back buffer to the canvas.  If damageRect was
+              made empty, then draw the entire back buffer.  */
 
-       /* Unlock the canvas and clear the damage.  */
-       surfaceView.unlockCanvasAndPost (canvas);
-       damageRegion.setEmpty ();
+           if (damageRect.isEmpty ())
+             canvas.drawBitmap (bitmap, 0f, 0f, paint);
+           else
+             canvas.drawBitmap (bitmap, damageRect, damageRect, paint);
+
+           /* Unlock the canvas and clear the damage.  */
+           surfaceView.unlockCanvasAndPost (canvas);
+           damageRegion.setEmpty ();
+         }
       }
   }
 
@@ -308,6 +349,78 @@ public class EmacsView extends ViewGroup
   public boolean
   onTouchEvent (MotionEvent motion)
   {
-    return window.onSomeKindOfMotionEvent (motion);
+    return window.onTouchEvent (motion);
+  }
+
+  private void
+  moveChildToBack (View child)
+  {
+    int index;
+
+    index = indexOfChild (child);
+
+    if (index > 0)
+      {
+       detachViewFromParent (index);
+
+       /* The view at 0 is the surface view.  */
+       attachViewToParent (child, 1,
+                           child.getLayoutParams());
+      }
+  }
+
+
+  /* The following two functions must not be called if the view has no
+     parent, or is parented to an activity.  */
+
+  public void
+  raise ()
+  {
+    EmacsView parent;
+
+    parent = (EmacsView) getParent ();
+
+    Log.d (TAG, "raise: parent " + parent);
+
+    if (parent.indexOfChild (this)
+       == parent.getChildCount () - 1)
+      return;
+
+    parent.bringChildToFront (this);
+
+    /* Yes, all of this is really necessary! */
+    parent.requestLayout ();
+    parent.invalidate ();
+    requestLayout ();
+    invalidate ();
+
+    /* The surface view must be destroyed and recreated.  */
+    removeView (surfaceView);
+    addView (surfaceView, 0);
+  }
+
+  public void
+  lower ()
+  {
+    EmacsView parent;
+
+    parent = (EmacsView) getParent ();
+
+    Log.d (TAG, "lower: parent " + parent);
+
+    if (parent.indexOfChild (this) == 1)
+      return;
+
+    parent.moveChildToBack (this);
+
+    /* Yes, all of this is really necessary! */
+    parent.requestLayout ();
+    parent.invalidate ();
+    requestLayout ();
+    invalidate ();
+
+    /* The surface view must be removed and attached again.  */
+    removeView (surfaceView);
+    addView (surfaceView, 0);
   }
 };
diff --git a/java/org/gnu/emacs/EmacsWindow.java 
b/java/org/gnu/emacs/EmacsWindow.java
index a9a24b6186..1f8596dba5 100644
--- a/java/org/gnu/emacs/EmacsWindow.java
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -22,6 +22,7 @@ package org.gnu.emacs;
 import java.lang.IllegalStateException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.HashMap;
 
 import android.graphics.Rect;
 import android.graphics.Canvas;
@@ -50,9 +51,29 @@ import android.os.Build;
    Views are also drawables, meaning they can accept drawing
    requests.  */
 
+/* Help wanted.  What does not work includes `EmacsView.raise',
+   `EmacsView.lower', reparenting a window onto another window.
+
+   All three are likely undocumented restrictions within
+   EmacsSurface.  */
+
 public class EmacsWindow extends EmacsHandleObject
   implements EmacsDrawable
 {
+  private static final String TAG = "EmacsWindow";
+
+  private class Coordinate
+  {
+    /* Integral coordinate.  */
+    int x, y;
+
+    Coordinate (int x, int y)
+    {
+      this.x = x;
+      this.y = y;
+    }
+  };
+
   /* The view associated with the window.  */
   public EmacsView view;
 
@@ -60,12 +81,16 @@ public class EmacsWindow extends EmacsHandleObject
   private Rect rect;
 
   /* The parent window, or null if it is the root window.  */
-  private EmacsWindow parent;
+  public EmacsWindow parent;
 
   /* List of all children in stacking order.  This must be kept
      consistent!  */
   public ArrayList<EmacsWindow> children;
 
+  /* Map between pointer identifiers and last known position.  Used to
+     compute which pointer changed upon a touch event.  */
+  private HashMap<Integer, Coordinate> pointerMap;
+
   /* The window consumer currently attached, if it exists.  */
   private EmacsWindowAttachmentManager.WindowConsumer attached;
 
@@ -77,6 +102,14 @@ public class EmacsWindow extends EmacsHandleObject
      last button press or release event.  */
   private int lastButtonState, lastModifiers;
 
+  /* Whether or not the window is mapped, and whether or not it is
+     deiconified.  */
+  private boolean isMapped, isIconified;
+
+  /* Whether or not to ask for focus upon being mapped, and whether or
+     not the window should be focusable.  */
+  private boolean dontFocusOnMap, dontAcceptFocus;
+
   public
   EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
               int width, int height)
@@ -84,6 +117,7 @@ public class EmacsWindow extends EmacsHandleObject
     super (handle);
 
     rect = new Rect (x, y, x + width, y + height);
+    pointerMap = new HashMap<Integer, Coordinate> ();
 
     /* Create the view from the context's UI thread.  The window is
        unmapped, so the view is GONE.  */
@@ -97,7 +131,7 @@ public class EmacsWindow extends EmacsHandleObject
     if (parent != null)
       {
        parent.children.add (this);
-       parent.view.post (new Runnable () {
+        EmacsService.SERVICE.runOnUiThread (new Runnable () {
            @Override
            public void
            run ()
@@ -106,23 +140,6 @@ public class EmacsWindow extends EmacsHandleObject
            }
          });
       }
-    else
-      EmacsService.SERVICE.runOnUiThread (new Runnable () {
-         @Override
-         public void
-         run ()
-         {
-           EmacsWindowAttachmentManager manager;
-
-           manager = EmacsWindowAttachmentManager.MANAGER;
-
-           /* If parent is the root window, notice that there are new
-              children available for interested activites to pick
-              up.  */
-
-           manager.registerWindow (EmacsWindow.this);
-         }
-       });
 
     scratchGC = new EmacsGC ((short) 0);
   }
@@ -159,7 +176,7 @@ public class EmacsWindow extends EmacsHandleObject
                                       + "children!");
 
     /* Remove the view from its parent and make it invisible.  */
-    view.post (new Runnable () {
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
        public void
        run ()
        {
@@ -174,7 +191,7 @@ public class EmacsWindow extends EmacsHandleObject
 
          parent = (View) view.getParent ();
 
-         if (parent != null && attached == null)
+         if (parent != null)
            ((ViewGroup) parent).removeView (view);
 
          manager.detachWindow (EmacsWindow.this);
@@ -199,24 +216,33 @@ public class EmacsWindow extends EmacsHandleObject
   public void
   viewLayout (int left, int top, int right, int bottom)
   {
+    int rectWidth, rectHeight;
+
     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 ());
       }
+
+    rectWidth = right - left;
+    rectHeight = bottom - top;
+
+    EmacsNative.sendConfigureNotify (this.handle,
+                                    System.currentTimeMillis (),
+                                    left, top, rectWidth,
+                                    rectHeight);
   }
 
   public void
   requestViewLayout ()
   {
-    view.post (new Runnable () {
+    /* This is necessary because otherwise subsequent drawing on the
+       Emacs thread may be lost.  */
+    view.explicitlyDirtyBitmap (rect);
+
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
        @Override
        public void
        run ()
@@ -261,28 +287,77 @@ public class EmacsWindow extends EmacsHandleObject
   public void
   mapWindow ()
   {
-    view.post (new Runnable () {
-       @Override
-       public void
-       run ()
-       {
+    if (isMapped)
+      return;
 
-         view.setVisibility (View.VISIBLE);
-         /* Eventually this should check no-focus-on-map.  */
-         view.requestFocus ();
-       }
-      });
+    isMapped = true;
+
+    if (parent == null)
+      {
+        EmacsService.SERVICE.runOnUiThread (new Runnable () {
+           @Override
+           public void
+           run ()
+           {
+             EmacsWindowAttachmentManager manager;
+
+             /* Make the view visible, first of all.  */
+             view.setVisibility (View.VISIBLE);
+
+             manager = EmacsWindowAttachmentManager.MANAGER;
+
+             /* If parent is the root window, notice that there are new
+                children available for interested activites to pick
+                up.  */
+             manager.registerWindow (EmacsWindow.this);
+
+             if (!getDontFocusOnMap ())
+               /* Eventually this should check no-focus-on-map.  */
+               view.requestFocus ();
+           }
+         });
+      }
+    else
+      {
+       /* Do the same thing as above, but don't register this
+          window.  */
+        EmacsService.SERVICE.runOnUiThread (new Runnable () {
+           @Override
+           public void
+           run ()
+           {
+             view.setVisibility (View.VISIBLE);
+
+             if (!getDontFocusOnMap ())
+             /* Eventually this should check no-focus-on-map.  */
+               view.requestFocus ();
+           }
+         });
+      }
   }
 
   public void
   unmapWindow ()
   {
+    if (!isMapped)
+      return;
+
+    isMapped = false;
+
     view.post (new Runnable () {
        @Override
        public void
        run ()
        {
+         EmacsWindowAttachmentManager manager;
+
+         manager = EmacsWindowAttachmentManager.MANAGER;
+
          view.setVisibility (View.GONE);
+
+         /* Now that the window is unmapped, unregister it as
+            well.  */
+         manager.detachWindow (EmacsWindow.this);
        }
       });
   }
@@ -413,6 +488,161 @@ public class EmacsWindow extends EmacsHandleObject
     return 4;
   }
 
+  /* Return the ID of the pointer which changed in EVENT.  Value is -1
+     if it could not be determined, else the pointer that changed, or
+     -2 if -1 would have been returned, but there is also a pointer
+     that is a mouse.  */
+
+  private int
+  figureChange (MotionEvent event)
+  {
+    int pointerID, i, truncatedX, truncatedY, pointerIndex;
+    Coordinate coordinate;
+    boolean mouseFlag;
+
+    /* pointerID is always initialized but the Java compiler is too
+       dumb to know that.  */
+    pointerID = -1;
+    mouseFlag = false;
+
+    switch (event.getActionMasked ())
+      {
+      case MotionEvent.ACTION_DOWN:
+       /* Primary pointer pressed with index 0.  */
+
+       /* Detect mice.  If this is a mouse event, give it to
+          onSomeKindOfMotionEvent.  */
+       if ((Build.VERSION.SDK_INT
+            >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+           && event.getToolType (0) == MotionEvent.TOOL_TYPE_MOUSE)
+         return -2;
+
+       pointerID = event.getPointerId (0);
+       pointerMap.put (pointerID,
+                       new Coordinate ((int) event.getX (0),
+                                       (int) event.getY (0)));
+       break;
+
+      case MotionEvent.ACTION_UP:
+       /* Primary pointer released with index 0.  */
+       pointerID = event.getPointerId (0);
+       pointerMap.remove (pointerID);
+       break;
+
+      case MotionEvent.ACTION_POINTER_DOWN:
+       /* New pointer.  Find the pointer ID from the index and place
+          it in the map.  */
+       pointerIndex = event.getActionIndex ();
+       pointerID = event.getPointerId (pointerIndex);
+       pointerMap.put (pointerID,
+                       new Coordinate ((int) event.getX (pointerID),
+                                       (int) event.getY (pointerID)));
+       break;
+
+      case MotionEvent.ACTION_POINTER_UP:
+       /* Pointer removed.  Remove it from the map.  */
+       pointerIndex = event.getActionIndex ();
+       pointerID = event.getPointerId (pointerIndex);
+       pointerMap.remove (pointerID);
+       break;
+
+      default:
+
+       /* Loop through each pointer in the event.  */
+       for (i = 0; i < event.getPointerCount (); ++i)
+         {
+           pointerID = event.getPointerId (i);
+
+           /* Look up that pointer in the map.  */
+           coordinate = pointerMap.get (pointerID);
+
+           if (coordinate != null)
+             {
+               /* See if coordinates have changed.  */
+               truncatedX = (int) event.getX (i);
+               truncatedY = (int) event.getY (i);
+
+               if (truncatedX != coordinate.x
+                   || truncatedY != coordinate.y)
+                 {
+                   /* The pointer changed.  Update the coordinate and
+                      break out of the loop.  */
+                   coordinate.x = truncatedX;
+                   coordinate.y = truncatedY;
+
+                   break;
+                 }
+             }
+
+           /* See if this is a mouse.  If so, set the mouseFlag.  */
+           if ((Build.VERSION.SDK_INT
+                >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+               && event.getToolType (i) == MotionEvent.TOOL_TYPE_MOUSE)
+             mouseFlag = true;
+         }
+
+       /* Set the pointer ID to -1 if the loop failed to find any
+          changed pointer.  If a mouse pointer was found, set it to
+          -2.  */
+       if (i == event.getPointerCount ())
+         pointerID = (mouseFlag ? -2 : -1);
+      }
+
+    /* Return the pointer ID.  */
+    return pointerID;
+  }
+
+  public boolean
+  onTouchEvent (MotionEvent event)
+  {
+    int pointerID, index;
+
+    /* Extract the ``touch ID'' (or in Android, the ``pointer
+       ID''.) */
+    pointerID = figureChange (event);
+
+    if (pointerID < 0)
+      {
+       /* If this is a mouse event, give it to
+          onSomeKindOfMotionEvent.  */
+       if (pointerID == -2)
+         return onSomeKindOfMotionEvent (event);
+
+       return false;
+      }
+
+    /* Find the pointer index corresponding to the event.  */
+    index = event.findPointerIndex (pointerID);
+
+    switch (event.getActionMasked ())
+      {
+      case MotionEvent.ACTION_DOWN:
+      case MotionEvent.ACTION_POINTER_DOWN:
+       /* Touch down event.  */
+       EmacsNative.sendTouchDown (this.handle, (int) event.getX (index),
+                                  (int) event.getY (index),
+                                  event.getEventTime (), pointerID);
+       return true;
+
+      case MotionEvent.ACTION_UP:
+      case MotionEvent.ACTION_POINTER_UP:
+       /* Touch up event.  */
+       EmacsNative.sendTouchUp (this.handle, (int) event.getX (index),
+                                (int) event.getY (index),
+                                event.getEventTime (), pointerID);
+       return true;
+
+      case MotionEvent.ACTION_MOVE:
+       /* Pointer motion event.  */
+       EmacsNative.sendTouchMove (this.handle, (int) event.getX (index),
+                                  (int) event.getY (index),
+                                  event.getEventTime (), pointerID);
+       return true;
+      }
+
+    return false;
+  }
+
   public boolean
   onSomeKindOfMotionEvent (MotionEvent event)
   {
@@ -472,13 +702,201 @@ public class EmacsWindow extends EmacsHandleObject
 
       case MotionEvent.ACTION_DOWN:
       case MotionEvent.ACTION_UP:
-       /* Emacs must return true even though touch events are not yet
-          handled, because the value of this function is used by the
-          system to decide whether or not Emacs gets ACTION_MOVE
+       /* Emacs must return true even though touch events are not
+          handled here, because the value of this function is used by
+          the system to decide whether or not Emacs gets ACTION_MOVE
           events.  */
        return true;
+
+      case MotionEvent.ACTION_SCROLL:
+       /* Send a scroll event with the specified deltas.  */
+       EmacsNative.sendWheel (this.handle, (int) event.getX (),
+                              (int) event.getY (),
+                              event.getEventTime (),
+                              lastModifiers,
+                              event.getAxisValue (MotionEvent.AXIS_HSCROLL),
+                              event.getAxisValue (MotionEvent.AXIS_VSCROLL));
+       return true;
       }
 
     return false;
   }
+
+  public void
+  reparentTo (final EmacsWindow otherWindow, int x, int y)
+  {
+    int width, height;
+
+    /* Reparent this window to the other window.  */
+
+    if (parent != null)
+      parent.children.remove (this);
+
+    if (otherWindow != null)
+      otherWindow.children.add (this);
+
+    parent = otherWindow;
+
+    /* Move this window to the new location.  */
+    synchronized (this)
+      {
+       width = rect.width ();
+       height = rect.height ();
+       rect.left = x;
+       rect.top = y;
+       rect.right = x + width;
+       rect.bottom = y + height;
+      }
+
+    /* Now do the work necessary on the UI thread to reparent the
+       window.  */
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         EmacsWindowAttachmentManager manager;
+         View parent;
+
+         /* First, detach this window if necessary.  */
+         manager = EmacsWindowAttachmentManager.MANAGER;
+         manager.detachWindow (EmacsWindow.this);
+
+         /* Also unparent this view.  */
+         parent = (View) view.getParent ();
+
+         if (parent != null)
+           ((ViewGroup) parent).removeView (view);
+
+         /* Next, either add this window as a child of the new
+            parent's view, or make it available again.  */
+         if (otherWindow != null)
+           otherWindow.view.addView (view);
+         else if (EmacsWindow.this.isMapped)
+           manager.registerWindow (EmacsWindow.this);
+
+         /* Request relayout.  */
+         view.requestLayout ();
+       }
+      });
+  }
+
+  public void
+  makeInputFocus (long time)
+  {
+    /* TIME is currently ignored.  Request the input focus now.  */
+
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         view.requestFocus ();
+       }
+      });
+  }
+
+  public void
+  raise ()
+  {
+    /* This does nothing here.  */
+    if (parent == null)
+      return;
+
+    /* Remove and add this view again.  */
+    parent.children.remove (this);
+    parent.children.add (this);
+
+    /* Request a relayout.  */
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         view.raise ();
+       }
+      });
+  }
+
+  public void
+  lower ()
+  {
+    /* This does nothing here.  */
+    if (parent == null)
+      return;
+
+    /* Remove and add this view again.  */
+    parent.children.remove (this);
+    parent.children.add (this);
+
+    /* Request a relayout.  */
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         view.lower ();
+       }
+      });
+  }
+
+  public int[]
+  getWindowGeometry ()
+  {
+    int[] array;
+    Rect rect;
+
+    array = new int[4];
+    rect = getGeometry ();
+
+    array[0] = rect.left;
+    array[1] = rect.top;
+    array[2] = rect.width ();
+    array[3] = rect.height ();
+
+    return array;
+  }
+
+  public void
+  noticeIconified ()
+  {
+    isIconified = true;
+    EmacsNative.sendIconified (this.handle);
+  }
+
+  public void
+  noticeDeiconified ()
+  {
+    isIconified = false;
+    EmacsNative.sendDeiconified (this.handle);
+  }
+
+  public synchronized void
+  setDontAcceptFocus (final boolean dontAcceptFocus)
+  {
+    this.dontAcceptFocus = dontAcceptFocus;
+
+    /* Update the view's focus state.  */
+    EmacsService.SERVICE.runOnUiThread (new Runnable () {
+       @Override
+       public void
+       run ()
+       {
+         view.setFocusable (!dontAcceptFocus);
+         view.setFocusableInTouchMode (!dontAcceptFocus);
+       }
+      });
+  }
+
+  public synchronized void
+  setDontFocusOnMap (final boolean dontFocusOnMap)
+  {
+    this.dontFocusOnMap = dontFocusOnMap;
+  }
+
+  public synchronized boolean
+  getDontFocusOnMap ()
+  {
+    return dontFocusOnMap;
+  }
 };
diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java 
b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
index 34be2ab878..15eb3bb65c 100644
--- a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
+++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
@@ -19,6 +19,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 package org.gnu.emacs;
 
+import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -68,7 +69,7 @@ public class EmacsWindowAttachmentManager
   };
 
   private List<WindowConsumer> consumers;
-  private List<EmacsWindow> windows;
+  public List<EmacsWindow> windows;
 
   public
   EmacsWindowAttachmentManager ()
@@ -98,12 +99,19 @@ public class EmacsWindowAttachmentManager
     EmacsNative.sendWindowAction ((short) 0, 0);
   }
 
-  public void
+  public synchronized void
   registerWindow (EmacsWindow window)
   {
     Intent intent;
 
-    Log.d (TAG, "registerWindow " + window);
+    Log.d (TAG, "registerWindow (maybe): " + window);
+
+    if (windows.contains (window))
+      /* The window is already registered.  */
+      return;
+
+    Log.d (TAG, "registerWindow: " + window);
+
     windows.add (window);
 
     for (WindowConsumer consumer : consumers)
@@ -146,7 +154,7 @@ public class EmacsWindowAttachmentManager
     consumers.remove (consumer);
   }
 
-  public void
+  public synchronized void
   detachWindow (EmacsWindow window)
   {
     WindowConsumer consumer;
@@ -162,5 +170,43 @@ public class EmacsWindowAttachmentManager
        consumers.remove (consumer);
        consumer.destroy ();
       }
+
+    windows.remove (window);
+  }
+
+  public void
+  noticeIconified (WindowConsumer consumer)
+  {
+    EmacsWindow window;
+
+    Log.d (TAG, "noticeIconified " + consumer);
+
+    /* If a window is attached, send the appropriate iconification
+       events.  */
+    window = consumer.getAttachedWindow ();
+
+    if (window != null)
+      window.noticeIconified ();
+  }
+
+  public void
+  noticeDeiconified (WindowConsumer consumer)
+  {
+    EmacsWindow window;
+
+    Log.d (TAG, "noticeDeiconified " + consumer);
+
+    /* If a window is attached, send the appropriate iconification
+       events.  */
+    window = consumer.getAttachedWindow ();
+
+    if (window != null)
+      window.noticeDeiconified ();
+  }
+
+  public synchronized List<EmacsWindow>
+  copyWindows ()
+  {
+    return new ArrayList<EmacsWindow> (windows);
   }
 };
diff --git a/lisp/frame.el b/lisp/frame.el
index 436323f7d5..d35df71a6c 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -1647,6 +1647,7 @@ live frame and defaults to the selected one."
 (declare-function ns-frame-geometry "nsfns.m" (&optional frame))
 (declare-function pgtk-frame-geometry "pgtkfns.c" (&optional frame))
 (declare-function haiku-frame-geometry "haikufns.c" (&optional frame))
+(declare-function android-frame-geometry "androidfns.c" (&optional frame))
 
 (defun frame-geometry (&optional frame)
   "Return geometric attributes of FRAME.
@@ -1700,6 +1701,8 @@ and width values are in pixels.
       (pgtk-frame-geometry frame))
      ((eq frame-type 'haiku)
       (haiku-frame-geometry frame))
+     ((eq frame-type 'android)
+      (android-frame-geometry frame))
      (t
       (list
        '(outer-position 0 . 0)
@@ -1826,6 +1829,7 @@ of frames like calls to map a frame or change its 
visibility."
 (declare-function ns-frame-edges "nsfns.m" (&optional frame type))
 (declare-function pgtk-frame-edges "pgtkfns.c" (&optional frame type))
 (declare-function haiku-frame-edges "haikufns.c" (&optional frame type))
+(declare-function android-frame-edges "androidfns.c" (&optional frame type))
 
 (defun frame-edges (&optional frame type)
   "Return coordinates of FRAME's edges.
@@ -1853,6 +1857,8 @@ FRAME."
       (pgtk-frame-edges frame type))
      ((eq frame-type 'haiku)
       (haiku-frame-edges frame type))
+     ((eq frame-type 'android)
+      (android-frame-edges frame type))
      (t
       (list 0 0 (frame-width frame) (frame-height frame))))))
 
@@ -1861,6 +1867,7 @@ FRAME."
 (declare-function ns-mouse-absolute-pixel-position "nsfns.m")
 (declare-function pgtk-mouse-absolute-pixel-position "pgtkfns.c")
 (declare-function haiku-mouse-absolute-pixel-position "haikufns.c")
+(declare-function android-mouse-absolute-pixel-position "androidfns.c")
 
 (defun mouse-absolute-pixel-position ()
   "Return absolute position of mouse cursor in pixels.
@@ -1879,6 +1886,8 @@ position (0, 0) of the selected frame's terminal."
       (pgtk-mouse-absolute-pixel-position))
      ((eq frame-type 'haiku)
       (haiku-mouse-absolute-pixel-position))
+     ((eq frame-type 'android)
+      (android-mouse-absolute-pixel-position))
      (t
       (cons 0 0)))))
 
@@ -1887,6 +1896,8 @@ position (0, 0) of the selected frame's terminal."
 (declare-function w32-set-mouse-absolute-pixel-position "w32fns.c" (x y))
 (declare-function x-set-mouse-absolute-pixel-position "xfns.c" (x y))
 (declare-function haiku-set-mouse-absolute-pixel-position "haikufns.c" (x y))
+(declare-function android-set-mouse-absolute-pixel-position
+                  "androidfns.c" (x y))
 
 (defun set-mouse-absolute-pixel-position (x y)
   "Move mouse pointer to absolute pixel position (X, Y).
@@ -1903,7 +1914,9 @@ position (0, 0) of the selected frame's terminal."
      ((eq frame-type 'w32)
       (w32-set-mouse-absolute-pixel-position x y))
      ((eq frame-type 'haiku)
-      (haiku-set-mouse-absolute-pixel-position x y)))))
+      (haiku-set-mouse-absolute-pixel-position x y))
+     ((eq frame-type 'android)
+      (android-set-mouse-absolute-pixel-position x y)))))
 
 (defun frame-monitor-attributes (&optional frame)
   "Return the attributes of the physical monitor dominating FRAME.
@@ -1999,6 +2012,7 @@ workarea attribute."
 ;; TODO: implement this on PGTK.
 ;; (declare-function pgtk-frame-list-z-order "pgtkfns.c" (&optional display))
 (declare-function haiku-frame-list-z-order "haikufns.c" (&optional display))
+(declare-function android-frame-list-z-order "androidfns.c" (&optional 
display))
 
 (defun frame-list-z-order (&optional display)
   "Return list of Emacs' frames, in Z (stacking) order.
@@ -2024,13 +2038,17 @@ Return nil if DISPLAY contains no Emacs frame."
       ;; (pgtk-frame-list-z-order display)
       nil)
      ((eq frame-type 'haiku)
-      (haiku-frame-list-z-order display)))))
+      (haiku-frame-list-z-order display))
+     ((eq frame-type 'android)
+      (android-frame-list-z-order display)))))
 
 (declare-function x-frame-restack "xfns.c" (frame1 frame2 &optional above))
 (declare-function w32-frame-restack "w32fns.c" (frame1 frame2 &optional above))
 (declare-function ns-frame-restack "nsfns.m" (frame1 frame2 &optional above))
 (declare-function pgtk-frame-restack "pgtkfns.c" (frame1 frame2 &optional 
above))
 (declare-function haiku-frame-restack "haikufns.c" (frame1 frame2 &optional 
above))
+(declare-function android-frame-restack "androidfns.c" (frame1 frame2
+                                                               &optional 
above))
 
 (defun frame-restack (frame1 frame2 &optional above)
   "Restack FRAME1 below FRAME2.
@@ -2064,7 +2082,9 @@ Some window managers may refuse to restack windows."
          ((eq frame-type 'haiku)
           (haiku-frame-restack frame1 frame2 above))
          ((eq frame-type 'pgtk)
-          (pgtk-frame-restack frame1 frame2 above))))
+          (pgtk-frame-restack frame1 frame2 above))
+         ((eq frame-type 'android)
+          (android-frame-restack frame1 frame2 above))))
     (error "Cannot restack frames")))
 
 (defun frame-size-changed-p (&optional frame)
@@ -2113,6 +2133,8 @@ frame's display)."
        (> w32-num-mouse-buttons 0)))
      ((memq frame-type '(x ns haiku pgtk))
       t)    ;; We assume X, NeXTstep, GTK, and Haiku *always* have a pointing 
device
+     ((eq frame-type 'android)
+      (android-detect-mouse))
      (t
       (or (and (featurep 'xt-mouse)
               xterm-mouse-mode)
@@ -2396,6 +2418,8 @@ If DISPLAY is omitted or nil, it defaults to the selected 
frame's display."
                  (&optional terminal))
 (declare-function haiku-display-monitor-attributes-list "haikufns.c"
                  (&optional terminal))
+(declare-function android-display-monitor-attributes-list "androidfns.c"
+                  (&optional terminal))
 
 (defun display-monitor-attributes-list (&optional display)
   "Return a list of physical monitor attributes on DISPLAY.
@@ -2449,6 +2473,8 @@ monitors."
       (pgtk-display-monitor-attributes-list display))
      ((eq frame-type 'haiku)
       (haiku-display-monitor-attributes-list display))
+     ((eq frame-type 'android)
+      (android-display-monitor-attributes-list display))
      (t
       (let ((geometry (list 0 0 (display-pixel-width display)
                            (display-pixel-height display))))
diff --git a/lisp/mwheel.el b/lisp/mwheel.el
index 1be52d24e3..67d93c3558 100644
--- a/lisp/mwheel.el
+++ b/lisp/mwheel.el
@@ -58,7 +58,8 @@
 
 (defcustom mouse-wheel-down-event
   (if (or (featurep 'w32-win) (featurep 'ns-win)
-          (featurep 'haiku-win) (featurep 'pgtk-win))
+          (featurep 'haiku-win) (featurep 'pgtk-win)
+          (featurep 'android-win))
       'wheel-up
     'mouse-4)
   "Event used for scrolling down."
@@ -79,7 +80,8 @@
 
 (defcustom mouse-wheel-up-event
   (if (or (featurep 'w32-win) (featurep 'ns-win)
-          (featurep 'haiku-win) (featurep 'pgtk-win))
+          (featurep 'haiku-win) (featurep 'pgtk-win)
+          (featurep 'android-win))
       'wheel-down
     'mouse-5)
   "Event used for scrolling up."
@@ -254,7 +256,8 @@ Also see `mouse-wheel-tilt-scroll'."
 
 (defvar mouse-wheel-left-event
   (if (or (featurep 'w32-win) (featurep 'ns-win)
-          (featurep 'haiku-win) (featurep 'pgtk-win))
+          (featurep 'haiku-win) (featurep 'pgtk-win)
+          (featurep 'android-win))
       'wheel-left
     'mouse-6)
   "Event used for scrolling left.")
@@ -268,7 +271,8 @@ Also see `mouse-wheel-tilt-scroll'."
 
 (defvar mouse-wheel-right-event
   (if (or (featurep 'w32-win) (featurep 'ns-win)
-          (featurep 'haiku-win) (featurep 'pgtk-win))
+          (featurep 'haiku-win) (featurep 'pgtk-win)
+          (featurep 'android-win))
       'wheel-right
     'mouse-7)
   "Event used for scrolling right.")
diff --git a/src/android.c b/src/android.c
index 2f85266200..eab18dc615 100644
--- a/src/android.c
+++ b/src/android.c
@@ -82,6 +82,11 @@ struct android_emacs_service
   jmethodID copy_area;
   jmethodID clear_window;
   jmethodID clear_area;
+  jmethodID ring_bell;
+  jmethodID query_tree;
+  jmethodID get_screen_width;
+  jmethodID get_screen_height;
+  jmethodID detect_mouse;
 };
 
 struct android_emacs_pixmap
@@ -196,9 +201,6 @@ struct android_event_queue
   /* 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;
 
@@ -314,11 +316,6 @@ android_init_events (void)
                         "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",
@@ -387,8 +384,7 @@ android_next_event (union android_event *event_return)
   /* Free the container.  */
   free (container);
 
-  /* Signal that events can now be written.  */
-  pthread_cond_signal (&event_queue.write_var);
+  /* Unlock the queue.  */
   pthread_mutex_unlock (&event_queue.mutex);
 }
 
@@ -407,12 +403,6 @@ android_write_event (union android_event *event)
     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;
@@ -421,6 +411,10 @@ android_write_event (union android_event *event)
   event_queue.num_events++;
   pthread_cond_signal (&event_queue.read_var);
   pthread_mutex_unlock (&event_queue.mutex);
+
+  /* Now set pending_signals to true.  This allows C-g to be handled
+     immediately even without SIGIO.  */
+  pending_signals = true;
 }
 
 int
@@ -604,31 +598,60 @@ android_file_access_p (const char *name, int amode)
 {
   AAsset *asset;
   AAssetDir *directory;
+  int length;
 
   if (!asset_manager)
     return false;
 
   if (!(amode & W_OK) && (name = android_get_asset_name (name)))
     {
+      if (!strcmp (name, "") || !strcmp (name, "/"))
+       /* /assets always exists.  */
+       return true;
+
       /* 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.  */
+         /* See if it's a directory as well.  To open a directory
+            with the asset manager, the trailing slash (if specified)
+            must be removed.  */
          directory = AAssetManager_openDir (asset_manager, name);
 
          if (directory)
            {
+             /* Make sure the directory actually has files in it.  */
+
+             if (!AAssetDir_getNextFileName (directory))
+               {
+                 AAssetDir_close (directory);
+                 errno = ENOENT;
+                 return false;
+               }
+
              AAssetDir_close (directory);
              return true;
            }
 
+         errno = ENOENT;
          return false;
        }
 
       AAsset_close (asset);
+
+      /* If NAME is a directory name, but it was a regular file, set
+        errno to ENOTDIR and return false.  This is to behave like
+        faccessat.  */
+
+      length = strlen (name);
+      if (name[length - 1] == '/')
+       {
+         errno = ENOTDIR;
+         return false;
+       }
+
       return true;
     }
 
@@ -819,12 +842,31 @@ int
 android_close (int fd)
 {
   if (fd < ANDROID_MAX_ASSET_FD
-      && (android_table[fd].flags & ANDROID_FD_TABLE_ENTRY_IS_VALID))
+      && (android_table[fd].flags
+         & ANDROID_FD_TABLE_ENTRY_IS_VALID))
     android_table[fd].flags = 0;
 
   return close (fd);
 }
 
+/* Like fclose.  However, remove any information associated with
+   FILE's file descriptor from the asset table as well.  */
+
+int
+android_fclose (FILE *stream)
+{
+  int fd;
+
+  fd = fileno (stream);
+
+  if (fd != -1 && fd < ANDROID_MAX_ASSET_FD
+      && (android_table[fd].flags
+         & ANDROID_FD_TABLE_ENTRY_IS_VALID))
+    android_table[fd].flags = 0;
+
+  return fclose (stream);
+}
+
 /* Return the current user's ``home'' directory, which is actually the
    app data directory on Android.  */
 
@@ -1010,7 +1052,12 @@ android_init_emacs_service (void)
               "(Lorg/gnu/emacs/EmacsWindow;)V");
   FIND_METHOD (clear_area, "clearArea",
               "(Lorg/gnu/emacs/EmacsWindow;IIII)V");
-
+  FIND_METHOD (ring_bell, "ringBell", "()V");
+  FIND_METHOD (query_tree, "queryTree",
+              "(Lorg/gnu/emacs/EmacsWindow;)[S");
+  FIND_METHOD (get_screen_width, "getScreenWidth", "(Z)I");
+  FIND_METHOD (get_screen_height, "getScreenHeight", "(Z)I");
+  FIND_METHOD (detect_mouse, "detectMouse", "()Z");
 #undef FIND_METHOD
 }
 
@@ -1342,6 +1389,97 @@ NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject 
object,
   android_write_event (&event);
 }
 
+extern JNIEXPORT void JNICALL
+NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object,
+                            jshort window, jint x, jint y,
+                            jlong time, jint pointer_id)
+{
+  union android_event event;
+
+  event.touch.type = ANDROID_TOUCH_DOWN;
+  event.touch.window = window;
+  event.touch.x = x;
+  event.touch.y = y;
+  event.touch.time = time;
+  event.touch.pointer_id = pointer_id;
+
+  android_write_event (&event);
+}
+
+extern JNIEXPORT void JNICALL
+NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object,
+                          jshort window, jint x, jint y,
+                          jlong time, jint pointer_id)
+{
+  union android_event event;
+
+  event.touch.type = ANDROID_TOUCH_UP;
+  event.touch.window = window;
+  event.touch.x = x;
+  event.touch.y = y;
+  event.touch.time = time;
+  event.touch.pointer_id = pointer_id;
+
+  android_write_event (&event);
+}
+
+extern JNIEXPORT void JNICALL
+NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object,
+                            jshort window, jint x, jint y,
+                            jlong time, jint pointer_id)
+{
+  union android_event event;
+
+  event.touch.type = ANDROID_TOUCH_MOVE;
+  event.touch.window = window;
+  event.touch.x = x;
+  event.touch.y = y;
+  event.touch.time = time;
+  event.touch.pointer_id = pointer_id;
+
+  android_write_event (&event);
+}
+
+extern JNIEXPORT void JNICALL
+NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object,
+                        jshort window, jint x, jint y,
+                        jlong time, jint state,
+                        jfloat x_delta, jfloat y_delta)
+{
+  union android_event event;
+
+  event.wheel.type = ANDROID_WHEEL;
+  event.wheel.window = window;
+  event.wheel.x = x;
+  event.wheel.y = y;
+  event.wheel.time = time;
+  event.wheel.state = state;
+  event.wheel.x_delta = x_delta;
+  event.wheel.y_delta = y_delta;
+
+  android_write_event (&event);
+}
+
+extern JNIEXPORT void JNICALL
+NATIVE_NAME (sendIconified) (JNIEnv *env, jobject object,
+                            jshort window)
+{
+  union android_event event;
+
+  event.iconified.type = ANDROID_ICONIFIED;
+  event.iconified.window = window;
+}
+
+extern JNIEXPORT void JNICALL
+NATIVE_NAME (sendDeiconified) (JNIEnv *env, jobject object,
+                              jshort window)
+{
+  union android_event event;
+
+  event.iconified.type = ANDROID_DEICONIFIED;
+  event.iconified.window = window;
+}
+
 #pragma clang diagnostic pop
 
 
@@ -1979,10 +2117,22 @@ android_set_clip_rectangles (struct android_gc *gc, int 
clip_x_origin,
 }
 
 void
-android_reparent_window (android_window w, android_window parent,
+android_reparent_window (android_window w, android_window parent_handle,
                         int x, int y)
 {
-  /* TODO */
+  jobject window, parent;
+  jmethodID method;
+
+  window = android_resolve_handle (w, ANDROID_HANDLE_WINDOW);
+  parent = android_resolve_handle (parent_handle,
+                                  ANDROID_HANDLE_WINDOW);
+
+  method = android_lookup_method ("org/gnu/emacs/EmacsWindow",
+                                 "reparentTo",
+                                 "(Lorg/gnu/emacs/EmacsWindow;II)V");
+  (*android_java_env)->CallVoidMethod (android_java_env, window,
+                                      method,
+                                      parent, (jint) x, (jint) y);
 }
 
 /* Look up the method with SIGNATURE by NAME in CLASS.  Abort if it
@@ -2904,6 +3054,163 @@ android_put_image (android_pixmap handle, struct 
android_image *image)
   ANDROID_DELETE_LOCAL_REF (bitmap);
 }
 
+void
+android_bell (void)
+{
+  (*android_java_env)->CallVoidMethod (android_java_env,
+                                      emacs_service,
+                                      service_class.ring_bell);
+}
+
+void
+android_set_input_focus (android_window handle, unsigned long time)
+{
+  jobject window;
+  jmethodID make_input_focus;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  make_input_focus = android_lookup_method ("org/gnu/emacs/EmacsWindow",
+                                           "makeInputFocus", "(J)V");
+
+  (*android_java_env)->CallVoidMethod (android_java_env, window,
+                                      make_input_focus, (jlong) time);
+}
+
+void
+android_raise_window (android_window handle)
+{
+  jobject window;
+  jmethodID raise;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  raise = android_lookup_method ("org/gnu/emacs/EmacsWindow",
+                                "raise", "()V");
+
+  (*android_java_env)->CallVoidMethod (android_java_env, window,
+                                      raise);
+}
+
+void
+android_lower_window (android_window handle)
+{
+  jobject window;
+  jmethodID lower;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  lower = android_lookup_method ("org/gnu/emacs/EmacsWindow",
+                                "lower", "()V");
+
+  (*android_java_env)->CallVoidMethod (android_java_env, window,
+                                      lower);
+}
+
+int
+android_query_tree (android_window handle, android_window *root_return,
+                   android_window *parent_return,
+                   android_window **children_return,
+                   unsigned int *nchildren_return)
+{
+  jobject window, array;
+  jsize nelements, i;
+  android_window *children;
+  jshort *shorts;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+  /* window can be NULL, so this is a service method.  */
+  array
+    = (*android_java_env)->CallObjectMethod (android_java_env,
+                                            emacs_service,
+                                            service_class.query_tree,
+                                            window);
+  if (!array)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      memory_full (0);
+    }
+
+  /* The first element of the array is the parent window.  The rest
+     are the children.  */
+  nelements = (*android_java_env)->GetArrayLength (android_java_env,
+                                                  array);
+  eassert (nelements);
+
+  /* Now fill in the children.  */
+  children = xnmalloc (nelements - 1, sizeof *children);
+
+  shorts
+    = (*android_java_env)->GetShortArrayElements (android_java_env, array,
+                                                 NULL);
+  for (i = 1; i < nelements; ++i)
+    children[i] = shorts[i];
+
+  /* Finally, return the parent and other values.  */
+  *root_return = 0;
+  *parent_return = shorts[0];
+  *children_return = children;
+
+  /* Release the array contents.  */
+  (*android_java_env)->ReleaseShortArrayElements (android_java_env, array,
+                                                 shorts, JNI_ABORT);
+
+  ANDROID_DELETE_LOCAL_REF (array);
+  return 1;
+}
+
+void
+android_get_geometry (android_window handle,
+                     android_window *root_return,
+                     int *x_return, int *y_return,
+                     unsigned int *width_return,
+                     unsigned int *height_return,
+                     unsigned int *border_width_return)
+{
+  jobject window;
+  jarray window_geometry;
+  jmethodID get_geometry;
+  jint *ints;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  get_geometry = android_lookup_method ("org/gnu/emacs/EmacsWindow",
+                                       "getWindowGeometry", "()[I");
+
+  window_geometry
+    = (*android_java_env)->CallObjectMethod (android_java_env,
+                                            window,
+                                            get_geometry);
+  if (!window_geometry)
+    {
+      (*android_java_env)->ExceptionClear (android_java_env);
+      memory_full (0);
+    }
+
+  /* window_geometry is an array containing x, y, width and
+     height.  border_width is always 0 on Android.  */
+  eassert ((*android_java_env)->GetArrayLength (android_java_env,
+                                               window_geometry)
+          == 4);
+
+  *root_return = 0;
+  *border_width_return = 0;
+
+  ints
+    = (*android_java_env)->GetIntArrayElements (android_java_env,
+                                               window_geometry,
+                                               NULL);
+
+  *x_return = ints[0];
+  *y_return = ints[1];
+  *width_return = ints[2];
+  *height_return = ints[3];
+
+  (*android_java_env)->ReleaseIntArrayElements (android_java_env,
+                                               window_geometry,
+                                               ints, JNI_ABORT);
+
+  /* Now free the local reference.  */
+  ANDROID_DELETE_LOCAL_REF (window_geometry);
+}
+
 
 
 /* Low level drawing primitives.  */
@@ -2998,6 +3305,86 @@ android_damage_window (android_drawable handle,
 
 
 
+/* Other misc system routines.  */
+
+int
+android_get_screen_width (void)
+{
+  return (*android_java_env)->CallIntMethod (android_java_env,
+                                            emacs_service,
+                                            service_class.get_screen_width,
+                                            (jboolean) false);
+}
+
+int
+android_get_screen_height (void)
+{
+  return (*android_java_env)->CallIntMethod (android_java_env,
+                                            emacs_service,
+                                            service_class.get_screen_height,
+                                            (jboolean) false);
+}
+
+int
+android_get_mm_width (void)
+{
+  return (*android_java_env)->CallIntMethod (android_java_env,
+                                            emacs_service,
+                                            service_class.get_screen_width,
+                                            (jboolean) true);
+}
+
+int
+android_get_mm_height (void)
+{
+  return (*android_java_env)->CallIntMethod (android_java_env,
+                                            emacs_service,
+                                            service_class.get_screen_height,
+                                            (jboolean) true);
+}
+
+bool
+android_detect_mouse (void)
+{
+  return (*android_java_env)->CallBooleanMethod (android_java_env,
+                                                emacs_service,
+                                                service_class.detect_mouse);
+}
+
+void
+android_set_dont_focus_on_map (android_window handle,
+                              bool no_focus_on_map)
+{
+  jmethodID method;
+  jobject window;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  method = android_lookup_method ("org/gnu/emacs/EmacsWindow",
+                                 "setDontFocusOnMap", "(Z)V");
+
+  (*android_java_env)->CallVoidMethod (android_java_env, window,
+                                      method,
+                                      (jboolean) no_focus_on_map);
+}
+
+void
+android_set_dont_accept_focus (android_window handle,
+                              bool no_accept_focus)
+{
+  jmethodID method;
+  jobject window;
+
+  window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+  method = android_lookup_method ("org/gnu/emacs/EmacsWindow",
+                                 "setDontAcceptFocus", "(Z)V");
+
+  (*android_java_env)->CallVoidMethod (android_java_env, window,
+                                      method,
+                                      (jboolean) no_accept_focus);
+}
+
+
+
 #undef faccessat
 
 /* Replace the system faccessat with one which understands AT_EACCESS.
@@ -3015,6 +3402,128 @@ faccessat (int dirfd, const char *pathname, int mode, 
int flags)
   return real_faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS);
 }
 
+
+
+/* Directory listing emulation.  */
+
+struct android_dir
+{
+  /* The real DIR *, if it exists.  */
+  DIR *dir;
+
+  /* Otherwise, the AAssetDir.  */
+  void *asset_dir;
+};
+
+/* Like opendir.  However, return an asset directory if NAME points to
+   an asset.  */
+
+struct android_dir *
+android_opendir (const char *name)
+{
+  struct android_dir *dir;
+  AAssetDir *asset_dir;
+  const char *asset_name;
+
+  asset_name = android_get_asset_name (name);
+
+  /* If the asset manager exists and NAME is an asset, return an asset
+     directory.  */
+  if (asset_manager && asset_name)
+    {
+      asset_dir = AAssetManager_openDir (asset_manager,
+                                        asset_name);
+
+      if (!asset_dir)
+       {
+         errno = ENOENT;
+         return NULL;
+       }
+
+      dir = xmalloc (sizeof *dir);
+      dir->dir = NULL;
+      dir->asset_dir = asset_dir;
+      return dir;
+    }
+
+  /* Otherwise, open the directory normally.  */
+  dir = xmalloc (sizeof *dir);
+  dir->asset_dir = NULL;
+  dir->dir = opendir (name);
+
+  if (!dir->dir)
+    {
+      xfree (dir);
+      return NULL;
+    }
+
+  return dir;
+}
+
+/* Like readdir, except it understands asset directories.  */
+
+struct dirent *
+android_readdir (struct android_dir *dir)
+{
+  static struct dirent dirent;
+  const char *filename;
+
+  if (dir->asset_dir)
+    {
+      filename = AAssetDir_getNextFileName (dir->asset_dir);
+      errno = 0;
+
+      if (!filename)
+       return NULL;
+
+      memset (&dirent, 0, sizeof dirent);
+      dirent.d_ino = 0;
+      dirent.d_off = 0;
+      dirent.d_reclen = sizeof dirent;
+      dirent.d_type = DT_UNKNOWN;
+      strncpy (dirent.d_name, filename,
+              sizeof dirent.d_name - 1);
+      return &dirent;
+    }
+
+  return readdir (dir->dir);
+}
+
+/* Like closedir, but it also closes asset manager directories.  */
+
+void
+android_closedir (struct android_dir *dir)
+{
+  if (dir->dir)
+    closedir (dir->dir);
+  else
+    AAssetDir_close (dir->asset_dir);
+
+  xfree (dir);
+}
+
+
+
+/* emacs_abort implementation for Android.  This logs a stack
+   trace.  */
+
+void
+emacs_abort (void)
+{
+  volatile char *foo;
+
+  __android_log_print (ANDROID_LOG_FATAL, __func__,
+                      "emacs_abort called, please review the ensuing"
+                      " stack trace");
+
+  /* Cause a NULL pointer dereference to make debuggerd generate a
+     tombstone.  */
+  foo = NULL;
+  *foo = '\0';
+
+  abort ();
+}
+
 #else /* ANDROID_STUBIFY */
 
 /* X emulation functions for Android.  */
diff --git a/src/android.h b/src/android.h
index 4cf194f23c..cddd315b9a 100644
--- a/src/android.h
+++ b/src/android.h
@@ -27,7 +27,9 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #ifndef ANDROID_STUBIFY
 #include <jni.h>
 #include <pwd.h>
+
 #include <sys/stat.h>
+#include <dirent.h>
 
 #include <android/bitmap.h>
 
@@ -53,6 +55,7 @@ extern int android_fstat (int, struct stat *);
 extern int android_fstatat (int, const char *restrict,
                            struct stat *restrict, int);
 extern int android_close (int);
+extern int android_fclose (FILE *);
 extern const char *android_get_home_directory (void);
 
 extern double android_pixel_density_x, android_pixel_density_y;
@@ -71,6 +74,26 @@ extern unsigned char *android_lock_bitmap (android_window,
                                           jobject *);
 extern void android_damage_window (android_window,
                                   struct android_rectangle *);
+extern int android_get_screen_width (void);
+extern int android_get_screen_height (void);
+extern int android_get_mm_width (void);
+extern int android_get_mm_height (void);
+extern bool android_detect_mouse (void);
+
+extern void android_set_dont_focus_on_map (android_window, bool);
+extern void android_set_dont_accept_focus (android_window, bool);
+
+
+
+/* Directory listing emulation.  */
+
+struct android_dir;
+
+extern struct android_dir *android_opendir (const char *);
+extern struct dirent *android_readdir (struct android_dir *);
+extern void android_closedir (struct android_dir *);
+
+
 
 #endif
 
diff --git a/src/androidfns.c b/src/androidfns.c
index 96c2746a21..459e407b90 100644
--- a/src/androidfns.c
+++ b/src/androidfns.c
@@ -21,6 +21,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <math.h>
 
 #include "lisp.h"
+#include "android.h"
 #include "androidterm.h"
 #include "blockinput.h"
 #include "keyboard.h"
@@ -151,6 +152,36 @@ android_decode_color (struct frame *f, Lisp_Object 
color_name, int mono_color)
   signal_error ("Undefined color", color_name);
 }
 
+static void
+android_set_parent_frame (struct frame *f, Lisp_Object new_value,
+                         Lisp_Object old_value)
+{
+  struct frame *p;
+
+  p = NULL;
+
+  if (!NILP (new_value)
+      && (!FRAMEP (new_value)
+         || !FRAME_LIVE_P (p = XFRAME (new_value))
+         || !FRAME_ANDROID_P (p)))
+    {
+      store_frame_param (f, Qparent_frame, old_value);
+      error ("Invalid specification of `parent-frame'");
+    }
+
+  if (p != FRAME_PARENT_FRAME (f))
+    {
+      block_input ();
+      android_reparent_window (FRAME_ANDROID_WINDOW (f),
+                              (p ? FRAME_ANDROID_WINDOW (p)
+                               : FRAME_DISPLAY_INFO (f)->root_window),
+                              f->left_pos, f->top_pos);
+      unblock_input ();
+
+      fset_parent_frame (f, new_value);
+    }
+}
+
 void
 android_implicitly_set_name (struct frame *f, Lisp_Object arg,
                             Lisp_Object oldval)
@@ -531,9 +562,9 @@ android_default_font_parameter (struct frame *f, 
Lisp_Object parms)
   if (! FONTP (font) && ! STRINGP (font))
     {
       const char *names[] = {
-       "Droid Sans Mono",
-       "monospace",
-       "DroidSansMono",
+       "Droid Sans Mono-12",
+       "Monospace-12",
+       "DroidSansMono-12",
        NULL
       };
       int i;
@@ -1119,8 +1150,7 @@ DEFUN ("x-display-pixel-width", Fx_display_pixel_width,
   error ("Android cross-compilation stub called!");
   return Qnil;
 #else
-  error ("Not implemented");
-  return Qnil;
+  return make_fixnum (android_get_screen_width ());
 #endif
 }
 
@@ -1133,8 +1163,7 @@ DEFUN ("x-display-pixel-height", Fx_display_pixel_height,
   error ("Android cross-compilation stub called!");
   return Qnil;
 #else
-  error ("Not implemented");
-  return Qnil;
+  return make_fixnum (android_get_screen_height ());
 #endif
 }
 
@@ -1185,8 +1214,7 @@ DEFUN ("x-display-mm-width", Fx_display_mm_width, 
Sx_display_mm_width,
   error ("Android cross-compilation stub called!");
   return Qnil;
 #else
-  error ("Not implemented");
-  return Qnil;
+  return make_fixnum (android_get_mm_width ());
 #endif
 }
 
@@ -1198,8 +1226,7 @@ DEFUN ("x-display-mm-height", Fx_display_mm_height, 
Sx_display_mm_height,
   error ("Android cross-compilation stub called!");
   return Qnil;
 #else
-  error ("Not implemented");
-  return Qnil;
+  return make_fixnum (android_get_mm_height ());
 #endif
 }
 
@@ -1225,77 +1252,373 @@ DEFUN ("x-display-visual-class", 
Fx_display_visual_class,
   return Qtrue_color;
 }
 
-DEFUN ("x-display-monitor-attributes-list", Fx_display_monitor_attributes_list,
-       Sx_display_monitor_attributes_list,
+DEFUN ("android-display-monitor-attributes-list",
+       Fandroid_display_monitor_attributes_list,
+       Sandroid_display_monitor_attributes_list,
        0, 1, 0,
-       doc: /* SKIP: real doc in xfns.c.  */)
+       doc: /* Return a list of physical monitor attributes on the X display 
TERMINAL.
+
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.
+
+Internal use only, use `display-monitor-attributes-list' instead.  */)
   (Lisp_Object terminal)
 {
 #ifdef ANDROID_STUBIFY
   error ("Android cross-compilation stub called!");
   return Qnil;
 #else
-  error ("Not implemented");
-  return Qnil;
+  struct MonitorInfo monitor;
+
+  memset (&monitor, 0, sizeof monitor);
+  monitor.geom.width = android_get_screen_width ();
+  monitor.geom.height = android_get_screen_height ();
+  monitor.mm_width = android_get_mm_width ();
+  monitor.mm_height = android_get_mm_height ();
+  monitor.work = monitor.geom;
+  monitor.name = (char *) "Android device monitor";
+
+  /* What to do about monitor_frames? */
+  return make_monitor_attribute_list (&monitor, 1,
+                                     0, Qnil, NULL);
 #endif
 }
 
-DEFUN ("x-frame-geometry", Fx_frame_geometry, Sx_frame_geometry,
-       0, 1, 0, doc: /* SKIP: real doc in xfns.c.  */)
-  (Lisp_Object terminal)
+#ifndef ANDROID_STUBIFY
+
+static Lisp_Object
+frame_geometry (Lisp_Object frame, Lisp_Object attribute)
+{
+  struct frame *f = decode_live_frame (frame);
+  android_window rootw;
+  unsigned int native_width, native_height, x_border_width = 0;
+  int x_native = 0, y_native = 0, xptr = 0, yptr = 0;
+  int left_off = 0, right_off = 0, top_off = 0, bottom_off = 0;
+  int outer_left, outer_top, outer_right, outer_bottom;
+  int native_left, native_top, native_right, native_bottom;
+  int inner_left, inner_top, inner_right, inner_bottom;
+  int internal_border_width;
+  bool menu_bar_external = false, tool_bar_external = false;
+  int menu_bar_height = 0, menu_bar_width = 0;
+  int tab_bar_height = 0, tab_bar_width = 0;
+  int tool_bar_height = 0, tool_bar_width = 0;
+
+  if (FRAME_INITIAL_P (f) || !FRAME_ANDROID_P (f)
+      || !FRAME_ANDROID_WINDOW (f))
+    return Qnil;
+
+  block_input ();
+  android_get_geometry (FRAME_ANDROID_WINDOW (f),
+                       &rootw, &x_native, &y_native,
+                       &native_width, &native_height, &x_border_width);
+  unblock_input ();
+
+  if (FRAME_PARENT_FRAME (f))
+    {
+      Lisp_Object parent, edges;
+
+      XSETFRAME (parent, FRAME_PARENT_FRAME (f));
+      edges = Fandroid_frame_edges (parent, Qnative_edges);
+      if (!NILP (edges))
+       {
+         x_native += XFIXNUM (Fnth (make_fixnum (0), edges));
+         y_native += XFIXNUM (Fnth (make_fixnum (1), edges));
+       }
+
+      outer_left = x_native;
+      outer_top = y_native;
+      outer_right = outer_left + native_width + 2 * x_border_width;
+      outer_bottom = outer_top + native_height + 2 * x_border_width;
+
+      native_left = x_native + x_border_width;
+      native_top = y_native + x_border_width;
+      native_right = native_left + native_width;
+      native_bottom = native_top + native_height;
+    }
+  else
+    {
+      outer_left = xptr;
+      outer_top = yptr;
+      outer_right = outer_left + left_off + native_width + right_off;
+      outer_bottom = outer_top + top_off + native_height + bottom_off;
+
+      native_left = outer_left + left_off;
+      native_top = outer_top + top_off;
+      native_right = native_left + native_width;
+      native_bottom = native_top + native_height;
+    }
+
+  internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f);
+  inner_left = native_left + internal_border_width;
+  inner_top = native_top + internal_border_width;
+  inner_right = native_right - internal_border_width;
+  inner_bottom = native_bottom - internal_border_width;
+
+  menu_bar_height = FRAME_MENU_BAR_HEIGHT (f);
+  inner_top += menu_bar_height;
+  menu_bar_width = menu_bar_height ? native_width : 0;
+
+  tab_bar_height = FRAME_TAB_BAR_HEIGHT (f);
+  tab_bar_width = (tab_bar_height
+                   ? native_width - 2 * internal_border_width
+                   : 0);
+  inner_top += tab_bar_height;
+
+  tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f);
+  tool_bar_width = (tool_bar_height
+                   ? native_width - 2 * internal_border_width
+                   : 0);
+  inner_top += tool_bar_height;
+
+  /* Construct list.  */
+  if (EQ (attribute, Qouter_edges))
+    return list4i (outer_left, outer_top, outer_right, outer_bottom);
+  else if (EQ (attribute, Qnative_edges))
+    return list4i (native_left, native_top, native_right, native_bottom);
+  else if (EQ (attribute, Qinner_edges))
+    return list4i (inner_left, inner_top, inner_right, inner_bottom);
+  else
+    return
+       list (Fcons (Qouter_position,
+                   Fcons (make_fixnum (outer_left),
+                          make_fixnum (outer_top))),
+            Fcons (Qouter_size,
+                   Fcons (make_fixnum (outer_right - outer_left),
+                          make_fixnum (outer_bottom - outer_top))),
+            /* Approximate.  */
+            Fcons (Qexternal_border_size,
+                   Fcons (make_fixnum (right_off),
+                          make_fixnum (bottom_off))),
+            Fcons (Qouter_border_width, make_fixnum (x_border_width)),
+            /* Approximate.  */
+            Fcons (Qtitle_bar_size,
+                   Fcons (make_fixnum (0),
+                          make_fixnum (top_off - bottom_off))),
+            Fcons (Qmenu_bar_external, menu_bar_external ? Qt : Qnil),
+            Fcons (Qmenu_bar_size,
+                   Fcons (make_fixnum (menu_bar_width),
+                          make_fixnum (menu_bar_height))),
+            Fcons (Qtab_bar_size,
+                   Fcons (make_fixnum (tab_bar_width),
+                          make_fixnum (tab_bar_height))),
+            Fcons (Qtool_bar_external, tool_bar_external ? Qt : Qnil),
+            Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)),
+            Fcons (Qtool_bar_size,
+                   Fcons (make_fixnum (tool_bar_width),
+                          make_fixnum (tool_bar_height))),
+            Fcons (Qinternal_border_width,
+                   make_fixnum (internal_border_width)));
+}
+
+#endif
+
+DEFUN ("android-frame-geometry", Fandroid_frame_geometry,
+       Sandroid_frame_geometry,
+       0, 1, 0,
+       doc: /* Return geometric attributes of FRAME.
+FRAME must be a live frame and defaults to the selected one.  The return
+value is an association list of the attributes listed below.  All height
+and width values are in pixels.
+
+`outer-position' is a cons of the outer left and top edges of FRAME
+  relative to the origin - the position (0, 0) - of FRAME's display.
+
+`outer-size' is a cons of the outer width and height of FRAME.  The
+  outer size includes the title bar and the external borders as well as
+  any menu and/or tool bar of frame.
+
+`external-border-size' is a cons of the horizontal and vertical width of
+  FRAME's external borders as supplied by the window manager.
+
+`title-bar-size' is a cons of the width and height of the title bar of
+  FRAME as supplied by the window manager.  If both of them are zero,
+  FRAME has no title bar.  If only the width is zero, Emacs was not
+  able to retrieve the width information.
+
+`menu-bar-external', if non-nil, means the menu bar is external (never
+  included in the inner edges of FRAME).
+
+`menu-bar-size' is a cons of the width and height of the menu bar of
+  FRAME.
+
+`tool-bar-external', if non-nil, means the tool bar is external (never
+  included in the inner edges of FRAME).
+
+`tool-bar-position' tells on which side the tool bar on FRAME is and can
+  be one of `left', `top', `right' or `bottom'.  If this is nil, FRAME
+  has no tool bar.
+
+`tool-bar-size' is a cons of the width and height of the tool bar of
+  FRAME.
+
+`internal-border-width' is the width of the internal border of
+  FRAME.  */)
+  (Lisp_Object frame)
 {
 #ifdef ANDROID_STUBIFY
   error ("Android cross-compilation stub called!");
   return Qnil;
 #else
-  error ("Not implemented");
+  return frame_geometry (frame, Qnil);
+#endif
+}
+
+DEFUN ("android-frame-edges", Fandroid_frame_edges, Sandroid_frame_edges, 0, 
2, 0,
+       doc: /* Return edge coordinates of FRAME.
+FRAME must be a live frame and defaults to the selected one.  The return
+value is a list of the form (LEFT, TOP, RIGHT, BOTTOM).  All values are
+in pixels relative to the origin - the position (0, 0) - of FRAME's
+display.
+
+If optional argument TYPE is the symbol `outer-edges', return the outer
+edges of FRAME.  The outer edges comprise the decorations of the window
+manager (like the title bar or external borders) as well as any external
+menu or tool bar of FRAME.  If optional argument TYPE is the symbol
+`native-edges' or nil, return the native edges of FRAME.  The native
+edges exclude the decorations of the window manager and any external
+menu or tool bar of FRAME.  If TYPE is the symbol `inner-edges', return
+the inner edges of FRAME.  These edges exclude title bar, any borders,
+menu bar or tool bar of FRAME.  */)
+  (Lisp_Object frame, Lisp_Object type)
+{
+#ifndef ANDROID_STUBIFY
+  return frame_geometry (frame, ((EQ (type, Qouter_edges)
+                                 || EQ (type, Qinner_edges))
+                                ? type
+                                : Qnative_edges));
+#else
   return Qnil;
 #endif
 }
 
-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.  */)
+#ifndef ANDROID_STUBIFY
+
+static Lisp_Object
+android_frame_list_z_order (struct android_display_info *dpyinfo,
+                           android_window window)
+{
+  android_window root, parent, *children;
+  unsigned int nchildren;
+  unsigned long i;
+  Lisp_Object frames;
+
+  frames = Qnil;
+
+  if (android_query_tree (window, &root, &parent,
+                         &children, &nchildren))
+    {
+      for (i = 0; i < nchildren; i++)
+       {
+         Lisp_Object frame, tail;
+
+         FOR_EACH_FRAME (tail, frame)
+            {
+              struct frame *cf = XFRAME (frame);
+
+              if (FRAME_ANDROID_P (cf)
+                  && (FRAME_ANDROID_WINDOW (cf) == children[i]))
+                frames = Fcons (frame, frames);
+            }
+       }
+
+      if (children)
+       xfree (children);
+    }
+
+  return frames;
+}
+
+#endif
+
+DEFUN ("android-frame-list-z-order", Fandroid_frame_list_z_order,
+       Sandroid_frame_list_z_order, 0, 1, 0,
+       doc: /* Return list of Emacs' frames, in Z (stacking) order.
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be either a frame or a display name (a string).  If
+omitted or nil, that stands for the selected frame's display.  Return
+nil if TERMINAL contains no Emacs frame.
+
+As a special case, if TERMINAL is non-nil and specifies a live frame,
+return the child frames of that frame in Z (stacking) order.
+
+Frames are listed from topmost (first) to bottommost (last).
+
+On Android, the order of the frames returned is undefined unless
+TERMINAL is a frame.  */)
   (Lisp_Object terminal)
 {
 #ifdef ANDROID_STUBIFY
   error ("Android cross-compilation stub called!");
   return Qnil;
 #else
-  error ("Not implemented");
-  return Qnil;
+  struct android_display_info *dpyinfo;
+  android_window window;
+
+  dpyinfo = check_android_display_info (terminal);
+
+  if (FRAMEP (terminal) && FRAME_LIVE_P (XFRAME (terminal)))
+    window = FRAME_ANDROID_WINDOW (XFRAME (terminal));
+  else
+    window = dpyinfo->root_window;
+
+  return android_frame_list_z_order (dpyinfo, window);
 #endif
 }
 
-DEFUN ("x-frame-restack", Fx_frame_restack, Sx_frame_restack, 2, 3, 0,
-       doc: /* SKIP: real doc in xfns.c.  */)
+DEFUN ("android-frame-restack", Fandroid_frame_restack,
+       Sandroid_frame_restack, 2, 3, 0,
+       doc: /* Restack FRAME1 below FRAME2.
+This means that if both frames are visible and the display areas of
+these frames overlap, FRAME2 (partially) obscures FRAME1.  If optional
+third argument ABOVE is non-nil, restack FRAME1 above FRAME2.  This
+means that if both frames are visible and the display areas of these
+frames overlap, FRAME1 (partially) obscures FRAME2.
+
+This may be thought of as an atomic action performed in two steps: The
+first step removes FRAME1's window-step window from the display.  The
+second step reinserts FRAME1's window below (above if ABOVE is true)
+that of FRAME2.  Hence the position of FRAME2 in its display's Z
+\(stacking) order relative to all other frames excluding FRAME1 remains
+unaltered.
+
+The Android system refuses to restack windows, so this does not
+work.  */)
   (Lisp_Object frame1, Lisp_Object frame2, Lisp_Object frame3)
 {
 #ifdef ANDROID_STUBIFY
   error ("Android cross-compilation stub called!");
   return Qnil;
 #else
-  error ("Not implemented");
+  /* This is not supported on Android because of limitations in the
+     platform that prevent ViewGroups from restacking
+     SurfaceViews.  */
   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.  */)
+DEFUN ("android-mouse-absolute-pixel-position",
+       Fandroid_mouse_absolute_pixel_position,
+       Sandroid_mouse_absolute_pixel_position, 0, 0, 0,
+       doc: /* Return absolute position of mouse cursor in pixels.
+The position is returned as a cons cell (X . Y) of the coordinates of
+the mouse cursor position in pixels relative to a position (0, 0) of the
+selected frame's display.  This does not work on Android.  */)
   (void)
 {
-  /* TODO: figure out how to implement this.  */
+  /* This cannot be implemented on Android.  */
   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.  */)
+DEFUN ("android-set-mouse-absolute-pixel-position",
+       Fandroid_set_mouse_absolute_pixel_position,
+       Sandroid_set_mouse_absolute_pixel_position, 2, 2, 0,
+       doc: /* Move mouse pointer to a pixel position at (X, Y).  The
+coordinates X and Y are interpreted to start from the top-left corner
+of the screen.  This does not work on Android.  */)
   (Lisp_Object x, Lisp_Object y)
 {
-  /* TODO: figure out how to implement this.  */
+  /* This cannot be implemented on Android.  */
   return Qnil;
 }
 
@@ -1364,6 +1687,20 @@ DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0,
 #endif
 }
 
+DEFUN ("android-detect-mouse", Fandroid_detect_mouse,
+       Sandroid_detect_mouse, 0, 0, 0,
+       doc: /* Figure out whether or not there is a mouse.
+Return non-nil if a mouse is connected to this computer, and nil if
+there is no mouse.  */)
+  (void)
+{
+#ifndef ANDROID_STUBIFY
+  return android_detect_mouse () ? Qt : Qnil;
+#else
+  return Qnil;
+#endif
+}
+
 
 
 #ifndef ANDROID_STUBIFY
@@ -1579,7 +1916,7 @@ android_set_menu_bar_lines (struct frame *f, Lisp_Object 
value,
          y = FRAME_TOP_MARGIN_HEIGHT (f);
 
          block_input ();
-         android_clear_area (FRAME_ANDROID_WINDOW (f),
+         android_clear_area (FRAME_ANDROID_DRAWABLE (f),
                              0, y, width, height);
          unblock_input ();
        }
@@ -1590,7 +1927,7 @@ android_set_menu_bar_lines (struct frame *f, Lisp_Object 
value,
          height = nlines * FRAME_LINE_HEIGHT (f) - y;
 
          block_input ();
-         android_clear_area (FRAME_ANDROID_WINDOW (f), 0, y,
+         android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0, y,
                              width, height);
          unblock_input ();
        }
@@ -1682,6 +2019,30 @@ android_set_alpha (struct frame *f, Lisp_Object arg, 
Lisp_Object oldval)
     }
 }
 
+static void
+android_set_no_focus_on_map (struct frame *f, Lisp_Object new_value,
+                            Lisp_Object old_value)
+{
+  if (!EQ (new_value, old_value))
+    {
+      android_set_dont_focus_on_map (FRAME_ANDROID_WINDOW (f),
+                                    new_value);
+      FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value);
+    }
+}
+
+static void
+android_set_no_accept_focus (struct frame *f, Lisp_Object new_value,
+                            Lisp_Object old_value)
+{
+  if (!EQ (new_value, old_value))
+    {
+      android_set_dont_accept_focus (FRAME_ANDROID_WINDOW (f),
+                                    new_value);
+      FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value);
+    }
+}
+
 frame_parm_handler android_frame_parm_handlers[] =
 {
   gui_set_autoraise,
@@ -1724,16 +2085,16 @@ frame_parm_handler android_frame_parm_handlers[] =
   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, */
+  NULL,
+  android_set_parent_frame,
+  NULL,
+  android_set_no_focus_on_map,
+  android_set_no_accept_focus,
+  NULL,
+  NULL,
   gui_set_no_special_glyphs,
-  NULL, /* x_set_alpha_background, */
-  NULL, /* x_set_use_frame_synchronization, */
+  NULL,
+  NULL,
 };
 
 #endif
@@ -1766,14 +2127,16 @@ syms_of_androidfns (void)
   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_display_monitor_attributes_list);
+  defsubr (&Sandroid_frame_geometry);
+  defsubr (&Sandroid_frame_edges);
+  defsubr (&Sandroid_frame_list_z_order);
+  defsubr (&Sandroid_frame_restack);
+  defsubr (&Sandroid_mouse_absolute_pixel_position);
+  defsubr (&Sandroid_set_mouse_absolute_pixel_position);
   defsubr (&Sandroid_get_connection);
   defsubr (&Sx_display_list);
   defsubr (&Sx_show_tip);
   defsubr (&Sx_hide_tip);
+  defsubr (&Sandroid_detect_mouse);
 }
diff --git a/src/androidfont.c b/src/androidfont.c
index b2c83201b0..b0a9f994a8 100644
--- a/src/androidfont.c
+++ b/src/androidfont.c
@@ -1,4 +1,4 @@
-/* Communication module for Android terminals.
+/* Android fallback font driver.
 
 Copyright (C) 2023 Free Software Foundation, Inc.
 
@@ -17,6 +17,10 @@ GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 
+/* Due to the terrible nature of the Android Typeface subsystems, this
+   font driver is only used as a fallback when sfntfont-android.c
+   fails to enumerate any fonts at all.  */
+
 #include <config.h>
 
 #include "lisp.h"
@@ -636,7 +640,7 @@ androidfont_draw (struct glyph_string *s, int from, int to,
 
   gcontext = android_resolve_handle (s->gc->gcontext,
                                     ANDROID_HANDLE_GCONTEXT);
-  drawable = android_resolve_handle (FRAME_ANDROID_WINDOW (s->f),
+  drawable = android_resolve_handle (FRAME_ANDROID_DRAWABLE (s->f),
                                     ANDROID_HANDLE_WINDOW);
   chars = (*android_java_env)->NewIntArray (android_java_env,
                                            to - from);
diff --git a/src/androidgui.h b/src/androidgui.h
index 7d045f7b45..422e72408c 100644
--- a/src/androidgui.h
+++ b/src/androidgui.h
@@ -222,6 +222,12 @@ enum android_event_type
     ANDROID_MOTION_NOTIFY,
     ANDROID_BUTTON_PRESS,
     ANDROID_BUTTON_RELEASE,
+    ANDROID_TOUCH_DOWN,
+    ANDROID_TOUCH_UP,
+    ANDROID_TOUCH_MOVE,
+    ANDROID_WHEEL,
+    ANDROID_ICONIFIED,
+    ANDROID_DEICONIFIED,
   };
 
 struct android_any_event
@@ -312,6 +318,54 @@ struct android_button_event
   unsigned int button;
 };
 
+struct android_touch_event
+{
+  /* Type of the event.  */
+  enum android_event_type type;
+
+  /* Window associated with the event.  */
+  android_window window;
+
+  /* X and Y coordinates of the event.  */
+  int x, y;
+
+  /* Time of the event, and the pointer identifier.  */
+  unsigned long time;
+
+  /* Index of the pointer being tracked.  */
+  unsigned int pointer_id;
+};
+
+struct android_wheel_event
+{
+  /* Type of the event.  */
+  enum android_event_type type;
+
+  /* Window associated with the event.  */
+  android_window window;
+
+  /* X and Y coordinates of the event.  */
+  int x, y;
+
+  /* Time of the event, and the pointer identifier.  */
+  unsigned long time;
+
+  /* Modifier state at the time of the event.  */
+  int state;
+
+  /* Motion alongside the X and Y axes.  */
+  double x_delta, y_delta;
+};
+
+struct android_iconify_event
+{
+  /* Type of the event.  */
+  enum android_event_type type;
+
+  /* Window associated with the event.  */
+  android_window window;
+};
+
 union android_event
 {
   enum android_event_type type;
@@ -323,8 +377,26 @@ union android_event
   struct android_crossing_event xcrossing;
   struct android_motion_event xmotion;
   struct android_button_event xbutton;
+
+  /* This has no parallel in X, since the X model of having
+     monotonically increasing touch IDs can't work on Android.  */
+  struct android_touch_event touch;
+
+  /* This has no parallel in X outside the X Input Extension, and
+     emulating the input extension interface would be awfully
+     complicated.  */
+  struct android_wheel_event wheel;
+
+  /* This has no parallel in X because Android doesn't have window
+     properties.  */
+  struct android_iconify_event iconified;
 };
 
+enum
+  {
+    ANDROID_CURRENT_TIME = 0L,
+  };
+
 extern int android_pending (void);
 extern void android_next_event (union android_event *);
 
@@ -396,6 +468,17 @@ extern void android_clear_area (android_window, int, int, 
unsigned int,
 extern android_pixmap android_create_bitmap_from_data (char *, unsigned int,
                                                       unsigned int);
 
+extern void android_bell (void);
+extern void android_set_input_focus (android_window, unsigned long);
+extern void android_raise_window (android_window);
+extern void android_lower_window (android_window);
+extern int android_query_tree (android_window, android_window *,
+                              android_window *, android_window **,
+                              unsigned int *);
+extern void android_get_geometry (android_window, android_window *,
+                                 int *, int *, unsigned int *,
+                                 unsigned int *, unsigned int *);
+
 #endif
 
 
diff --git a/src/androidterm.c b/src/androidterm.c
index 05fe7f01bf..220858c0fe 100644
--- a/src/androidterm.c
+++ b/src/androidterm.c
@@ -19,6 +19,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 #include <config.h>
 #include <stdio.h>
+#include <math.h>
 
 #include "lisp.h"
 #include "androidterm.h"
@@ -46,6 +47,11 @@ struct android_display_info *x_display_list;
 
 static bool any_help_event_p;
 
+/* Counters for tallying up scroll wheel events if
+   mwheel_coalesce_scroll_events is true.  */
+
+static double wheel_event_x, wheel_event_y;
+
 enum
   {
     ANDROID_EVENT_NORMAL,
@@ -83,19 +89,133 @@ 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));
+  android_clear_window (FRAME_ANDROID_DRAWABLE (f));
+}
+
+static void
+android_flash (struct frame *f)
+{
+  struct android_gc *gc;
+  struct android_gc_values values;
+  int rc;
+  fd_set fds;
+
+  block_input ();
+
+  values.function = ANDROID_GC_XOR;
+  values.foreground = (FRAME_FOREGROUND_PIXEL (f)
+                      ^ FRAME_BACKGROUND_PIXEL (f));
+
+  gc = android_create_gc ((ANDROID_GC_FUNCTION
+                          | ANDROID_GC_FOREGROUND),
+                         &values);
+
+  /* Get the height not including a menu bar widget.  */
+  int height = FRAME_PIXEL_HEIGHT (f);
+  /* Height of each line to flash.  */
+  int flash_height = FRAME_LINE_HEIGHT (f);
+  /* These will be the left and right margins of the rectangles.  */
+  int flash_left = FRAME_INTERNAL_BORDER_WIDTH (f);
+  int flash_right = FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f);
+  int width = flash_right - flash_left;
+
+  /* If window is tall, flash top and bottom line.  */
+  if (height > 3 * FRAME_LINE_HEIGHT (f))
+    {
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+                             flash_left,
+                             (FRAME_INTERNAL_BORDER_WIDTH (f)
+                              + FRAME_TOP_MARGIN_HEIGHT (f)),
+                             width, flash_height);
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+                             flash_left,
+                             (height - flash_height
+                              - FRAME_INTERNAL_BORDER_WIDTH (f)),
+                             width, flash_height);
+
+    }
+  else
+    /* If it is short, flash it all.  */
+    android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+                           flash_left, FRAME_INTERNAL_BORDER_WIDTH (f),
+                           width, (height - 2
+                                   * FRAME_INTERNAL_BORDER_WIDTH (f)));
+
+  flush_frame (f);
+
+  struct timespec delay = make_timespec (0, 150 * 1000 * 1000);
+  struct timespec wakeup = timespec_add (current_timespec (), delay);
+
+  /* Keep waiting until past the time wakeup or any input gets
+     available.  */
+  while (! detect_input_pending ())
+    {
+      struct timespec current = current_timespec ();
+      struct timespec timeout;
+
+      /* Break if result would not be positive.  */
+      if (timespec_cmp (wakeup, current) <= 0)
+       break;
+
+      /* How long `select' should wait.  */
+      timeout = make_timespec (0, 10 * 1000 * 1000);
+
+      /* Wait for some input to become available on the X
+        connection.  */
+      FD_ZERO (&fds);
+
+      /* Try to wait that long--but we might wake up sooner.  */
+      rc = pselect (0, &fds, NULL, NULL, &timeout, NULL);
+
+      /* Some input is available, exit the visible bell.  */
+      if (rc >= 0)
+       break;
+    }
+
+  /* If window is tall, flash top and bottom line.  */
+  if (height > 3 * FRAME_LINE_HEIGHT (f))
+    {
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+                             flash_left,
+                             (FRAME_INTERNAL_BORDER_WIDTH (f)
+                              + FRAME_TOP_MARGIN_HEIGHT (f)),
+                             width, flash_height);
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+                             flash_left,
+                             (height - flash_height
+                              - FRAME_INTERNAL_BORDER_WIDTH (f)),
+                             width, flash_height);
+    }
+  else
+    /* If it is short, flash it all.  */
+    android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+                           flash_left, FRAME_INTERNAL_BORDER_WIDTH (f),
+                           width, (height - 2
+                                   * FRAME_INTERNAL_BORDER_WIDTH (f)));
+
+  android_free_gc (gc);
+  flush_frame (f);
+
+  unblock_input ();
 }
 
 static void
 android_ring_bell (struct frame *f)
 {
-  /* TODO */
+  if (visible_bell)
+    android_flash (f);
+  else
+    {
+      block_input ();
+      android_bell ();
+      unblock_input ();
+    }
 }
 
 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
@@ -127,10 +247,17 @@ show_back_buffer (struct frame *f)
 {
   struct android_swap_info swap_info;
 
+  /* Somehow Android frames can be swapped while garbaged.  */
+  if (FRAME_GARBAGED_P (f))
+    return;
+
   memset (&swap_info, 0, sizeof (swap_info));
   swap_info.swap_window = FRAME_ANDROID_WINDOW (f);
   swap_info.swap_action = ANDROID_COPIED;
   android_swap_buffers (&swap_info, 1);
+
+  /* Now the back buffer no longer needs to be flipped.  */
+  FRAME_ANDROID_NEED_BUFFER_FLIP (f) = false;
 }
 
 /* Flip back buffers on F if it has undrawn content.  */
@@ -142,7 +269,8 @@ android_flush_dirty_back_buffer_on (struct frame *f)
       || buffer_flipping_blocked_p ()
       /* If the frame is not already up to date, do not flush buffers
         on input, as that will result in flicker.  */
-      || !FRAME_ANDROID_COMPLETE_P (f))
+      || !FRAME_ANDROID_COMPLETE_P (f)
+      && FRAME_ANDROID_NEED_BUFFER_FLIP (f))
     return;
 
   show_back_buffer (f);
@@ -174,13 +302,13 @@ static void android_frame_rehighlight (struct 
android_display_info *);
 static void
 android_lower_frame (struct frame *f)
 {
-  /* TODO.  */
+  android_lower_window (FRAME_ANDROID_WINDOW (f));
 }
 
 static void
 android_raise_frame (struct frame *f)
 {
-  /* TODO.  */
+  android_raise_window (FRAME_ANDROID_WINDOW (f));
 }
 
 static void
@@ -354,6 +482,46 @@ android_construct_mouse_click (struct input_event *result,
   return Qnil;
 }
 
+/* Generate a TOUCHSCREEN_UPDATE_EVENT for all pressed tools in FRAME.
+   Return the event in IE.  Do not set IE->timestamp, as that is left
+   to the caller.  */
+
+static void
+android_update_tools (struct frame *f, struct input_event *ie)
+{
+  struct android_touch_point *touchpoint;
+
+  ie->kind = TOUCHSCREEN_UPDATE_EVENT;
+  XSETFRAME (ie->frame_or_window, f);
+  ie->arg = Qnil;
+
+  /* Build the list of active touches.  */
+  for (touchpoint = FRAME_OUTPUT_DATA (f)->touch_points;
+       touchpoint; touchpoint = touchpoint->next)
+    ie->arg = Fcons (list3i (touchpoint->x,
+                            touchpoint->y,
+                            touchpoint->tool_id),
+                    ie->arg);
+}
+
+/* Find and return an existing tool pressed against FRAME, identified
+   by POINTER_ID.  Return NULL if no tool by that ID was found.  */
+
+static struct android_touch_point *
+android_find_tool (struct frame *f, int pointer_id)
+{
+  struct android_touch_point *touchpoint;
+
+  for (touchpoint = FRAME_OUTPUT_DATA (f)->touch_points;
+       touchpoint; touchpoint = touchpoint->next)
+    {
+      if (touchpoint->tool_id == pointer_id)
+       return touchpoint;
+    }
+
+  return NULL;
+}
+
 static int
 handle_one_android_event (struct android_display_info *dpyinfo,
                          union android_event *event, int *finish,
@@ -364,6 +532,10 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
   Mouse_HLInfo *hlinfo;
   union buffered_input_event inev;
   int modifiers, count, do_help;
+  struct android_touch_point *touchpoint, **last;
+  Lisp_Object window;
+  int scroll_height;
+  double scroll_unit;
 
   /* It is okay for this to not resemble handle_one_xevent so much.
      Differences in event handling code are much less nasty than
@@ -633,6 +805,28 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
 
       f = mouse_or_wdesc_frame (dpyinfo, event->xbutton.window);
 
+      if (f && event->xbutton.type == ANDROID_BUTTON_PRESS
+         && !popup_activated ()
+         /* && !x_window_to_scroll_bar (event->xbutton.display, */
+         /*                          event->xbutton.window, 2) */
+         && !FRAME_NO_ACCEPT_FOCUS (f))
+       {
+         /* When clicking into a child frame or when clicking
+            into a parent frame with the child frame selected and
+            `no-accept-focus' is not set, select the clicked
+            frame.  */
+         struct frame *hf = dpyinfo->highlight_frame;
+
+         if (FRAME_PARENT_FRAME (f) || (hf && frame_ancestor_p (f, hf)))
+           {
+             android_set_input_focus (FRAME_ANDROID_WINDOW (f),
+                                      event->xbutton.time);
+
+             if (FRAME_PARENT_FRAME (f))
+               android_raise_window (FRAME_ANDROID_WINDOW (f));
+           }
+       }
+
       if (f)
        {
          /* Is this in the tab-bar?  */
@@ -715,6 +909,223 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
 
       goto OTHER;
 
+      /* Touch events.  The events here don't parallel X so much.  */
+    case ANDROID_TOUCH_DOWN:
+
+      if (!any)
+       goto OTHER;
+
+      /* This event is sent when a tool is put on the screen.  X and Y
+        are the location of the finger, and pointer_id identifies the
+        tool for as long as it is still held down.  First, see if the
+        touch point already exists and can be reused (this shouldn't
+        happen, but be safe.)  */
+
+      touchpoint = android_find_tool (any, event->touch.pointer_id);
+
+      if (touchpoint)
+       {
+         /* Simply update the tool position and send an update.  */
+         touchpoint->x = event->touch.x;
+         touchpoint->y = event->touch.x;
+         android_update_tools (any, &inev.ie);
+         inev.ie.timestamp = event->touch.time;
+
+         goto OTHER;
+       }
+
+      /* Otherwise, link a new touchpoint onto the output's list of
+        pressed tools.  */
+
+      touchpoint = xmalloc (sizeof *touchpoint);
+      touchpoint->tool_id = event->touch.pointer_id;
+      touchpoint->x = event->touch.x;
+      touchpoint->y = event->touch.x;
+      touchpoint->next = FRAME_OUTPUT_DATA (any)->touch_points;
+      FRAME_OUTPUT_DATA (any)->touch_points = touchpoint;
+
+      /* Now generate the Emacs event.  */
+      inev.ie.kind = TOUCHSCREEN_BEGIN_EVENT;
+      inev.ie.timestamp = event->touch.time;
+      XSETFRAME (inev.ie.frame_or_window, any);
+      XSETINT (inev.ie.x, event->touch.x);
+      XSETINT (inev.ie.y, event->touch.y);
+      XSETINT (inev.ie.arg, event->touch.pointer_id);
+
+      goto OTHER;
+
+    case ANDROID_TOUCH_MOVE:
+
+      if (!any)
+       goto OTHER;
+
+      /* Look for the tool that moved.  */
+
+      touchpoint = android_find_tool (any, event->touch.pointer_id);
+
+      /* If it doesn't exist, skip processing this event.  */
+
+      if (!touchpoint)
+       goto OTHER;
+
+      /* Otherwise, update the position and send the update event.  */
+
+      touchpoint->x = event->touch.x;
+      touchpoint->y = event->touch.y;
+      android_update_tools (any, &inev.ie);
+      inev.ie.timestamp = event->touch.time;
+
+      goto OTHER;
+
+    case ANDROID_TOUCH_UP:
+
+      if (!any)
+       goto OTHER;
+
+      /* Now find and unlink the tool in question.  */
+
+      last = &FRAME_OUTPUT_DATA (any)->touch_points;
+      while ((touchpoint = *last))
+       {
+         if (touchpoint->tool_id == event->touch.pointer_id)
+           {
+             *last = touchpoint->next;
+
+             /* The tool was unlinked.  Free it and generate the
+                appropriate Emacs event.  */
+             xfree (touchpoint);
+             inev.ie.kind = TOUCHSCREEN_END_EVENT;
+             inev.ie.timestamp = event->touch.time;
+
+             XSETFRAME (inev.ie.frame_or_window, any);
+             XSETINT (inev.ie.x, event->touch.x);
+             XSETINT (inev.ie.y, event->touch.y);
+             XSETINT (inev.ie.arg, event->touch.pointer_id);
+
+             /* Break out of the loop.  */
+             goto OTHER;
+           }
+         else
+           last = &touchpoint->next;
+       }
+
+      /* No touch point was found.  This shouldn't happen.  */
+      goto OTHER;
+
+      /* Wheel motion.  The events here don't parallel X because
+        Android doesn't have scroll valuators.  */
+
+    case ANDROID_WHEEL:
+
+      if (!any)
+       goto OTHER;
+
+      if (fabs (event->wheel.x_delta) > 0
+         || fabs (event->wheel.y_delta) > 0)
+       {
+         if (mwheel_coalesce_scroll_events)
+           {
+             if (signbit (event->wheel.x_delta)
+                 != signbit (wheel_event_x))
+               wheel_event_x = 0.0;
+
+             if (signbit (event->wheel.y_delta)
+                 != signbit (wheel_event_y))
+               wheel_event_y = 0.0;
+
+             /* Tally up deltas until one of them exceeds 1.0.  */
+             wheel_event_x += event->wheel.x_delta;
+             wheel_event_y += event->wheel.y_delta;
+
+             if (fabs (wheel_event_x) < 1.0
+                 && fabs (wheel_event_y) < 1.0)
+               goto OTHER;
+           }
+         else
+           {
+             /* Use the deltas in the event.  */
+             wheel_event_x = event->wheel.x_delta;
+             wheel_event_y = event->wheel.y_delta;
+           }
+
+         /* Determine what kind of event to send.  */
+         inev.ie.kind = ((fabs (wheel_event_y)
+                          >= fabs (wheel_event_x))
+                         ? WHEEL_EVENT : HORIZ_WHEEL_EVENT);
+         inev.ie.timestamp = event->wheel.time;
+
+         /* Set the event coordinates.  */
+         XSETINT (inev.ie.x, event->wheel.x);
+         XSETINT (inev.ie.y, event->wheel.y);
+
+         /* Set the frame.  */
+         XSETFRAME (inev.ie.frame_or_window, any);
+
+         /* Figure out the scroll direction.  */
+         inev.ie.modifiers = (signbit ((fabs (wheel_event_x)
+                                        >= fabs (wheel_event_y))
+                                       ? wheel_event_x
+                                       : wheel_event_y)
+                              ? down_modifier : up_modifier);
+
+         /* Figure out how much to scale the deltas by.  */
+         window = window_from_coordinates (any, event->wheel.x,
+                                           event->wheel.y, NULL,
+                                           false, false);
+
+         if (WINDOWP (window))
+           scroll_height = XWINDOW (window)->pixel_height;
+         else
+           /* EVENT_X and EVENT_Y can be outside the
+              frame if F holds the input grab, so fall
+              back to the height of the frame instead.  */
+           scroll_height = FRAME_PIXEL_HEIGHT (any);
+
+         scroll_unit = pow (scroll_height, 2.0 / 3.0);
+
+         /* Add the keyboard modifiers.  */
+         inev.ie.modifiers
+           |= android_android_to_emacs_modifiers (dpyinfo,
+                                                  event->wheel.state);
+
+         /* Finally include the scroll deltas.  */
+         inev.ie.arg = list3 (Qnil,
+                              make_float (wheel_event_x
+                                          * scroll_unit),
+                              make_float (wheel_event_y
+                                          * scroll_unit));
+
+         wheel_event_x = 0.0;
+         wheel_event_y = 0.0;
+       }
+
+      goto OTHER;
+
+      /* Iconification.  This is vastly simpler than on X.  */
+    case ANDROID_ICONIFIED:
+
+      if (FRAME_ICONIFIED_P (any))
+       goto OTHER;
+
+      SET_FRAME_VISIBLE (any, false);
+      SET_FRAME_ICONIFIED (any, true);
+
+      inev.ie.kind = ICONIFY_EVENT;
+      XSETFRAME (inev.ie.frame_or_window, any);
+      goto OTHER;
+
+    case ANDROID_DEICONIFIED:
+
+      if (!FRAME_ICONIFIED_P (any))
+       goto OTHER;
+
+      SET_FRAME_VISIBLE (any, true);
+      SET_FRAME_ICONIFIED (any, false);
+
+      inev.ie.kind = DEICONIFY_EVENT;
+      XSETFRAME (inev.ie.frame_or_window, any);
+      goto OTHER;
+
     default:
       goto OTHER;
     }
@@ -781,8 +1192,7 @@ android_read_socket (struct terminal *terminal,
      now.  */
   if (dpyinfo->pending_autoraise_frame)
     {
-      /* android_raise_frame (dpyinfo->pending_autoraise_frame);
-        TODO */
+      android_raise_frame (dpyinfo->pending_autoraise_frame);
       dpyinfo->pending_autoraise_frame = NULL;
     }
 
@@ -796,7 +1206,8 @@ android_frame_up_to_date (struct frame *f)
   block_input ();
   FRAME_MOUSE_UPDATE (f);
 
-  if (!buffer_flipping_blocked_p ())
+  if (!buffer_flipping_blocked_p ()
+      && FRAME_ANDROID_NEED_BUFFER_FLIP (f))
     show_back_buffer (f);
 
   /* The frame is now complete, as its contents have been drawn.  */
@@ -808,7 +1219,10 @@ static void
 android_buffer_flipping_unblocked_hook (struct frame *f)
 {
   block_input ();
-  show_back_buffer (f);
+
+  if (FRAME_ANDROID_NEED_BUFFER_FLIP (f))
+    show_back_buffer (f);
+
   unblock_input ();
 }
 
@@ -935,7 +1349,25 @@ android_get_focus_frame (struct frame *f)
 static void
 android_focus_frame (struct frame *f, bool noactivate)
 {
-  /* TODO */
+  /* Set the input focus to the frame's window.  The system only lets
+     this work on child frames.  */
+  android_set_input_focus (FRAME_ANDROID_WINDOW (f),
+                          ANDROID_CURRENT_TIME);
+}
+
+/* The two procedures below only have to update the cursor on Android,
+   as there are no window borders there.  */
+
+static void
+android_frame_highlight (struct frame *f)
+{
+  gui_update_cursor (f, true);
+}
+
+static void
+android_frame_unhighlight (struct frame *f)
+{
+  gui_update_cursor (f, true);
 }
 
 static void
@@ -963,12 +1395,10 @@ android_frame_rehighlight (struct android_display_info 
*dpyinfo)
   if (dpyinfo->highlight_frame != old_highlight)
     {
       /* This is not yet required on Android.  */
-#if 0
       if (old_highlight)
        android_frame_unhighlight (old_highlight);
       if (dpyinfo->highlight_frame)
        android_frame_highlight (dpyinfo->highlight_frame);
-#endif
     }
 }
 
@@ -1027,7 +1457,8 @@ android_fullscreen_hook (struct frame *f)
 void
 android_iconify_frame (struct frame *f)
 {
-  /* TODO */
+  /* This really doesn't work on Android.  */
+  error ("Can't notify window manager of iconification");
 }
 
 static void
@@ -1149,7 +1580,7 @@ android_set_offset (struct frame *f, int xoff, int yoff,
 static void
 android_set_alpha (struct frame *f)
 {
-  /* TODO */
+  /* Not supported on Android.  */
 }
 
 static Lisp_Object
@@ -1215,6 +1646,7 @@ android_free_frame_resources (struct frame *f)
 {
   struct android_display_info *dpyinfo;
   Mouse_HLInfo *hlinfo;
+  struct android_touch_point *last, *next;
 
   dpyinfo = FRAME_DISPLAY_INFO (f);
   hlinfo = &dpyinfo->mouse_highlight;
@@ -1256,6 +1688,18 @@ android_free_frame_resources (struct frame *f)
   if (f == dpyinfo->last_mouse_frame)
     dpyinfo->last_mouse_frame = NULL;
 
+  /* Free all tool presses currently active on this frame.  */
+  next = FRAME_OUTPUT_DATA (f)->touch_points;
+  while (next)
+    {
+      last = next;
+      next = next->next;
+      xfree (last);
+    }
+
+  /* Clear this in case unblock_input reads events.  */
+  FRAME_OUTPUT_DATA (f)->touch_points = NULL;
+
   unblock_input ();
 }
 
@@ -1316,8 +1760,8 @@ android_scroll_run (struct window *w, struct run *run)
   /* 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),
+  android_copy_area (FRAME_ANDROID_DRAWABLE (f),
+                    FRAME_ANDROID_DRAWABLE (f),
                     f->output_data.android->normal_gc,
                     x, from_y, width, height, x, to_y);
 
@@ -1337,7 +1781,9 @@ static void
 android_flip_and_flush (struct frame *f)
 {
   block_input ();
-  show_back_buffer (f);
+
+  if (FRAME_ANDROID_NEED_BUFFER_FLIP (f))
+    show_back_buffer (f);
 
   /* The frame is complete again as its contents were just
      flushed.  */
@@ -1355,7 +1801,7 @@ android_clear_rectangle (struct frame *f, struct 
android_gc *gc, int x,
                              | ANDROID_GC_FOREGROUND),
                         &xgcv);
   android_set_foreground (gc, xgcv.background);
-  android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc,
+  android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
                          x, y, width, height);
   android_set_foreground (gc, xgcv.foreground);
 }
@@ -1405,7 +1851,7 @@ android_draw_fringe_bitmap (struct window *w, struct 
glyph_row *row,
       if (face->stipple)
        {
          android_set_fill_style (face->gc, ANDROID_FILL_OPAQUE_STIPPLED);
-         android_fill_rectangle (FRAME_ANDROID_WINDOW (f), face->gc,
+         android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), face->gc,
                                  p->bx, p->by, p->nx, p->ny);
          android_set_fill_style (face->gc, ANDROID_FILL_SOLID);
 
@@ -1428,7 +1874,7 @@ android_draw_fringe_bitmap (struct window *w, struct 
glyph_row *row,
       unsigned long background, cursor_pixel;
       int depth;
 
-      drawable = FRAME_ANDROID_WINDOW (f);
+      drawable = FRAME_ANDROID_DRAWABLE (f);
       clipmask = ANDROID_NONE;
       background = face->background;
       cursor_pixel = f->output_data.android->cursor_pixel;
@@ -1697,7 +2143,7 @@ android_draw_glyph_string_background (struct glyph_string 
*s, bool force_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,
+         android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
                                  s->x, s->y + box_line_width,
                                  s->background_width,
                                  s->height - 2 * box_line_width);
@@ -1734,7 +2180,7 @@ android_fill_triangle (struct frame *f, struct android_gc 
*gc,
   abc[1] = point2;
   abc[2] = point3;
 
-  android_fill_polygon (FRAME_ANDROID_WINDOW (f),
+  android_fill_polygon (FRAME_ANDROID_DRAWABLE (f),
                        gc, abc, 3, ANDROID_CONVEX,
                        ANDROID_COORD_MODE_ORIGIN);
 }
@@ -1776,7 +2222,7 @@ android_clear_point (struct frame *f, struct android_gc 
*gc,
   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_draw_point (FRAME_ANDROID_DRAWABLE (f), gc, x, y);
   android_set_foreground (gc, xgcv.foreground);
 }
 
@@ -1798,7 +2244,7 @@ android_draw_relief_rect (struct frame *f, int left_x, 
int top_y, int right_x,
   black_gc = f->output_data.android->black_relief.gc;
   normal_gc = f->output_data.android->normal_gc;
 
-  drawable = FRAME_ANDROID_WINDOW (f);
+  drawable = FRAME_ANDROID_DRAWABLE (f);
 
   android_set_clip_rectangles (white_gc, 0, 0, clip_rect, 1);
   android_set_clip_rectangles (black_gc, 0, 0, clip_rect, 1);
@@ -1811,11 +2257,11 @@ android_draw_relief_rect (struct frame *f, int left_x, 
int top_y, int right_x,
   /* Draw lines.  */
 
   if (top_p)
-    android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, left_x, top_y,
+    android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x, top_y,
                            right_x - left_x + 1, hwidth);
 
   if (left_p)
-    android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, left_x, top_y,
+    android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x, top_y,
                            vwidth, bottom_y - top_y + 1);
 
   if (raised_p)
@@ -1824,12 +2270,12 @@ android_draw_relief_rect (struct frame *f, int left_x, 
int top_y, int right_x,
     gc = white_gc;
 
   if (bot_p)
-    android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, left_x,
+    android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x,
                            bottom_y - hwidth + 1,
                            right_x - left_x + 1, hwidth);
 
   if (right_p)
-    android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc,
+    android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
                            right_x - vwidth + 1,
                            top_y, vwidth, bottom_y - top_y + 1);
 
@@ -1853,7 +2299,7 @@ android_draw_relief_rect (struct frame *f, int left_x, 
int top_y, int right_x,
 
   if (top_p && left_p && bot_p && right_p
       && hwidth > 1 && vwidth > 1)
-    android_draw_rectangle (FRAME_ANDROID_WINDOW (f),
+    android_draw_rectangle (FRAME_ANDROID_DRAWABLE (f),
                            black_gc, left_x, top_y,
                            right_x - left_x, bottom_y - top_y);
   else
@@ -1913,22 +2359,22 @@ android_draw_box_rect (struct glyph_string *s,
   android_set_clip_rectangles (s->gc, 0, 0, clip_rect, 1);
 
   /* Top.  */
-  android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, left_x,
+  android_fill_rectangle (FRAME_ANDROID_DRAWABLE (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,
+    android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x,
                            top_y, vwidth, bottom_y - top_y + 1);
 
   /* Bottom.  */
-  android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, left_x,
+  android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x,
                          bottom_y - hwidth + 1, right_x - left_x + 1,
                          hwidth);
 
   /* Right.  */
   if (right_p)
-    android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc,
+    android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
                            right_x - vwidth + 1, top_y, vwidth,
                            bottom_y - top_y + 1);
 
@@ -2153,7 +2599,7 @@ android_draw_glyph_string_bg_rect (struct glyph_string 
*s, int x, int y,
     {
       /* 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,
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, x,
                              y, w, h);
       android_set_fill_style (s->gc, ANDROID_FILL_SOLID);
     }
@@ -2294,7 +2740,7 @@ android_draw_image_foreground (struct glyph_string *s)
 
       if (gui_intersect_rectangles (&clip_rect, &image_rect, &r))
        android_copy_area (s->img->pixmap,
-                          FRAME_ANDROID_WINDOW (s->f),
+                          FRAME_ANDROID_DRAWABLE (s->f),
                           s->gc, s->slice.x + r.x - x,
                           s->slice.y + r.y - y,
                           r.width, r.height, r.x, r.y);
@@ -2307,7 +2753,7 @@ android_draw_image_foreground (struct glyph_string *s)
       if (s->hl == DRAW_CURSOR && !s->img->mask)
        {
          int relief = eabs (s->img->relief);
-         android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc,
+         android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
                                  x - relief, y - relief,
                                  s->slice.width + relief*2 - 1,
                                  s->slice.height + relief*2 - 1);
@@ -2317,7 +2763,7 @@ android_draw_image_foreground (struct glyph_string *s)
     }
   else
     /* Draw a rectangle if image could not be loaded.  */
-    android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc, x, y,
+    android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, x, y,
                            s->slice.width - 1, s->slice.height - 1);
 }
 
@@ -2444,7 +2890,7 @@ android_draw_stretch_glyph_string (struct glyph_string *s)
            {
              /* Fill background with a stipple pattern.  */
              android_set_fill_style (gc, ANDROID_FILL_OPAQUE_STIPPLED);
-             android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f),
+             android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
                                      gc, x, y, w, h);
              android_set_fill_style (gc, ANDROID_FILL_SOLID);
 
@@ -2457,7 +2903,7 @@ android_draw_stretch_glyph_string (struct glyph_string *s)
                                          | ANDROID_GC_BACKGROUND),
                                     &xgcv);
              android_set_foreground (gc, xgcv.background);
-             android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f),
+             android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
                                      gc, x, y, w, h);
              android_set_foreground (gc, xgcv.foreground);
            }
@@ -2536,7 +2982,7 @@ android_draw_underwave (struct glyph_string *s, int 
decoration_width)
 
   while (x1 <= xmax)
     {
-      android_draw_line (FRAME_ANDROID_WINDOW (s->f), s->gc,
+      android_draw_line (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
                         x1, y1, x2, y2);
       x1  = x2, y1 = y2;
       x2 += dx, y2 = y0 + odd*dy;
@@ -2567,7 +3013,7 @@ android_draw_glyph_string_foreground (struct glyph_string 
*s)
       for (i = 0; i < s->nchars; ++i)
        {
          struct glyph *g = s->first_glyph + i;
-         android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f),
+         android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
                                  s->gc, x, s->y,
                                  g->pixel_width - 1,
                                  s->height - 1);
@@ -2618,7 +3064,7 @@ android_draw_composite_glyph_string_foreground (struct 
glyph_string *s)
   if (s->font_not_found_p)
     {
       if (s->cmp_from == 0)
-       android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f),
+       android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
                                s->gc, x, s->y,
                                s->width - 1, s->height - 1);
     }
@@ -2754,7 +3200,7 @@ android_draw_glyphless_glyph_string_foreground (struct 
glyph_string *s)
                                 false);
        }
       if (glyph->u.glyphless.method != GLYPHLESS_DISPLAY_THIN_SPACE)
-       android_draw_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc,
+       android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
                                x, s->ybase - glyph->ascent,
                                glyph->pixel_width - 1,
                                glyph->ascent + glyph->descent - 1);
@@ -2987,14 +3433,14 @@ android_draw_glyph_string (struct glyph_string *s)
               s->underline_position = position;
               y = s->ybase + position;
               if (s->face->underline_defaulted_p)
-                android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc,
+                android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
                                        s->x, y, decoration_width, thickness);
               else
                 {
                   struct android_gc_values xgcv;
                   android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
                   android_set_foreground (s->gc, s->face->underline_color);
-                  android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f), s->gc,
+                  android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
                                          s->x, y, decoration_width, thickness);
                   android_set_foreground (s->gc, xgcv.foreground);
                 }
@@ -3006,7 +3452,7 @@ android_draw_glyph_string (struct glyph_string *s)
          unsigned long dy = 0, h = 1;
 
          if (s->face->overline_color_defaulted_p)
-           android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f),
+           android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
                                    s->gc, s->x, s->y + dy,
                                    decoration_width, h);
          else
@@ -3014,8 +3460,8 @@ android_draw_glyph_string (struct glyph_string *s)
              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_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+                                     s->x, s->y + dy, decoration_width, h);
              android_set_foreground (s->gc, xgcv.foreground);
            }
        }
@@ -3044,8 +3490,9 @@ android_draw_glyph_string (struct glyph_string *s)
              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_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+                                     s->x, glyph_y + dy, decoration_width,
+                                     h);
              android_set_foreground (s->gc, xgcv.foreground);
            }
        }
@@ -3125,7 +3572,7 @@ static void
 android_clear_frame_area (struct frame *f, int x, int y,
                          int width, int height)
 {
-  android_clear_area (FRAME_ANDROID_WINDOW (f),
+  android_clear_area (FRAME_ANDROID_DRAWABLE (f),
                      x, y, width, height);
 }
 
@@ -3154,25 +3601,25 @@ android_clear_under_internal_border (struct frame *f)
          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,
+         android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0, margin,
                                  width, border);
-         android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, 0, 0,
+         android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0, 0,
                                  border, height);
-         android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, width - border,
+         android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, width - 
border,
                                  0, border, height);
-         android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, 0,
+         android_fill_rectangle (FRAME_ANDROID_DRAWABLE (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,
+         android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0, 0,
                              border, height);
-         android_clear_area (FRAME_ANDROID_WINDOW (f), 0,
+         android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0,
                              margin, width, border);
-         android_clear_area (FRAME_ANDROID_WINDOW (f), width - border,
+         android_clear_area (FRAME_ANDROID_DRAWABLE (f), width - border,
                              0, border, height);
-         android_clear_area (FRAME_ANDROID_WINDOW (f), 0,
+         android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0,
                              height - border, width, border);
        }
     }
@@ -3221,7 +3668,7 @@ android_draw_hollow_cursor (struct window *w, struct 
glyph_row *row)
     }
   /* 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_draw_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x, y, wd, h - 1);
   android_reset_clip_rectangles (f, gc);
 }
 
@@ -3295,7 +3742,7 @@ android_draw_bar_cursor (struct window *w, struct 
glyph_row *row, int width,
          if ((cursor_glyph->resolved_level & 1) != 0)
            x += cursor_glyph->pixel_width - width;
 
-         android_fill_rectangle (FRAME_ANDROID_WINDOW (f), gc, x,
+         android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x,
                                  WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y),
                                  width, row->height);
        }
@@ -3318,7 +3765,7 @@ android_draw_bar_cursor (struct window *w, struct 
glyph_row *row, int 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,
+         android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x,
                                  cursor_start_y,
                                  w->phys_cursor_width - 1, width);
        }
@@ -3387,7 +3834,7 @@ android_draw_vertical_window_border (struct window *w, 
int x, int y0, int y1)
     android_set_foreground (f->output_data.android->normal_gc,
                            face->foreground);
 
-  android_draw_line (FRAME_ANDROID_WINDOW (f),
+  android_draw_line (FRAME_ANDROID_DRAWABLE (f),
                     f->output_data.android->normal_gc,
                     x, y0, x, y1);
 }
@@ -3415,17 +3862,17 @@ android_draw_window_divider (struct window *w, int x0, 
int x1, int y0, int y1)
     {
       android_set_foreground (f->output_data.android->normal_gc,
                              color_first);
-      android_fill_rectangle (FRAME_ANDROID_WINDOW (f),
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
                              f->output_data.android->normal_gc,
                              x0, y0, 1, y1 - y0);
       android_set_foreground (f->output_data.android->normal_gc,
                              color);
-      android_fill_rectangle (FRAME_ANDROID_WINDOW (f),
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
                              f->output_data.android->normal_gc,
                              x0 + 1, y0, x1 - x0 - 2, y1 - y0);
       android_set_foreground (f->output_data.android->normal_gc,
                              color_last);
-      android_fill_rectangle (FRAME_ANDROID_WINDOW (f),
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
                              f->output_data.android->normal_gc,
                              x1 - 1, y0, 1, y1 - y0);
     }
@@ -3435,16 +3882,16 @@ android_draw_window_divider (struct window *w, int x0, 
int x1, int y0, int y1)
     {
       android_set_foreground (f->output_data.android->normal_gc,
                              color_first);
-      android_fill_rectangle (FRAME_ANDROID_WINDOW (f),
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
                              f->output_data.android->normal_gc,
                              x0, y0, x1 - x0, 1);
       android_set_foreground (f->output_data.android->normal_gc, color);
-      android_fill_rectangle (FRAME_ANDROID_WINDOW (f),
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
                              f->output_data.android->normal_gc,
                              x0, y0 + 1, x1 - x0, y1 - y0 - 2);
       android_set_foreground (f->output_data.android->normal_gc,
                              color_last);
-      android_fill_rectangle (FRAME_ANDROID_WINDOW (f),
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
                              f->output_data.android->normal_gc,
                              x0, y1 - 1, x1 - x0, 1);
     }
@@ -3453,7 +3900,7 @@ android_draw_window_divider (struct window *w, int x0, 
int x1, int y0, int y1)
       /* 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),
+      android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
                              f->output_data.android->normal_gc,
                              x0, y0, x1 - x0, y1 - y0);
     }
diff --git a/src/androidterm.h b/src/androidterm.h
index 814bcf9f08..ebde15c40a 100644
--- a/src/androidterm.h
+++ b/src/androidterm.h
@@ -134,6 +134,18 @@ struct android_display_info
   Time last_mouse_movement_time;
 };
 
+/* Structure representing a single tool (finger or stylus) pressed
+   onto a frame.  */
+
+struct android_touch_point
+{
+  /* The next tool on this list.  */
+  struct android_touch_point *next;
+
+  /* The tool ID and the last known X and Y positions.  */
+  int tool_id, x, y;
+};
+
 struct android_output
 {
   /* Graphics contexts for the default font.  */
@@ -201,6 +213,10 @@ struct android_output
      input.  */
   bool_bf complete : 1;
 
+  /* True that indicates whether or not a buffer flip is required
+     because the frame contents have been dirtied.  */
+  bool_bf need_buffer_flip : 1;
+
   /* Relief GCs, colors etc.  */
   struct relief {
     struct android_gc *gc;
@@ -214,6 +230,10 @@ struct android_output
   /* Focus state.  Only present for consistency with X; it is actually
      a boolean.  */
   int focus_state;
+
+  /* List of all tools (either styluses or fingers) pressed onto the
+     frame.  */
+  struct android_touch_point *touch_points;
 };
 
 enum
@@ -240,6 +260,14 @@ enum
 #define FRAME_ANDROID_NEED_BUFFER_FLIP(f)      \
   ((f)->output_data.android->need_buffer_flip)
 
+/* Return the drawable used for rendering to frame F and mark the
+   frame as needing a buffer flip later.  There's no easy way to run
+   code after any drawing command, but code can be run whenever
+   someone asks for the handle necessary to draw.  */
+#define FRAME_ANDROID_DRAWABLE(f)                      \
+  (((f))->output_data.android->need_buffer_flip = true, \
+   FRAME_ANDROID_WINDOW ((f)))
+
 /* Return whether or not the frame F has been completely drawn.  Used
    while handling async input.  */
 #define FRAME_ANDROID_COMPLETE_P(f)            \
diff --git a/src/dired.c b/src/dired.c
index 084f3225cb..57d79b8446 100644
--- a/src/dired.c
+++ b/src/dired.c
@@ -44,6 +44,21 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "msdos.h"     /* for fstatat */
 #endif
 
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+typedef DIR emacs_dir;
+#define emacs_readdir readdir
+#define emacs_closedir closedir
+#else
+
+#include "android.h"
+
+/* The Android emulation of dirent stuff is required to be able to
+   list the /assets special directory.  */
+typedef struct android_dir emacs_dir;
+#define emacs_readdir android_readdir
+#define emacs_closedir android_closedir
+#endif
+
 #ifdef WINDOWSNT
 extern int is_slow_fs (const char *);
 #endif
@@ -78,19 +93,30 @@ dirent_type (struct dirent *dp)
 #endif
 }
 
-static DIR *
+static emacs_dir *
 open_directory (Lisp_Object dirname, Lisp_Object encoded_dirname, int *fdp)
 {
   char *name = SSDATA (encoded_dirname);
-  DIR *d;
+  emacs_dir *d;
   int fd, opendir_errno;
 
-#ifdef DOS_NT
-  /* Directories cannot be opened.  The emulation assumes that any
-     file descriptor other than AT_FDCWD corresponds to the most
-     recently opened directory.  This hack is good enough for Emacs.  */
+#if defined DOS_NT || (defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+  /* On DOS_NT, directories cannot be opened.  The emulation assumes
+     that any file descriptor other than AT_FDCWD corresponds to the
+     most recently opened directory.  This hack is good enough for
+     Emacs.
+
+     This code is also used on Android for a different reason: a
+     special `assets' directory outside the normal file system is used
+     to open assets inside the Android application package, and must
+     be listed using the opendir-like interface provided in
+     android.h.  */
   fd = 0;
+#ifndef HAVE_ANDROID
   d = opendir (name);
+#else
+  d = android_opendir (name);
+#endif
   opendir_errno = errno;
 #else
   fd = emacs_open (name, O_RDONLY | O_DIRECTORY, 0);
@@ -125,7 +151,7 @@ directory_files_internal_w32_unwind (Lisp_Object arg)
 static void
 directory_files_internal_unwind (void *d)
 {
-  closedir (d);
+  emacs_closedir (d);
 }
 
 /* Return the next directory entry from DIR; DIR's name is DIRNAME.
@@ -133,12 +159,12 @@ directory_files_internal_unwind (void *d)
    Signal any unrecoverable errors.  */
 
 static struct dirent *
-read_dirent (DIR *dir, Lisp_Object dirname)
+read_dirent (emacs_dir *dir, Lisp_Object dirname)
 {
   while (true)
     {
       errno = 0;
-      struct dirent *dp = readdir (dir);
+      struct dirent *dp = emacs_readdir (dir);
       if (dp || errno == 0)
        return dp;
       if (! (errno == EAGAIN || errno == EINTR))
@@ -190,7 +216,7 @@ directory_files_internal (Lisp_Object directory, 
Lisp_Object full,
   Lisp_Object encoded_dirfilename = ENCODE_FILE (dirfilename);
 
   int fd;
-  DIR *d = open_directory (dirfilename, encoded_dirfilename, &fd);
+  emacs_dir *d = open_directory (dirfilename, encoded_dirfilename, &fd);
 
   /* Unfortunately, we can now invoke expand-file-name and
      file-attributes on filenames, both of which can throw, so we must
@@ -300,7 +326,7 @@ directory_files_internal (Lisp_Object directory, 
Lisp_Object full,
       list = Fcons (attrs ? Fcons (finalname, fileattrs) : finalname, list);
     }
 
-  closedir (d);
+  emacs_closedir (d);
 #ifdef WINDOWSNT
   if (attrs)
     Vw32_get_true_file_attributes = w32_save;
@@ -514,7 +540,7 @@ file_name_completion (Lisp_Object file, Lisp_Object 
dirname, bool all_flag,
        }
     }
   int fd;
-  DIR *d = open_directory (dirname, encoded_dir, &fd);
+  emacs_dir *d = open_directory (dirname, encoded_dir, &fd);
   record_unwind_protect_ptr (directory_files_internal_unwind, d);
 
   /* Loop reading directory entries.  */
diff --git a/src/dispnew.c b/src/dispnew.c
index 12814bc0b7..f31e185a84 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -3175,6 +3175,7 @@ redraw_frame (struct frame *f)
      its redisplay done.  */
   mark_window_display_accurate (FRAME_ROOT_WINDOW (f), 0);
   set_window_update_flags (XWINDOW (FRAME_ROOT_WINDOW (f)), true);
+
   f->garbaged = false;
 }
 
@@ -6053,7 +6054,7 @@ FILE = nil means just close any termscript file currently 
open.  */)
   if (tty->termscript != 0)
     {
       block_input ();
-      fclose (tty->termscript);
+      emacs_fclose (tty->termscript);
       tty->termscript = 0;
       unblock_input ();
     }
diff --git a/src/fileio.c b/src/fileio.c
index 9d81f0ca54..6fa524b3bb 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -277,7 +277,7 @@ void
 fclose_unwind (void *arg)
 {
   FILE *stream = arg;
-  fclose (stream);
+  emacs_fclose (stream);
 }
 
 /* Restore point, having saved it as a marker.  */
@@ -2989,6 +2989,12 @@ If there is no error, returns nil.  */)
 
   encoded_filename = ENCODE_FILE (absname);
 
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+  /* FILE may be some kind of special Android file.  */
+  if (android_file_access_p (SSDATA (encoded_filename), R_OK))
+    return Qnil;
+#endif
+
   if (faccessat (AT_FDCWD, SSDATA (encoded_filename), R_OK, AT_EACCESS) != 0)
     report_file_error (SSDATA (string), filename);
 
@@ -3205,7 +3211,11 @@ file_accessible_directory_p (Lisp_Object file)
      There are three exceptions: "", "/", and "//".  Leave "" alone,
      as it's invalid.  Append only "." to the other two exceptions as
      "/" and "//" are distinct on some platforms, whereas "/", "///",
-     "////", etc. are all equivalent.  */
+     "////", etc. are all equivalent.
+
+     Android has a special directory named "/assets".  There is no "."
+     directory there, but appending a "/" is sufficient to check
+     whether or not it is a directory.  */
   if (! len)
     dir = data;
   else
@@ -3215,11 +3225,27 @@ file_accessible_directory_p (Lisp_Object file)
         special cases "/" and "//", and it's a safe optimization
         here.  After appending '.', append another '/' to work around
         a macOS bug (Bug#30350).  */
-      static char const appended[] = "/./";
-      char *buf = SAFE_ALLOCA (len + sizeof appended);
-      memcpy (buf, data, len);
-      strcpy (buf + len, &appended[data[len - 1] == '/']);
-      dir = buf;
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+      if (!strncmp ("/assets/", data,
+                   sizeof "/assets" - 1))
+       {
+         static char const appended[] = "/";
+         char *buf = SAFE_ALLOCA (len + sizeof appended);
+         memcpy (buf, data, len);
+         strcpy (buf + len, &appended[data[len - 1] == '/']);
+         dir = buf;
+       }
+      else
+       {
+#endif
+         static char const appended[] = "/./";
+         char *buf = SAFE_ALLOCA (len + sizeof appended);
+         memcpy (buf, data, len);
+         strcpy (buf + len, &appended[data[len - 1] == '/']);
+         dir = buf;
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+       }
+#endif
     }
 
   ok = file_access_p (dir, F_OK);
@@ -5973,7 +5999,7 @@ do_auto_save_unwind (void *arg)
   if (stream != NULL)
     {
       block_input ();
-      fclose (stream);
+      emacs_fclose (stream);
       unblock_input ();
     }
 }
diff --git a/src/keyboard.c b/src/keyboard.c
index daca964b98..78637ef4f1 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -4956,8 +4956,8 @@ const char *const lispy_function_keys[] =
     [66]  = "return",
     [67]  = "backspace",
     [82]  = "menu",
-    [92]  = "page-up",
-    [93]  = "page-down",
+    [92]  = "prior",
+    [93]  = "next",
   };
 
 #elif defined HAVE_NTGUI
@@ -11219,7 +11219,7 @@ This may include sensitive information such as 
passwords.  */)
   if (dribble)
     {
       block_input ();
-      fclose (dribble);
+      emacs_fclose (dribble);
       unblock_input ();
       dribble = 0;
     }
diff --git a/src/lisp.h b/src/lisp.h
index bf8ff5cdc1..f64a27ce11 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -28,6 +28,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <float.h>
 #include <inttypes.h>
 #include <limits.h>
+#include <stdio.h>
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -5077,6 +5078,7 @@ extern int emacs_open (const char *, int, int);
 extern int emacs_open_noquit (const char *, int, int);
 extern int emacs_pipe (int[2]);
 extern int emacs_close (int);
+extern int emacs_fclose (FILE *);
 extern ptrdiff_t emacs_read (int, void *, ptrdiff_t);
 extern ptrdiff_t emacs_read_quit (int, void *, ptrdiff_t);
 extern ptrdiff_t emacs_write (int, void const *, ptrdiff_t);
diff --git a/src/lread.c b/src/lread.c
index 2ac3c85ea8..d585dece39 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -1141,7 +1141,7 @@ close_infile_unwind (void *arg)
 {
   struct infile *prev_infile = arg;
   eassert (infile && infile != prev_infile);
-  fclose (infile->stream);
+  emacs_fclose (infile->stream);
   infile = prev_infile;
 }
 
diff --git a/src/sfnt.c b/src/sfnt.c
index 20cf2376d8..9b6c421212 100644
--- a/src/sfnt.c
+++ b/src/sfnt.c
@@ -3072,8 +3072,8 @@ sfnt_curve_is_flat (struct sfnt_point control0,
 
   /* 2.0 is a constant describing the area covered at which point the
      curve is considered "flat".  */
-  return (abs (sfnt_mul_fixed (g.x, h.x)
-              - sfnt_mul_fixed (g.y, h.y))
+  return (abs (sfnt_mul_fixed (g.x, h.y)
+              - sfnt_mul_fixed (g.y, h.x))
          <= 0400000);
 }
 
@@ -3261,9 +3261,11 @@ sfnt_prepare_raster (struct sfnt_raster *raster,
                     struct sfnt_glyph_outline *outline)
 {
   raster->width
-    = sfnt_ceil_fixed (outline->xmax - outline->xmin) >> 16;
+    = (sfnt_ceil_fixed (outline->xmax)
+       - sfnt_floor_fixed (outline->xmin)) >> 16;
   raster->height
-    = sfnt_ceil_fixed (outline->ymax - outline->ymin) >> 16;
+    = (sfnt_ceil_fixed (outline->ymax)
+       - sfnt_floor_fixed (outline->ymin)) >> 16;
   raster->refcount = 0;
 
   /* Align the raster to a SFNT_POLY_ALIGNMENT byte boundary.  */
@@ -3292,10 +3294,10 @@ sfnt_step_edge (struct sfnt_edge *edge)
 }
 
 /* Build a list of edges for each contour in OUTLINE, applying
-   OUTLINE->xmin and OUTLINE->ymin as the offset to each edge.  Call
-   EDGE_PROC with DCONTEXT and the resulting edges as arguments.  It
-   is OK to modify the edges given to EDGE_PROC.  Align all edges to
-   the sub-pixel grid.  */
+   OUTLINE->xmin and floor (OUTLINE->ymin) as the offset to each edge.
+   Call EDGE_PROC with DCONTEXT and the resulting edges as arguments.
+   It is OK to modify the edges given to EDGE_PROC.  Align all edges
+   to the sub-pixel grid.  */
 
 static void
 sfnt_build_outline_edges (struct sfnt_glyph_outline *outline,
@@ -3303,13 +3305,18 @@ sfnt_build_outline_edges (struct sfnt_glyph_outline 
*outline,
 {
   struct sfnt_edge *edges;
   size_t i, edge, next_vertex;
-  sfnt_fixed dx, dy, bot, step_x;
+  sfnt_fixed dx, dy, bot, step_x, ymin, xmin;
   int inc_x;
   size_t top, bottom, y;
 
   edges = alloca (outline->outline_used * sizeof *edges);
   edge = 0;
 
+  /* ymin and xmin must be the same as the offset used to set offy and
+     offx in rasters.  */
+  ymin = sfnt_floor_fixed (outline->ymin);
+  xmin = sfnt_floor_fixed (outline->xmin);
+
   for (i = 0; i < outline->outline_used; ++i)
     {
       /* Set NEXT_VERTEX to the next point (vertex) in this contour.
@@ -3356,12 +3363,12 @@ sfnt_build_outline_edges (struct sfnt_glyph_outline 
*outline,
          top = next_vertex;
        }
 
-      bot = (outline->outline[bottom].y - outline->ymin);
-      edges[edge].top = (outline->outline[top].y - outline->ymin);
+      bot = (outline->outline[bottom].y - ymin);
+      edges[edge].top = (outline->outline[top].y - ymin);
 
       /* Record the edge.  Rasterization happens from bottom to
         up, so record the X at the bottom.  */
-      edges[edge].x = (outline->outline[bottom].x - outline->xmin);
+      edges[edge].x = (outline->outline[bottom].x - xmin);
       dx = (outline->outline[top].x - outline->outline[bottom].x);
       dy = abs (outline->outline[top].y
                - outline->outline[bottom].y);
@@ -4585,7 +4592,7 @@ main (int argc, char **argv)
              /* Time this important bit.  */
              clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
              outline = sfnt_build_glyph_outline (glyph, head,
-                                                 45,
+                                                 12,
                                                  sfnt_test_get_glyph,
                                                  sfnt_test_free_glyph,
                                                  &dcontext);
@@ -4652,7 +4659,7 @@ main (int argc, char **argv)
 
              if (hmtx && head)
                {
-                 if (!sfnt_lookup_glyph_metrics (code, 36,
+                 if (!sfnt_lookup_glyph_metrics (code, 12,
                                                  &metrics,
                                                  hmtx, hhea,
                                                  head, maxp))
diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c
index 47aa27dc11..01bfdbaaf5 100644
--- a/src/sfntfont-android.c
+++ b/src/sfntfont-android.c
@@ -42,19 +42,6 @@ static Lisp_Object font_cache;
 
 
 
-static unsigned int
-sfntfont_android_saturate32 (unsigned int a, unsigned int b)
-{
-  unsigned int c;
-
-  c = a + b;
-
-  if (c < a)
-    c = -1;
-
-  return c;
-}
-
 /* Scale each of the four packed bytes in P in the low 16 bits of P by
    SCALE.  Return the result.
 
@@ -107,8 +94,9 @@ sfntfont_android_blend (unsigned int src, unsigned int dst)
   src = src & ~0x00ff00ff;
   src |= (src_rb >> 16 | src_rb << 16);
 
-  /* Saturating is unnecessary but helps find bugs.  */
-  return sfntfont_android_saturate32 (both, src);
+  /* This addition need not be saturating because both has already
+     been multiplied by 255 - a.  */
+  return both + src;
 }
 
 #define U255TO256(x) ((unsigned short) (x) + ((x) >> 7))
@@ -128,8 +116,9 @@ sfntfont_android_blendrgb (unsigned int src, unsigned int 
dst)
 
   both = ag_part | rb_part;
 
-  /* Saturating is unnecessary but helps find bugs.  */
-  return sfntfont_android_saturate32 (both, src);
+  /* This addition need not be saturating because both has already
+     been multiplied by 255 - a.  */
+  return both + src;
 }
 
 /* Composite the bitmap described by BUFFER, STRIDE and TEXT_RECTANGLE
@@ -162,6 +151,10 @@ sfntfont_android_composite_bitmap (unsigned char *restrict 
buffer,
 
          src_y = i + (rect->y - text_rectangle->y);
 
+         if (src_y > text_rectangle->height)
+           /* Huh? */
+           return;
+
          src_row = (unsigned int *) ((buffer + src_y * stride));
          dst_row = (unsigned int *) (dest + ((i + rect->y)
                                              * bitmap_info->stride));
@@ -343,7 +336,7 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int 
from,
     }
 
   /* Lock the bitmap.  It must be unlocked later.  */
-  bitmap_data = android_lock_bitmap (FRAME_ANDROID_WINDOW (s->f),
+  bitmap_data = android_lock_bitmap (FRAME_ANDROID_DRAWABLE (s->f),
                                     &bitmap_info, &bitmap);
 
   /* If locking the bitmap fails, just discard the data that was
@@ -385,7 +378,7 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int 
from,
   ANDROID_DELETE_LOCAL_REF (bitmap);
 
   /* Damage the window by the text rectangle.  */
-  android_damage_window (FRAME_ANDROID_WINDOW (s->f),
+  android_damage_window (FRAME_ANDROID_DRAWABLE (s->f),
                         &text_rectangle);
 
   /* Release the temporary scanline buffer.  */
@@ -495,13 +488,19 @@ init_sfntfont_android (void)
      version of Android the device is running.  */
   if (android_get_device_api_level () >= 15)
     Vsfnt_default_family_alist
-      = list2 (Fcons (build_string ("Monospace"),
+      = list3 (Fcons (build_string ("Monospace"),
+                     build_string ("Droid Sans Mono")),
+              /* Android doesn't come with a Monospace Serif font, so
+                 this will have to do.  */
+              Fcons (build_string ("Monospace Serif"),
                      build_string ("Droid Sans Mono")),
               Fcons (build_string ("Sans Serif"),
                      build_string ("Roboto")));
   else
     Vsfnt_default_family_alist
-      = list2 (Fcons (build_string ("Monospace"),
+      = list3 (Fcons (build_string ("Monospace"),
+                     build_string ("Droid Sans Mono")),
+              Fcons (build_string ("Monospace Serif"),
                      build_string ("Droid Sans Mono")),
               Fcons (build_string ("Sans Serif"),
                      build_string ("Droid Sans")));
diff --git a/src/sfntfont.c b/src/sfntfont.c
index 9206fbc662..25cea59f6a 100644
--- a/src/sfntfont.c
+++ b/src/sfntfont.c
@@ -1982,6 +1982,9 @@ sfntfont_text_extents (struct font *font, const unsigned 
int *code,
 
   total_width = 0;
 
+  /* First clear the metrics array.  */
+  memset (metrics, 0, sizeof *metrics);
+
   /* Get the metrcs one by one, then sum them up.  */
   for (i = 0; i < nglyphs; ++i)
     {
@@ -2059,7 +2062,7 @@ sfntfont_draw (struct glyph_string *s, int from, int to,
   struct sfnt_glyph_metrics metrics;
 
   length = to - from;
-  font = s->face->font;
+  font = s->font;
   info = (struct sfnt_font_info *) font;
 
   rasters = alloca (length * sizeof *rasters);
diff --git a/src/sysdep.c b/src/sysdep.c
index dd97ae1cb3..4d89d4f25a 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -2335,7 +2335,8 @@ emacs_backtrace (int backtrace_limit)
     }
 }
 
-#ifndef HAVE_NTGUI
+#if !defined HAVE_NTGUI && !(defined HAVE_ANDROID              \
+                            && !defined ANDROID_STUBIFY)
 void
 emacs_abort (void)
 {
@@ -2568,6 +2569,20 @@ emacs_close (int fd)
     }
 }
 
+/* Wrapper around fclose.  On Android, this calls `android_fclose' to
+   clear information associated with the FILE's file descriptor if
+   necessary.  */
+
+int
+emacs_fclose (FILE *stream)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+  return fclose (stream);
+#else
+  return android_fclose (stream);
+#endif
+}
+
 /* Maximum number of bytes to read or write in a single system call.
    This works around a serious bug in Linux kernels before 2.6.16; see
    <https://bugzilla.redhat.com/show_bug.cgi?format=multiple&id=612839>.
diff --git a/src/term.c b/src/term.c
index 1a21b6f576..05163cce53 100644
--- a/src/term.c
+++ b/src/term.c
@@ -2354,8 +2354,8 @@ A suspended tty may be resumed by calling `resume-tty' on 
it.  */)
 
 #ifndef MSDOS
       if (f != t->display_info.tty->output)
-        fclose (t->display_info.tty->output);
-      fclose (f);
+        emacs_fclose (t->display_info.tty->output);
+      emacs_fclose (f);
 #endif
 
       t->display_info.tty->input = 0;
@@ -4632,12 +4632,12 @@ delete_tty (struct terminal *terminal)
     {
       delete_keyboard_wait_descriptor (fileno (tty->input));
       if (tty->input != stdin)
-        fclose (tty->input);
+        emacs_fclose (tty->input);
     }
   if (tty->output && tty->output != stdout && tty->output != tty->input)
-    fclose (tty->output);
+    emacs_fclose (tty->output);
   if (tty->termscript)
-    fclose (tty->termscript);
+    emacs_fclose (tty->termscript);
 
   xfree (tty->old_tty);
   xfree (tty->Wcm);
diff --git a/src/verbose.mk.in b/src/verbose.mk.in
index 25dff99088..f826bf18a0 100644
--- a/src/verbose.mk.in
+++ b/src/verbose.mk.in
@@ -83,5 +83,5 @@ AM_V_RC      = @$(info $   RC       $@)
 
 # These are used for the Android port.
 AM_V_JAVAC     = @$(info $   JAVAC    $@)
-AM_V_DX                = @$(info $   DX       $@)
+AM_V_D8                = @$(info $   D8       $@)
 endif



reply via email to

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