]> git.eshelyaron.com Git - emacs.git/commitdiff
Fix build and running on Android 2.2
authorPo Lu <luangruo@yahoo.com>
Fri, 17 Feb 2023 08:27:00 +0000 (16:27 +0800)
committerPo Lu <luangruo@yahoo.com>
Fri, 17 Feb 2023 08:27:00 +0000 (16:27 +0800)
* INSTALL.android: Document that Android 2.2 is now supported,
with caveats.
* configure.ac (ANDROID_MIN_SDK, ANDROID_SDK_18_OR_EARLIER)
(SYSTEM_TYPE, ANDROID_STUBIFY, SIZEOF_LONG): Correctly detect
things missing on Android 2.2.
* java/Makefile.in (ANDROID_JAR, JARSIGNER_FLAGS):
* java/debug.sh (jdb, gdbserver, line):
* java/org/gnu/emacs/EmacsApplication.java (findDumpFile):
* java/org/gnu/emacs/EmacsService.java (onCreate):
* java/org/gnu/emacs/EmacsThread.java (EmacsThread, run): Run
parameter initialization on main thread.
* src/android-asset.h (struct android_asset_manager)
(struct android_asset, AAssetManager_fromJava, AAssetManager_open)
(AAsset_close, android_asset_create_stream)
(android_asset_read_internal, AAsset_openFileDescriptor)
(AAsset_getLength, AAsset_getBuffer, AAsset_read): New file.
* src/android.c (android_user_full_name, android_hack_asset_fd)
(android_check_compressed_file): Implement for Android 2.2.
* src/process.c (Fprocess_send_eof): Don't call tcdrain if
unavailable.
* src/sfntfont-android.c (system_font_directories): Fix compiler
warning.
* src/sfntfont.c (sfntfont_read_cmap): Correctly test rc of
emacs_open.
* src/textconv.c (handle_pending_conversion_events_1): Mark
buffer UNINIT.

13 files changed:
INSTALL.android
configure.ac
java/Makefile.in
java/debug.sh
java/org/gnu/emacs/EmacsApplication.java
java/org/gnu/emacs/EmacsService.java
java/org/gnu/emacs/EmacsThread.java
src/android-asset.h [new file with mode: 0644]
src/android.c
src/process.c
src/sfntfont-android.c
src/sfntfont.c
src/textconv.c

index a8d73331493aa5af7c45676dbb9b8ff7cae543e1..ab39a55eaf312c05f41b9c8db8c47c9fee0cb213 100644 (file)
@@ -91,9 +91,18 @@ for, and the include directories specify the paths to the relevant
 Android headers.  In addition, it may be necessary to specify
 "-gdwarf-2", due to a bug in the Android NDK.
 
