From df29bb71fc47b5e24fa971b668e4fa09dd6d244d Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 25 Feb 2023 20:11:48 +0800 Subject: [PATCH] Update Android port * java/org/gnu/emacs/EmacsNoninteractive.java (main): Port to Android 2.2. * src/android-asset.h (AAsset_openFileDescriptor): Delete stub function. * src/android.c (android_check_compressed_file): Delete function. (android_open): Stop trying to find compressed files or to use the system provided file descriptor. Explain why. --- java/org/gnu/emacs/EmacsNoninteractive.java | 118 +++++++++++++------- src/android-asset.h | 9 -- src/android.c | 65 ++--------- 3 files changed, 87 insertions(+), 105 deletions(-) diff --git a/java/org/gnu/emacs/EmacsNoninteractive.java b/java/org/gnu/emacs/EmacsNoninteractive.java index 4da82f2f894..30901edb75f 100644 --- a/java/org/gnu/emacs/EmacsNoninteractive.java +++ b/java/org/gnu/emacs/EmacsNoninteractive.java @@ -87,56 +87,23 @@ public class EmacsNoninteractive /* Create and attach the activity thread. */ activityThread = method.invoke (null); + context = null; /* Now get an LoadedApk. */ - loadedApkClass = Class.forName ("android.app.LoadedApk"); - /* Get a LoadedApk. How to do this varies by Android version. - On Android 2.3.3 and earlier, there is no - ``compatibilityInfo'' argument to getPackageInfo. */ - - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) + try { - method - = activityThreadClass.getMethod ("getPackageInfo", - String.class, - int.class); - loadedApk = method.invoke (activityThread, "org.gnu.emacs", - 0); + loadedApkClass = Class.forName ("android.app.LoadedApk"); } - else + catch (ClassNotFoundException exception) { - compatibilityInfoClass - = Class.forName ("android.content.res.CompatibilityInfo"); - - method - = activityThreadClass.getMethod ("getPackageInfo", - String.class, - compatibilityInfoClass, - int.class); - loadedApk = method.invoke (activityThread, "org.gnu.emacs", null, - 0); - } - - if (loadedApk == null) - throw new RuntimeException ("getPackageInfo returned NULL"); + /* Android 2.2 has no LoadedApk class, but fortunately it + does not need to be used, since contexts can be + directly created. */ - /* Now, get a context. */ - contextImplClass = Class.forName ("android.app.ContextImpl"); + loadedApkClass = null; + contextImplClass = Class.forName ("android.app.ContextImpl"); - try - { - method = contextImplClass.getDeclaredMethod ("createAppContext", - activityThreadClass, - loadedApkClass); - method.setAccessible (true); - context = (Context) method.invoke (null, activityThread, loadedApk); - } - catch (NoSuchMethodException exception) - { - /* Older Android versions don't have createAppContext, but - instead require creating a ContextImpl, and then - calling createPackageContext. */ method = activityThreadClass.getDeclaredMethod ("getSystemContext"); context = (Context) method.invoke (activityThread); method = contextImplClass.getDeclaredMethod ("createPackageContext", @@ -147,6 +114,73 @@ public class EmacsNoninteractive 0); } + /* If the context has not already been created, then do what + is appropriate for newer versions of Android. */ + + if (context == null) + { + /* Get a LoadedApk. How to do this varies by Android version. + On Android 2.3.3 and earlier, there is no + ``compatibilityInfo'' argument to getPackageInfo. */ + + if (Build.VERSION.SDK_INT + <= Build.VERSION_CODES.GINGERBREAD_MR1) + { + method + = activityThreadClass.getMethod ("getPackageInfo", + String.class, + int.class); + loadedApk = method.invoke (activityThread, "org.gnu.emacs", + 0); + } + else + { + compatibilityInfoClass + = Class.forName ("android.content.res.CompatibilityInfo"); + + method + = activityThreadClass.getMethod ("getPackageInfo", + String.class, + compatibilityInfoClass, + int.class); + loadedApk = method.invoke (activityThread, "org.gnu.emacs", + null, 0); + } + + if (loadedApk == null) + throw new RuntimeException ("getPackageInfo returned NULL"); + + /* Now, get a context. */ + contextImplClass = Class.forName ("android.app.ContextImpl"); + + try + { + method + = contextImplClass.getDeclaredMethod ("createAppContext", + activityThreadClass, + loadedApkClass); + method.setAccessible (true); + context = (Context) method.invoke (null, activityThread, + loadedApk); + } + catch (NoSuchMethodException exception) + { + /* Older Android versions don't have createAppContext, but + instead require creating a ContextImpl, and then + calling createPackageContext. */ + method + = activityThreadClass.getDeclaredMethod ("getSystemContext"); + context = (Context) method.invoke (activityThread); + method + = contextImplClass.getDeclaredMethod ("createPackageContext", + String.class, + int.class); + method.setAccessible (true); + context = (Context) method.invoke (context, "org.gnu.emacs", + 0); + } + } + /* Don't actually start the looper or anything. Instead, obtain an AssetManager. */ assets = context.getAssets (); diff --git a/src/android-asset.h b/src/android-asset.h index b0e83bbf424..3b2f8105865 100644 --- a/src/android-asset.h +++ b/src/android-asset.h @@ -365,15 +365,6 @@ android_asset_read_internal (AAsset *asset, int nbytes, char *buffer) 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) { diff --git a/src/android.c b/src/android.c index 72c50c0a13c..40d6234d508 100644 --- a/src/android.c +++ b/src/android.c @@ -1376,42 +1376,6 @@ android_hack_asset_fd (AAsset *asset) #endif } -/* Read two bytes from FD and see if they are ``PK'', denoting ZIP - archive compressed data. If FD is -1, return -1. - - If they are not, rewind the file descriptor to offset 0. - - If either operation fails, return -1 and close FD. Else, value is - FD. */ - -static int -android_check_compressed_file (int fd) -{ - char bytes[2]; - - if (fd == -1) - return -1; - - if (read (fd, bytes, 2) != 2) - goto lseek_back; - - if (bytes[0] != 'P' || bytes[1] != 'K') - goto lseek_back; - - /* This could be compressed data! */ - return -1; - - lseek_back: - /* Seek back to offset 0. If this fails, return -1. */ - if (lseek (fd, 0, SEEK_SET) != 0) - { - close (fd); - return -1; - } - - return fd; -} - /* Make FD close-on-exec. If any system call fails, do not abort, but log a warning to the system log. */ @@ -1482,28 +1446,21 @@ android_open (const char *filename, int oflag, int mode) return -1; } - /* Try to obtain the file descriptor corresponding to this - asset. */ - fd = AAsset_openFileDescriptor (asset, &out_start, - &out_length); + /* Create a shared memory file descriptor containing the asset + contents. + + The documentation misleads people into thinking that + AAsset_openFileDescriptor does precisely this. However, it + instead returns an offset into any uncompressed assets in the + ZIP archive. This cannot be found in its documentation. */ - /* The platform sometimes returns a file descriptor to ZIP - compressed data. Detect that and fall back to creating a - shared memory file descriptor. */ - fd = android_check_compressed_file (fd); + fd = android_hack_asset_fd (asset); if (fd == -1) { - /* The asset can't be accessed for some reason. Try to - create a shared memory file descriptor. */ - fd = android_hack_asset_fd (asset); - - if (fd == -1) - { - AAsset_close (asset); - errno = ENXIO; - return -1; - } + AAsset_close (asset); + errno = ENXIO; + return -1; } /* If O_CLOEXEC is specified, make the file descriptor close on -- 2.39.5