From 0b1ef9ea31ce039001546b3ed34494332e5e3629 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Wed, 25 Jan 2023 22:07:51 +0800 Subject: [PATCH] Update Android port * java/org/gnu/emacs/EmacsDrawLine.java: Fix this again. Gosh, how does Android do this. * java/org/gnu/emacs/EmacsNoninteractive.java (main): Port to Android 2.3.3. * java/org/gnu/emacs/EmacsSdk11Clipboard.java (EmacsSdk11Clipboard): Port to Android 4.0.3. * java/org/gnu/emacs/EmacsService.java (getClipboardManager): New function. * src/alloc.c (find_string_data_in_pure): Fix Android alignment issue. * src/android-emacs.c (main): Port to Android 4.4. * src/android.c (initEmacs): Align stack to 32 bytes, so it ends up aligned to 16 even though gcc thinks the stack is already aligned to 16 bytes. * src/callproc.c (init_callproc): Use /system/bin/sh instead of /bin/sh by default. --- java/org/gnu/emacs/EmacsDrawLine.java | 2 +- java/org/gnu/emacs/EmacsNoninteractive.java | 30 +++++-- java/org/gnu/emacs/EmacsSdk11Clipboard.java | 8 +- java/org/gnu/emacs/EmacsService.java | 46 ++++++++++ src/alloc.c | 16 ++++ src/android-emacs.c | 94 +++++++++++++++++---- src/android.c | 14 +++ src/callproc.c | 5 ++ 8 files changed, 186 insertions(+), 29 deletions(-) diff --git a/java/org/gnu/emacs/EmacsDrawLine.java b/java/org/gnu/emacs/EmacsDrawLine.java index 717e2279a7d..c6e5123bfca 100644 --- a/java/org/gnu/emacs/EmacsDrawLine.java +++ b/java/org/gnu/emacs/EmacsDrawLine.java @@ -60,7 +60,7 @@ public class EmacsDrawLine coordinates appropriately. */ if (gc.clip_mask == null) - canvas.drawLine ((float) x + 0.5f, (float) y + 0.5f, + canvas.drawLine ((float) x, (float) y + 0.5f, (float) x2 + 0.5f, (float) y2 + 0.5f, paint); diff --git a/java/org/gnu/emacs/EmacsNoninteractive.java b/java/org/gnu/emacs/EmacsNoninteractive.java index a3aefee5e0b..b4854d8323f 100644 --- a/java/org/gnu/emacs/EmacsNoninteractive.java +++ b/java/org/gnu/emacs/EmacsNoninteractive.java @@ -95,7 +95,7 @@ public class EmacsNoninteractive On Android 2.3.3 and earlier, there is no ``compatibilityInfo'' argument to getPackageInfo. */ - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD) + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) { method = activityThreadClass.getMethod ("getPackageInfo", @@ -123,11 +123,29 @@ public class EmacsNoninteractive /* Now, get a context. */ contextImplClass = Class.forName ("android.app.ContextImpl"); - method = contextImplClass.getDeclaredMethod ("createAppContext", - activityThreadClass, - loadedApkClass); - method.setAccessible (true); - context = (Context) method.invoke (null, activityThread, loadedApk); + + try + { + method = contextImplClass.getDeclaredMethod ("createAppContext", + activityThreadClass, + loadedApkClass); + method.setAccessible (true); + context = (Context) method.invoke (null, activityThread, loadedApk); + } + catch (NoSuchMethodException exception) + { + /* Older Android versions don't have createAppContext, but + instead require creating a ContextImpl, and then + calling createPackageContext. */ + method = activityThreadClass.getDeclaredMethod ("getSystemContext"); + context = (Context) method.invoke (activityThread); + method = contextImplClass.getDeclaredMethod ("createPackageContext", + String.class, + int.class); + method.setAccessible (true); + context = (Context) method.invoke (context, "org.gnu.emacs", + 0); + } /* Don't actually start the looper or anything. Instead, obtain an AssetManager. */ diff --git a/java/org/gnu/emacs/EmacsSdk11Clipboard.java b/java/org/gnu/emacs/EmacsSdk11Clipboard.java index 0a725200723..2df2015c9c1 100644 --- a/java/org/gnu/emacs/EmacsSdk11Clipboard.java +++ b/java/org/gnu/emacs/EmacsSdk11Clipboard.java @@ -42,13 +42,7 @@ public class EmacsSdk11Clipboard extends EmacsClipboard public EmacsSdk11Clipboard () { - String what; - Context context; - - what = Context.CLIPBOARD_SERVICE; - context = EmacsService.SERVICE; - manager - = (ClipboardManager) context.getSystemService (what); + manager = EmacsService.SERVICE.getClipboardManager (); manager.addPrimaryClipChangedListener (this); } diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 91db76b08e3..eb9b61dd876 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -39,6 +39,7 @@ import android.app.NotificationChannel; import android.app.PendingIntent; import android.app.Service; +import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -565,4 +566,49 @@ public class EmacsService extends Service return null; } + + /* Get a SDK 11 ClipboardManager. + + Android 4.0.x requires that this be called from the main + thread. */ + + public ClipboardManager + getClipboardManager () + { + final Holder manager; + Runnable runnable; + + manager = new Holder (); + + runnable = new Runnable () { + public void + run () + { + Object tem; + + synchronized (this) + { + tem = getSystemService (Context.CLIPBOARD_SERVICE); + manager.thing = (ClipboardManager) tem; + notify (); + } + } + }; + + synchronized (runnable) + { + runOnUiThread (runnable); + + try + { + runnable.wait (); + } + catch (InterruptedException e) + { + EmacsNative.emacsAbort (); + } + } + + return manager.thing; + } }; diff --git a/src/alloc.c b/src/alloc.c index ed55ae32710..bc43f22005d 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -5662,6 +5662,22 @@ find_string_data_in_pure (const char *data, ptrdiff_t nbytes) if (pure_bytes_used_non_lisp <= nbytes) return NULL; + /* The Android GCC generates code like: + + 0xa539e755 <+52>: lea 0x430(%esp),%esi +=> 0xa539e75c <+59>: movdqa %xmm0,0x0(%ebp) + 0xa539e761 <+64>: add $0x10,%ebp + + but data is not aligned appropriately, so a GP fault results. */ + +#if defined __i386__ \ + && defined HAVE_ANDROID \ + && !defined ANDROID_STUBIFY \ + && !defined (__clang__) + if ((intptr_t) data & 15) + return NULL; +#endif + /* Set up the Boyer-Moore table. */ skip = nbytes + 1; for (i = 0; i < 256; i++) diff --git a/src/android-emacs.c b/src/android-emacs.c index c1f2a6f43bb..e64caf9a9d4 100644 --- a/src/android-emacs.c +++ b/src/android-emacs.c @@ -52,12 +52,37 @@ main (int argc, char **argv) args[0] = (char *) "/system/bin/app_process"; #endif + /* Machines with ART require the boot classpath to be manually + specified. Machines with Dalvik however refuse to do so, as they + open the jars inside the BOOTCLASSPATH environment variable at + startup, resulting in the following crash: + + W/dalvikvm( 1608): Refusing to reopen boot DEX + '/system/framework/core.jar' + W/dalvikvm( 1608): Refusing to reopen boot DEX + '/system/framework/bouncycastle.jar' + E/dalvikvm( 1608): Too many exceptions during init (failed on + 'Ljava/io/IOException;' 'Re-opening BOOTCLASSPATH DEX files is + not allowed') + E/dalvikvm( 1608): VM aborting */ + +#if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL + if (android_get_device_api_level () < 21) + { + bootclasspath = NULL; + goto skip_setup; + } +#else + if (__ANDROID_API__ < 21) + { + bootclasspath = NULL; + goto skip_setup; + } +#endif + /* Next, obtain the boot class path. */ bootclasspath = getenv ("BOOTCLASSPATH"); - /* And the Emacs class path. */ - emacs_class_path = getenv ("EMACS_CLASS_PATH"); - if (!bootclasspath) { fprintf (stderr, "The BOOTCLASSPATH environment variable" @@ -68,6 +93,11 @@ main (int argc, char **argv) return 1; } + skip_setup: + + /* And the Emacs class path. */ + emacs_class_path = getenv ("EMACS_CLASS_PATH"); + if (!emacs_class_path) { fprintf (stderr, "EMACS_CLASS_PATH not set." @@ -76,25 +106,59 @@ main (int argc, char **argv) return 1; } - if (asprintf (&bootclasspath, "-Djava.class.path=%s:%s", - bootclasspath, emacs_class_path) < 0) + if (bootclasspath) { - perror ("asprintf"); - return 1; + if (asprintf (&bootclasspath, "-Djava.class.path=%s:%s", + bootclasspath, emacs_class_path) < 0) + { + perror ("asprintf"); + return 1; + } + } + else + { + if (asprintf (&bootclasspath, "-Djava.class.path=%s", + emacs_class_path) < 0) + { + perror ("asprintf"); + return 1; + } } args[1] = bootclasspath; args[2] = (char *) "/system/bin"; - args[3] = (char *) "--nice-name=emacs"; - args[4] = (char *) "org.gnu.emacs.EmacsNoninteractive"; - /* Arguments from here on are passed to main in - EmacsNoninteractive.java. */ - args[5] = argv[0]; +#if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL + /* I don't know exactly when --nice-name was introduced; this is + just a guess. */ + if (android_get_device_api_level () >= 26) + { + args[3] = (char *) "--nice-name=emacs"; + args[4] = (char *) "org.gnu.emacs.EmacsNoninteractive"; + + /* Arguments from here on are passed to main in + EmacsNoninteractive.java. */ + args[5] = argv[0]; - /* Now copy the rest of the arguments over. */ - for (i = 1; i < argc; ++i) - args[5 + i] = argv[i]; + /* Now copy the rest of the arguments over. */ + for (i = 1; i < argc; ++i) + args[5 + i] = argv[i]; + } + else + { +#endif + args[3] = (char *) "org.gnu.emacs.EmacsNoninteractive"; + + /* Arguments from here on are passed to main in + EmacsNoninteractive.java. */ + args[4] = argv[0]; + + /* Now copy the rest of the arguments over. */ + for (i = 1; i < argc; ++i) + args[4 + i] = argv[i]; +#if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL + } +#endif /* Finally, try to start the app_process. */ execvp (args[0], args); diff --git a/src/android.c b/src/android.c index 8c4442e3397..2f21a03b53f 100644 --- a/src/android.c +++ b/src/android.c @@ -205,6 +205,9 @@ static struct android_emacs_window window_class; stored in unsigned long to be consistent with X. */ static unsigned int event_serial; +/* Unused pointer used to control compiler optimizations. */ +void *unused_pointer; + /* Event handling functions. Events are stored on a (circular) queue @@ -1718,6 +1721,17 @@ NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv, const char *c_argument; char *dump_file; + /* android_emacs_init is not main, so GCC is not nice enough to add + the stack alignment prologue. + + Unfortunately for us, dalvik on Android 4.0.x calls native code + with a 4 byte aligned stack. */ + + __attribute__ ((aligned (32))) char buffer[32]; + + /* Trick GCC into not optimizing this variable away. */ + unused_pointer = buffer; + android_java_env = env; nelements = (*env)->GetArrayLength (env, argv); diff --git a/src/callproc.c b/src/callproc.c index 85895a7d9f2..e15eebe23dd 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -1987,7 +1987,12 @@ init_callproc (void) dir_warning ("arch-independent data dir", Vdata_directory); sh = getenv ("SHELL"); +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + /* The Android shell is found under /system/bin, not /bin. */ + Vshell_file_name = build_string (sh ? sh : "/system/bin/sh"); +#else Vshell_file_name = build_string (sh ? sh : "/bin/sh"); +#endif Lisp_Object gamedir = Qnil; if (PATH_GAME) -- 2.39.5