-Emacs is known to build for Android 2.2 (API version 8) or later, and
-run on Android 2.3 or later.  It is supposed to run on Android 2.2 as
-well.
+Even older versions of the Android SDK do not require the extra
+`-isystem' directives.
+
+Emacs is known to run on Android 2.2 (API version 8) or later, with
+the NDK r10b or later.  We wanted to make Emacs work on even older
+versions of Android, but they are missing the required JNI graphics
+library that allows Emacs to display text from C code.
+
+Due to an extremely nasty bug in the Android 2.2 system, the generated
+Emacs package cannot be compressed in builds for Android 2.2.  As a
+result, the Emacs package will be approximately 100 megabytes larger
+than a compressed package for a newer version of Android.
 
 DEBUG AND RELEASE BUILDS
 
index b4fed263cf5f74ef6ac29e7f79d24504454252f2..a4a16ec806275af7153da2fc05c56af582c0d135 100644 (file)
@@ -1044,11 +1044,18 @@ package will likely install on older systems but crash on startup.])
   # Now tell java/Makefile if Emacs is being built for Android 4.3 or
   # earlier.
   ANDROID_SDK_18_OR_EARLIER=
-  if test "$android_sdk" -lt "18"; then
+  if test "$android_sdk" -le "18"; then
      ANDROID_SDK_18_OR_EARLIER=yes
   fi
   AC_SUBST([ANDROID_SDK_18_OR_EARLIER])
 
+  # Likewise for Android 2.2.
+  ANDROID_SDK_8_OR_EARLIER=
+  if test "$android_sdk" -le "8"; then
+     ANDROID_SDK_8_OR_EARLIER=yes
+  fi
+  AC_SUBST([ANDROID_SDK_8_OR_EARLIER])
+
   # Save confdefs.h and config.log for now.
   mv -f confdefs.h _confdefs.h
   mv -f config.log _config.log
@@ -2251,6 +2258,10 @@ AC_DEFINE_UNQUOTED([SYSTEM_TYPE], ["$SYSTEM_TYPE"],
   [The type of system you are compiling for; sets 'system-type'.])
 AC_SUBST([SYSTEM_TYPE])
 
+# Check for pw_gecos in struct passwd; this is known to be missing on
+# Android.
+
+AC_CHECK_MEMBERS([struct passwd.pw_gecos], [], [], [#include <pwd.h>])
 
 pre_PKG_CONFIG_CFLAGS=$CFLAGS
 pre_PKG_CONFIG_LIBS=$LIBS
@@ -2487,7 +2498,13 @@ for Android, but all API calls need to be stubbed out])
     ANDROID_CFLAGS="$ANDROID_CFLAGS -ftree-vectorize"
 
     # Link with libraries required for Android support.
-    ANDROID_LIBS="-landroid -llog -ljnigraphics"
+    # API 9 and later require `-landroid' for the asset manager.
+    # API 8 uses an emulation via the JNI.
+    if test "$ANDROID_SDK" -lt "9"; then
+      ANDROID_LIBS="-llog -ljnigraphics"
+    else
+      ANDROID_LIBS="-landroid -llog -ljnigraphics"
+    fi
 
     # This is required to make the system load emacs.apk's libpng
     # (among others) instead of the system's own.  But it doesn't work
@@ -5610,7 +5627,7 @@ OLD_LIBS=$LIBS
 LIBS="$LIB_PTHREAD $LIB_MATH $LIBS"
 AC_CHECK_FUNCS([accept4 fchdir gethostname \
 getrusage get_current_dir_name \
-lrand48 random rint trunc \
+lrand48 random rint tcdrain trunc \
 select getpagesize setlocale newlocale \
 getrlimit setrlimit shutdown \
 pthread_sigmask strsignal setitimer \
index fc0d23980e4b5a1f5c2f6ff86a1a69cf717e8653..92c03469c6946cb323682adc5dc685c7578609f5 100644 (file)
@@ -39,6 +39,7 @@ JARSIGNER_FLAGS =
 ANDROID_JAR = @ANDROID_JAR@
 ANDROID_ABI = @ANDROID_ABI@
 ANDROID_SDK_18_OR_EARLIER = @ANDROID_SDK_18_OR_EARLIER@
+ANDROID_SDK_8_OR_EARLIER = @ANDROID_SDK_8_OR_EARLIER@
 
 WARN_JAVAFLAGS = -Xlint:deprecation
 JAVAFLAGS = -classpath "$(ANDROID_JAR):." -target 1.7 -source 1.7 \
@@ -53,6 +54,16 @@ else
 JARSIGNER_FLAGS =
 endif
 
+# When building Emacs for Android 2.2, assets must not be compressed.
+# Otherwise, the asset manager fails to extract files larger than 1
+# MB.
+
+ifneq (,$(ANDROID_SDK_8_OR_EARLIER))
+AAPT_ASSET_ARGS = -0 ""
+else
+AAPT_ASSET_ARGS =
+endif
+
 SIGN_EMACS = -keystore emacs.keystore -storepass emacs1 $(JARSIGNER_FLAGS)
 SIGN_EMACS_V2 = sign --v2-signing-enabled --ks emacs.keystore  \
        --debuggable-apk-permitted --ks-pass pass:emacs1
@@ -192,7 +203,8 @@ emacs.apk-in: install_temp install_temp/assets/directory-tree \
 # of Android.  Make sure not to generate R.java, as it's already been
 # generated.
        $(AM_V_AAPT) $(AAPT) p -I "$(ANDROID_JAR)" -F $@   \
-         -f -M AndroidManifest.xml -A install_temp/assets \
+         -f -M AndroidManifest.xml $(AAPT_ASSET_ARGS)     \
+         -A install_temp/assets                           \
          -S res -J install_temp
        $(AM_V_SILENT) pushd install_temp &> /dev/null; \
          $(AAPT) add ../$@ `find lib -type f`;         \
index cbef75189846149a7f4ebb3ec2ac3063a814459d..30e5a94eee524e441d5dea1df24946e767237c29 100755 (executable)
@@ -32,6 +32,7 @@ jdb_port=64013
 jdb=no
 attach_existing=no
 gdbserver=
+gdb=gdb
 
 while [ $# -gt 0 ]; do
     case "$1" in
@@ -51,6 +52,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 "  --gdb               use specified GDB binary"
            echo "  --attach-existing   attach to an existing process"
            echo "  --gdbserver BINARY  upload and use the specified gdbserver binary"
            echo "  --help              print this message"
@@ -65,6 +67,10 @@ while [ $# -gt 0 ]; do
        "--jdb" )
            jdb=yes
            ;;
+       "--gdb" )
+           shift
+           gdb=$1
+           ;;
        "--gdbserver" )
            shift
            gdbserver=$1
@@ -355,4 +361,4 @@ fi
 
 # Finally, start gdb with any extra arguments needed.
 cd "$oldpwd"
-gdb --eval-command "target remote localhost:$gdb_port" $gdbargs
+$gdb --eval-command "target remote localhost:$gdb_port" $gdbargs
index 96328b99d1ca07813ec8025f9633df56881032ea..6a065165eb11b89fb005f2cf15a372cb3d9ea935 100644 (file)
@@ -49,6 +49,7 @@ public class EmacsApplication extends Application
        for a file named ``emacs-<fingerprint>.pdmp'' and delete the
        rest.  */
     filesDirectory = context.getFilesDir ();
+
     allFiles = filesDirectory.listFiles (new FileFilter () {
        @Override
        public boolean
index 2acb3ead086567b09ef71fc1ea17e28881a171df..4d373937ab0fa31160d2da2b18066b45d270a8b2 100644 (file)
@@ -180,11 +180,11 @@ public class EmacsService extends Service
   public void
   onCreate ()
   {
-    AssetManager manager;
+    final AssetManager manager;
     Context app_context;
-    String filesDir, libDir, cacheDir, classPath;
-    double pixelDensityX;
-    double pixelDensityY;
+    final String filesDir, libDir, cacheDir, classPath;
+    final double pixelDensityX;
+    final double pixelDensityY;
 
     SERVICE = this;
     handler = new Handler (Looper.getMainLooper ());
@@ -210,13 +210,18 @@ public class EmacsService extends Service
        Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir
               + ", libDir = " + libDir + ", and classPath = " + classPath);
 
-       EmacsNative.setEmacsParams (manager, filesDir, libDir,
-                                   cacheDir, (float) pixelDensityX,
-                                   (float) pixelDensityY,
-                                   classPath, this);
-
        /* Start the thread that runs Emacs.  */
-       thread = new EmacsThread (this, needDashQ);
+       thread = new EmacsThread (this, new Runnable () {
+           @Override
+           public void
+           run ()
+           {
+             EmacsNative.setEmacsParams (manager, filesDir, libDir,
+                                         cacheDir, (float) pixelDensityX,
+                                         (float) pixelDensityY,
+                                         classPath, EmacsService.this);
+           }
+         }, needDashQ);
        thread.start ();
       }
     catch (IOException exception)
index 2724d838d418cc329ebb74f44982f93d2c1c6bf1..30484710651449517dbead0a5273a22474f5e5ad 100644 (file)
@@ -28,11 +28,16 @@ public class EmacsThread extends Thread
   /* Whether or not Emacs should be started -Q.  */
   private boolean startDashQ;
 
+  /* Runnable run to initialize Emacs.  */
+  private Runnable paramsClosure;
+
   public
-  EmacsThread (EmacsService service, boolean startDashQ)
+  EmacsThread (EmacsService service, Runnable paramsClosure,
+              boolean startDashQ)
   {
     super ("Emacs main thread");
     this.startDashQ = startDashQ;
+    this.paramsClosure = paramsClosure;
   }
 
   @Override
@@ -46,6 +51,8 @@ public class EmacsThread extends Thread
     else
       args = new String[] { "libandroid-emacs.so", "-Q", };
 
+    paramsClosure.run ();
+
     /* Run the native code now.  */
     EmacsNative.initEmacs (args, EmacsApplication.dumpFileName,
                           Build.VERSION.SDK_INT);
diff --git a/src/android-asset.h b/src/android-asset.h
new file mode 100644 (file)
index 0000000..b0e83bb
--- /dev/null
@@ -0,0 +1,423 @@
+/* Android initialization for GNU Emacs.
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <android/log.h>
+
+/* This file contains an emulation of the Android asset manager API
+   used on builds for Android 2.2.  It is included by android.c
+   whenever appropriate.
+
+   The replacements in this file are not thread safe and must only be
+   called from the creating thread.  */
+
+struct android_asset_manager
+{
+  /* JNI environment.  */
+  JNIEnv *env;
+
+  /* Asset manager class and functions.  */
+  jclass class;
+  jmethodID open_fd;
+
+  /* Asset file descriptor class and functions.  */
+  jclass fd_class;
+  jmethodID get_length;
+  jmethodID create_input_stream;
+  jmethodID close;
+
+  /* Input stream class and functions.  */
+  jclass input_stream_class;
+  jmethodID read;
+  jmethodID stream_close;
+
+  /* Associated asset manager object.  */
+  jobject asset_manager;
+};
+
+typedef struct android_asset_manager AAssetManager;
+
+struct android_asset
+{
+  /* The asset manager.  */
+  AAssetManager *manager;
+
+  /* The length of the asset, or -1.  */
+  jlong length;
+
+  /* The asset file descriptor and input stream.  */
+  jobject fd, stream;
+
+  /* The mode.  */
+  int mode;
+};
+
+typedef struct android_asset AAsset;
+
+static AAssetManager *
+AAssetManager_fromJava (JNIEnv *env, jobject java_manager)
+{
+  AAssetManager *manager;
+  jclass temp;
+
+  manager = malloc (sizeof *manager);
+
+  if (!manager)
+    return NULL;
+
+  manager->env = env;
+  manager->asset_manager
+    = (*env)->NewGlobalRef (env, java_manager);
+
+  if (!manager->asset_manager)
+    {
+      free (manager);
+      return NULL;
+    }
+
+  manager->class
+    = (*env)->FindClass (env, "android/content/res/AssetManager");
+  assert (manager->class);
+
+  manager->open_fd
+    = (*env)->GetMethodID (env, manager->class, "openFd",
+                          "(Ljava/lang/String;)"
+                          "Landroid/content/res/AssetFileDescriptor;");
+  assert (manager->open);
+
+  manager->fd_class
+    = (*env)->FindClass (env, "android/content/res/AssetFileDescriptor");
+  assert (manager->fd_class);
+
+  manager->get_length
+    = (*env)->GetMethodID (env, manager->fd_class, "getLength",
+                          "()J");
+  assert (manager->get_length);
+
+  manager->create_input_stream
+    = (*env)->GetMethodID (env, manager->fd_class,
+                          "createInputStream",
+                          "()Ljava/io/FileInputStream;");
+  assert (manager->create_input_stream);
+
+  manager->close
+    = (*env)->GetMethodID (env, manager->fd_class,
+                          "close", "()V");
+  assert (manager->close);
+
+  manager->input_stream_class
+    = (*env)->FindClass (env, "java/io/InputStream");
+  assert (manager->input_stream_class);
+
+  manager->read
+    = (*env)->GetMethodID (env, manager->input_stream_class,
+                          "read", "([B)I");
+  assert (manager->read);
+
+  manager->stream_close
+    = (*env)->GetMethodID (env, manager->input_stream_class,
+                          "close", "()V");
+  assert (manager->stream_close);
+
+  /* Now convert all the class references to global ones.  */
+  temp = manager->class;
+  manager->class
+    = (*env)->NewGlobalRef (env, temp);
+  assert (manager->class);
+  (*env)->DeleteLocalRef (env, temp);
+  temp = manager->fd_class;
+  manager->fd_class
+    = (*env)->NewGlobalRef (env, temp);
+  assert (manager->fd_class);
+  (*env)->DeleteLocalRef (env, temp);
+  temp = manager->input_stream_class;
+  manager->input_stream_class
+    = (*env)->NewGlobalRef (env, temp);
+  assert (manager->input_stream_class);
+  (*env)->DeleteLocalRef (env, temp);
+
+  /* Return the asset manager.  */
+  return manager;
+}
+
+enum
+  {
+    AASSET_MODE_STREAMING = 0,
+    AASSET_MODE_BUFFER   = 1,
+  };
+
+static AAsset *
+AAssetManager_open (AAssetManager *manager, const char *c_name,
+                   int mode)
+{
+  jobject desc;
+  jstring name;
+  AAsset *asset;
+
+  /* Push a local frame.  */
+  asset = NULL;
+
+  (*(manager->env))->PushLocalFrame (manager->env, 3);
+
+  if ((*(manager->env))->ExceptionCheck (manager->env))
+    goto fail;
+
+  /* Encoding issues can be ignored for now as there are only ASCII
+     file names in Emacs.  */
+  name = (*(manager->env))->NewStringUTF (manager->env, c_name);
+
+  if (!name)
+    goto fail;
+
+  /* Now try to open an ``AssetFileDescriptor''.  */
+  desc = (*(manager->env))->CallObjectMethod (manager->env,
+                                             manager->asset_manager,
+                                             manager->open_fd,
+                                             name);
+
+  if (!desc)
+    goto fail;
+
+  /* Allocate the asset.  */
+  asset = calloc (1, sizeof *asset);
+
+  if (!asset)
+    {
+      (*(manager->env))->CallVoidMethod (manager->env,
+                                        desc,
+                                        manager->close);
+      goto fail;
+    }
+
+  /* Pop the local frame and return desc.  */
+  desc = (*(manager->env))->NewGlobalRef (manager->env, desc);
+
+  if (!desc)
+    goto fail;
+
+  (*(manager->env))->PopLocalFrame (manager->env, NULL);
+
+  asset->manager = manager;
+  asset->length = -1;
+  asset->fd = desc;
+  asset->mode = mode;
+
+  return asset;
+
+ fail:
+  (*(manager->env))->ExceptionClear (manager->env);
+  (*(manager->env))->PopLocalFrame (manager->env, NULL);
+  free (asset);
+
+  return NULL;
+}
+
+static AAsset *
+AAsset_close (AAsset *asset)
+{
+  JNIEnv *env;
+
+  env = asset->manager->env;
+
+  (*env)->CallVoidMethod (asset->manager->env,
+                         asset->fd,
+                         asset->manager->close);
+  (*env)->DeleteGlobalRef (asset->manager->env,
+                          asset->fd);
+
+  if (asset->stream)
+    {
+      (*env)->CallVoidMethod (asset->manager->env,
+                             asset->stream,
+                             asset->manager->stream_close);
+      (*env)->DeleteGlobalRef (asset->manager->env,
+                              asset->stream);
+    }
+
+  free (asset);
+}
+
+/* Create an input stream associated with the given ASSET.  Set
+   ASSET->stream to its global reference.
+
+   Value is 1 upon failure, else 0.  ASSET must not already have an
+   input stream.  */
+
+static int
+android_asset_create_stream (AAsset *asset)
+{
+  jobject stream;
+  JNIEnv *env;
+
+  env = asset->manager->env;
+  stream
+    = (*env)->CallObjectMethod (env, asset->fd,
+                               asset->manager->create_input_stream);
+
+  if (!stream)
+    {
+      (*env)->ExceptionClear (env);
+      return 1;
+    }
+
+  asset->stream
+    = (*env)->NewGlobalRef (env, stream);
+
+  if (!asset->stream)
+    {
+      (*env)->ExceptionClear (env);
+      (*env)->DeleteLocalRef (env, stream);
+      return 1;
+    }
+
+  (*env)->DeleteLocalRef (env, stream);
+  return 0;
+}
+
+/* Read NBYTES from the specified asset into the given BUFFER;
+
+   Internally, allocate a Java byte array containing 4096 elements and
+   copy the data to and from that array.
+
+   Value is the number of bytes actually read, 0 at EOF, or -1 upon
+   failure, in which case errno is set accordingly.  If NBYTES is
+   zero, behavior is undefined.  */
+
+static int
+android_asset_read_internal (AAsset *asset, int nbytes, char *buffer)
+{
+  jbyteArray stash;
+  JNIEnv *env;
+  jint bytes_read, total;
+
+  /* Allocate a suitable amount of storage.  Either nbytes or 4096,
+     whichever is larger.  */
+  env = asset->manager->env;
+  stash = (*env)->NewByteArray (env, MIN (nbytes, 4096));
+
+  if (!stash)
+    {
+      (*env)->ExceptionClear (env);
+      errno = ENOMEM;
+      return -1;
+    }
+
+  /* Try to create an input stream.  */
+
+  if (!asset->stream
+      && android_asset_create_stream (asset))
+    {
+      (*env)->DeleteLocalRef (env, stash);
+      errno = ENOMEM;
+      return -1;
+    }
+
+  /* Start reading.  */
+
+  total = 0;
+
+  while (nbytes)
+    {
+      bytes_read = (*env)->CallIntMethod (env, asset->stream,
+                                         asset->manager->read,
+                                         stash);
+
+      /* Detect error conditions.  */
+
+      if ((*env)->ExceptionCheck (env))
+       goto out;
+
+      /* Detect EOF.  */
+
+      if (bytes_read == -1)
+       goto out;
+
+      /* Finally write out the amount that was read.  */
+      bytes_read = MIN (bytes_read, nbytes);
+      (*env)->GetByteArrayRegion (env, stash, 0, bytes_read, buffer);
+
+      buffer += bytes_read;
+      total += bytes_read;
+      nbytes -= bytes_read;
+    }
+
+  /* Make sure the value of nbytes still makes sense.  */
+  assert (nbytes >= 0);
+
+ out:
+  (*env)->ExceptionClear (env);
+  (*env)->DeleteLocalRef (env, stash);
+  return total;
+}
+
+static int
+AAsset_openFileDescriptor (AAsset *asset, off_t *out_start,
+                          off_t *out_end)
+{
+  *out_start = 0;
+  *out_end = 0;
+  return -1;
+}
+
+static long
+AAsset_getLength (AAsset *asset)
+{
+  JNIEnv *env;
+
+  if (asset->length != -1)
+    return asset->length;
+
+  env = asset->manager->env;
+  asset->length
+    = (*env)->CallLongMethod (env, asset->fd,
+                             asset->manager->get_length);
+  return asset->length;
+}
+
+static char *
+AAsset_getBuffer (AAsset *asset)
+{
+  long length;
+  char *buffer;
+
+  length = AAsset_getLength (asset);
+
+  if (!length)
+    return NULL;
+
+  buffer = malloc (length);
+
+  if (!buffer)
+    return NULL;
+
+  if (android_asset_read_internal (asset, length, buffer)
+      != length)
+    {
+      xfree (buffer);
+      return NULL;
+    }
+
+  return buffer;
+}
+
+static size_t
+AAsset_read (AAsset *asset, void *buffer, size_t size)
+{
+  return android_asset_read_internal (asset, MIN (size, INT_MAX),
+                                     buffer);
+}
index 8f446224dabedbfadbf21efd64da994d0db95504..09b9001b45da087637db7e8e0246f493fd7b43bc 100644 (file)
@@ -32,6 +32,9 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 #include <sys/mman.h>
 #include <sys/param.h>
 
+/* Old NDK versions lack MIN and MAX.  */
+#include <minmax.h>
+
 #include <assert.h>
 #include <fingerprint.h>
 
@@ -49,8 +52,13 @@ bool android_init_gui;
 
 #ifndef ANDROID_STUBIFY
 
+#if __ANDROID_API__ >= 9
 #include <android/asset_manager.h>
 #include <android/asset_manager_jni.h>
+#else
+#include "android-asset.h"
+#endif
+
 #include <android/bitmap.h>
 #include <android/log.h>
 
@@ -907,10 +915,14 @@ android_is_directory (const char *dir)
 char *
 android_user_full_name (struct passwd *pw)
 {
+#ifdef HAVE_STRUCT_PASSWD_PW_GECOS
   if (!pw->pw_gecos)
     return (char *) "Android user";
 
   return pw->pw_gecos;
+#else
+  return "Android user";
+#endif
 }
 
 /* Given a real file name, return the part that describes its asset
@@ -1048,6 +1060,60 @@ android_file_access_p (const char *name, int amode)
 static int
 android_hack_asset_fd (AAsset *asset)
 {
+#if __ANDROID_API__ < 9
+  int fd;
+  char filename[PATH_MAX];
+  size_t size;
+  void *mem;
+
+  /* Assets must be small enough to fit in size_t, if off_t is
+     larger.  */
+  size = AAsset_getLength (asset);
+
+  /* Get an unlinked file descriptor from a file in the cache
+     directory, which is guaranteed to only be written to by Emacs.
+     Creating an asset file descriptor doesn't work on these old
+     Android versions.  */
+
+  snprintf (filename, PATH_MAX, "%s/%s.%d",
+           android_cache_dir, "temp-unlinked",
+           getpid ());
+  fd = open (filename, O_CREAT | O_RDWR | O_TRUNC,
+            S_IRUSR | S_IWUSR);
+
+  if (fd < 1)
+    return -1;
+
+  if (unlink (filename))
+    goto fail;
+
+  if (ftruncate (fd, size))
+    goto fail;
+
+  mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0);
+  if (mem == MAP_FAILED)
+    {
+      __android_log_print (ANDROID_LOG_ERROR, __func__,
+                          "mmap: %s", strerror (errno));
+      goto fail;
+    }
+
+  if (AAsset_read (asset, mem, size) != size)
+    {
+      /* Too little was read.  Close the file descriptor and
+        report an error.  */
+      __android_log_print (ANDROID_LOG_ERROR, __func__,
+                          "AAsset_read: %s", strerror (errno));
+      goto fail;
+    }
+
+  munmap (mem, size);
+  return fd;
+
+ fail:
+  close (fd);
+  return -1;
+#else
   int fd, rc;
   unsigned char *mem;
   size_t size;
@@ -1172,10 +1238,11 @@ android_hack_asset_fd (AAsset *asset)
   /* Return anyway even if munmap fails.  */
   munmap (mem, size);
   return fd;
+#endif
 }
 
 /* Read two bytes from FD and see if they are ``PK'', denoting ZIP
-   archive compressed data.
+   archive compressed data.  If FD is -1, return -1.
 
    If they are not, rewind the file descriptor to offset 0.
 
@@ -1187,6 +1254,9 @@ android_check_compressed_file (int fd)
 {
   char bytes[2];
 
+  if (fd == -1)
+    return -1;
+
   if (read (fd, bytes, 2) != 2)
     goto lseek_back;
 
index e7ccb2c604edd7fbeac5f952b991dc08f6b9c821..bdaaba70fea816cf919b66733cca34386164ffd5 100644 (file)
@@ -7248,7 +7248,7 @@ process has been transmitted to the serial port.  */)
     send_process (proc, "\004", 1, Qnil);
   else if (EQ (XPROCESS (proc)->type, Qserial))
     {
-#ifndef WINDOWSNT
+#if !defined WINDOWSNT && defined HAVE_TCDRAIN
       if (tcdrain (XPROCESS (proc)->outfd) != 0)
        report_file_error ("Failed tcdrain", Qnil);
 #endif /* not WINDOWSNT */
index c28a911bfba793d22913bf4296af9f5af4b9240c..8324185cc6fb05eccd49a008263effe5d8ebd2c5 100644 (file)
@@ -51,7 +51,7 @@ struct sfntfont_android_scanline_buffer
 /* Array of directories to search for system fonts.  */
 static char *system_font_directories[] =
   {
-    "/system/fonts",
+    (char *) "/system/fonts",
     /* This should be filled in by init_sfntfont_android.  */
     (char[PATH_MAX]) { },
   };
index f9344067f1a34c9e6c427a96fe7b4bfd3fb454bf..20c109f2401897656aadeb0274be5c48cf18c579 100644 (file)
@@ -953,7 +953,7 @@ sfntfont_read_cmap (struct sfnt_font_desc *desc,
   /* Pick a character map and place it in *CMAP.  */
   fd = emacs_open (desc->path, O_RDONLY, 0);
 
-  if (fd < 1)
+  if (fd < 0)
     return;
 
   font = sfnt_read_table_directory (fd);
index 5090b0a33b6633ea86ba1e542e92eac96fd9a249..8f5a4987d5a5171fefc80a562ac1deeab1eefc50 100644 (file)
@@ -989,7 +989,7 @@ handle_pending_conversion_events_1 (struct frame *f,
 {
   Lisp_Object data;
   enum text_conversion_operation operation;
-  struct buffer *buffer;
+  struct buffer *buffer UNINIT;
   struct window *w;
   specpdl_ref count;
   unsigned long token;