diff options
| author | Po Lu | 2024-07-14 12:46:23 +0800 |
|---|---|---|
| committer | Po Lu | 2024-07-14 12:46:23 +0800 |
| commit | b00fc31dd1d4543f8b017e8d7fef7686cd430bcc (patch) | |
| tree | bc88bc250ff41feed860c4d2c90a3a2990de09d3 /java | |
| parent | 04bf3172f03497eef6985311c90fd6557ace42b2 (diff) | |
| download | emacs-b00fc31dd1d4543f8b017e8d7fef7686cd430bcc.tar.gz emacs-b00fc31dd1d4543f8b017e8d7fef7686cd430bcc.zip | |
Do not set LD_LIBRARY_PATH during Android initialization
* doc/emacs/android.texi (Android Environment): Adjust
documentation to match.
* java/org/gnu/emacs/EmacsNoninteractive.java (main1): New
function. Remove initialization of EmacsNative hither.
(main): Acquire an ApplicationInfo or LoadedApk, as the case may
be on the host system, derive a ClassLoader from the result, and
load and call `main1' from within this class loader.
* src/android-emacs.c (main):
* src/android.c (setEmacsParams): Do not override
LD_LIBRARY_PATH or set EMACS_LD_LIBRARY_PATH. This enables
Emacs to execute subprocesses in certain "fortified" Android
systems, amongst other things.
Diffstat (limited to 'java')
| -rw-r--r-- | java/org/gnu/emacs/EmacsNoninteractive.java | 250 |
1 files changed, 137 insertions, 113 deletions
diff --git a/java/org/gnu/emacs/EmacsNoninteractive.java b/java/org/gnu/emacs/EmacsNoninteractive.java index ba23399cb3e..9f2b9fa8b56 100644 --- a/java/org/gnu/emacs/EmacsNoninteractive.java +++ b/java/org/gnu/emacs/EmacsNoninteractive.java | |||
| @@ -30,37 +30,69 @@ import java.lang.reflect.Method; | |||
| 30 | 30 | ||
| 31 | /* Noninteractive Emacs. | 31 | /* Noninteractive Emacs. |
| 32 | 32 | ||
| 33 | This is the class that libandroid-emacs.so starts. | 33 | When started, libandroid-emacs.so invokes `app_process(64)' with a |
| 34 | libandroid-emacs.so figures out the system classpath, then starts | 34 | command line placing Emacs's classes.dex file in the JVM class path, |
| 35 | dalvikvm with the framework jars. | 35 | which in turn transfers control to `main'. `main' creates a context, |
| 36 | 36 | which may be likened to a connection to the system server, and a | |
| 37 | At that point, dalvikvm calls main, which sets up the main looper, | 37 | class loader derived from Emacs's application package, which it loads |
| 38 | creates an ActivityThread and attaches it to the main thread. | 38 | beforehand. From this class loader, it loads another instance of |
| 39 | 39 | itself, and invokes `main1', to ensure the execution of | |
| 40 | Then, it obtains an application context for the LoadedApk in the | 40 | `EmacsNative''s static initializer within the application class |
| 41 | application thread. | 41 | loader, where a proper library search path is in effect. */ |
| 42 | |||
| 43 | Finally, it obtains the necessary context specific objects and | ||
| 44 | initializes Emacs. */ | ||
| 45 | 42 | ||
| 46 | @SuppressWarnings ("unchecked") | 43 | @SuppressWarnings ("unchecked") |
| 47 | public final class EmacsNoninteractive | 44 | public final class EmacsNoninteractive |
| 48 | { | 45 | { |
| 46 | /* Prepare Emacs for startup and call `initEmacs'. This function is | ||
| 47 | called in an instance of `EmacsNoninteractive' loaded by the APK | ||
| 48 | ClassLoader acquired in `main', which guarantees that shared | ||
| 49 | libraries in the APK will be considered in resolving shared | ||
| 50 | libraries for `EmacsNative'. */ | ||
| 51 | |||
| 52 | public static void | ||
| 53 | main1 (String[] args, Context context) | ||
| 54 | throws Exception | ||
| 55 | { | ||
| 56 | AssetManager assets; | ||
| 57 | String filesDir, libDir, cacheDir; | ||
| 58 | |||
| 59 | /* Don't actually start the looper or anything. Instead, obtain | ||
| 60 | an AssetManager. */ | ||
| 61 | assets = context.getAssets (); | ||
| 62 | |||
| 63 | /* Now configure Emacs. The class path should already be set. */ | ||
| 64 | |||
| 65 | filesDir = context.getFilesDir ().getCanonicalPath (); | ||
| 66 | libDir = EmacsService.getLibraryDirectory (context); | ||
| 67 | cacheDir = context.getCacheDir ().getCanonicalPath (); | ||
| 68 | EmacsNative.setEmacsParams (assets, filesDir, | ||
| 69 | libDir, cacheDir, 0.0f, | ||
| 70 | 0.0f, 0.0f, null, null, | ||
| 71 | Build.VERSION.SDK_INT); | ||
| 72 | |||
| 73 | /* Now find the dump file that Emacs should use, if it has already | ||
| 74 | been dumped. */ | ||
| 75 | EmacsApplication.findDumpFile (context); | ||
| 76 | |||
| 77 | /* Start Emacs. */ | ||
| 78 | EmacsNative.initEmacs (args, EmacsApplication.dumpFileName); | ||
| 79 | } | ||
| 80 | |||
| 49 | public static void | 81 | public static void |
| 50 | main (String[] args) | 82 | main (String[] args) |
| 51 | { | 83 | { |
| 52 | Object activityThread, loadedApk; | 84 | Object activityThread, loadedApk; |
| 53 | Class activityThreadClass, loadedApkClass, contextImplClass; | 85 | Class activityThreadClass, loadedApkClass, contextImplClass; |
| 54 | Class compatibilityInfoClass; | 86 | Class compatibilityInfoClass, emacsNoninteractiveClass; |
| 55 | Method method; | 87 | Method method; |
| 56 | Context context; | 88 | Context context; |
| 57 | AssetManager assets; | 89 | ClassLoader classLoader; |
| 58 | String filesDir, libDir, cacheDir; | ||
| 59 | 90 | ||
| 60 | Looper.prepare (); | 91 | Looper.prepare (); |
| 92 | |||
| 61 | context = null; | 93 | context = null; |
| 62 | assets = null; | 94 | loadedApkClass = null; |
| 63 | filesDir = libDir = cacheDir = null; | 95 | classLoader = null; |
| 64 | 96 | ||
| 65 | try | 97 | try |
| 66 | { | 98 | { |
| @@ -72,7 +104,6 @@ public final class EmacsNoninteractive | |||
| 72 | 104 | ||
| 73 | /* Create and attach the activity thread. */ | 105 | /* Create and attach the activity thread. */ |
| 74 | activityThread = method.invoke (null); | 106 | activityThread = method.invoke (null); |
| 75 | context = null; | ||
| 76 | 107 | ||
| 77 | /* Now get an LoadedApk. */ | 108 | /* Now get an LoadedApk. */ |
| 78 | 109 | ||
| @@ -82,99 +113,88 @@ public final class EmacsNoninteractive | |||
| 82 | } | 113 | } |
| 83 | catch (ClassNotFoundException exception) | 114 | catch (ClassNotFoundException exception) |
| 84 | { | 115 | { |
| 85 | /* Android 2.2 has no LoadedApk class, but fortunately it | 116 | /* Android 2.2 has no LoadedApk class; the several following |
| 86 | does not need to be used, since contexts can be | 117 | statements will load a context and an |
| 87 | directly created. */ | 118 | ActivityThread.PackageInfo, as is appropriate on this |
| 119 | system. */ | ||
| 120 | } | ||
| 88 | 121 | ||
| 89 | loadedApkClass = null; | 122 | /* Get a LoadedApk or ActivityThread.PackageInfo. How to do |
| 90 | contextImplClass = Class.forName ("android.app.ContextImpl"); | 123 | this varies by Android version. On Android 2.3.3 and |
| 124 | earlier, there is no ``compatibilityInfo'' argument to | ||
| 125 | getPackageInfo. */ | ||
| 91 | 126 | ||
| 92 | method = activityThreadClass.getDeclaredMethod ("getSystemContext"); | 127 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) |
| 93 | context = (Context) method.invoke (activityThread); | 128 | { |
| 94 | method = contextImplClass.getDeclaredMethod ("createPackageContext", | 129 | method |
| 95 | String.class, | 130 | = activityThreadClass.getMethod ("getPackageInfo", |
| 96 | int.class); | 131 | String.class, |
| 97 | method.setAccessible (true); | 132 | int.class); |
| 98 | context = (Context) method.invoke (context, "org.gnu.emacs", | 133 | loadedApk = method.invoke (activityThread, "org.gnu.emacs", |
| 99 | 0); | 134 | (Context.CONTEXT_INCLUDE_CODE |
| 135 | | Context.CONTEXT_IGNORE_SECURITY)); | ||
| 100 | } | 136 | } |
| 137 | else | ||
| 138 | { | ||
| 139 | compatibilityInfoClass | ||
| 140 | = Class.forName ("android.content.res.CompatibilityInfo"); | ||
| 141 | |||
| 142 | method | ||
| 143 | = activityThreadClass.getMethod ("getPackageInfo", | ||
| 144 | String.class, | ||
| 145 | compatibilityInfoClass, | ||
| 146 | int.class); | ||
| 147 | loadedApk = method.invoke (activityThread, "org.gnu.emacs", | ||
| 148 | null, (Context.CONTEXT_INCLUDE_CODE | ||
| 149 | | Context.CONTEXT_IGNORE_SECURITY)); | ||
| 150 | } | ||
| 151 | |||
| 152 | if (loadedApk == null) | ||
| 153 | throw new RuntimeException ("getPackageInfo returned NULL"); | ||
| 154 | |||
| 155 | /* If loadedApkClass remains NULL, substitute the class of | ||
| 156 | the object returned by getPackageInfo. */ | ||
| 157 | if (loadedApkClass == null) | ||
| 158 | loadedApkClass = loadedApk.getClass (); | ||
| 101 | 159 | ||
| 102 | /* If the context has not already been created, then do what | 160 | /* Now, get a context. */ |
| 103 | is appropriate for newer versions of Android. */ | 161 | contextImplClass = Class.forName ("android.app.ContextImpl"); |
| 104 | 162 | ||
| 105 | if (context == null) | 163 | try |
| 106 | { | 164 | { |
| 107 | /* Get a LoadedApk. How to do this varies by Android version. | 165 | method |
| 108 | On Android 2.3.3 and earlier, there is no | 166 | = contextImplClass.getDeclaredMethod ("createAppContext", |
| 109 | ``compatibilityInfo'' argument to getPackageInfo. */ | 167 | activityThreadClass, |
| 110 | 168 | loadedApkClass); | |
| 111 | if (Build.VERSION.SDK_INT | 169 | method.setAccessible (true); |
| 112 | <= Build.VERSION_CODES.GINGERBREAD_MR1) | 170 | context = (Context) method.invoke (null, activityThread, |
| 113 | { | 171 | loadedApk); |
| 114 | method | 172 | } |
| 115 | = activityThreadClass.getMethod ("getPackageInfo", | 173 | catch (NoSuchMethodException exception) |
| 116 | String.class, | 174 | { |
| 117 | int.class); | 175 | /* Older Android versions don't have createAppContext, but |
| 118 | loadedApk = method.invoke (activityThread, "org.gnu.emacs", | 176 | instead require creating a ContextImpl, and then |
| 119 | 0); | 177 | calling createPackageContext. */ |
| 120 | } | 178 | method |
| 121 | else | 179 | = activityThreadClass.getDeclaredMethod ("getSystemContext"); |
| 122 | { | 180 | context = (Context) method.invoke (activityThread); |
| 123 | compatibilityInfoClass | 181 | method |
| 124 | = Class.forName ("android.content.res.CompatibilityInfo"); | 182 | = contextImplClass.getDeclaredMethod ("createPackageContext", |
| 125 | 183 | String.class, | |
| 126 | method | 184 | int.class); |
| 127 | = activityThreadClass.getMethod ("getPackageInfo", | 185 | method.setAccessible (true); |
| 128 | String.class, | 186 | context = (Context) method.invoke (context, "org.gnu.emacs", |
| 129 | compatibilityInfoClass, | 187 | 0); |
| 130 | int.class); | ||
| 131 | loadedApk = method.invoke (activityThread, "org.gnu.emacs", | ||
| 132 | null, 0); | ||
| 133 | } | ||
| 134 | |||
| 135 | if (loadedApk == null) | ||
| 136 | throw new RuntimeException ("getPackageInfo returned NULL"); | ||
| 137 | |||
| 138 | /* Now, get a context. */ | ||
| 139 | contextImplClass = Class.forName ("android.app.ContextImpl"); | ||
| 140 | |||
| 141 | try | ||
| 142 | { | ||
| 143 | method | ||
| 144 | = contextImplClass.getDeclaredMethod ("createAppContext", | ||
| 145 | activityThreadClass, | ||
| 146 | loadedApkClass); | ||
| 147 | method.setAccessible (true); | ||
| 148 | context = (Context) method.invoke (null, activityThread, | ||
| 149 | loadedApk); | ||
| 150 | } | ||
| 151 | catch (NoSuchMethodException exception) | ||
| 152 | { | ||
| 153 | /* Older Android versions don't have createAppContext, but | ||
| 154 | instead require creating a ContextImpl, and then | ||
| 155 | calling createPackageContext. */ | ||
| 156 | method | ||
| 157 | = activityThreadClass.getDeclaredMethod ("getSystemContext"); | ||
| 158 | context = (Context) method.invoke (activityThread); | ||
| 159 | method | ||
| 160 | = contextImplClass.getDeclaredMethod ("createPackageContext", | ||
| 161 | String.class, | ||
| 162 | int.class); | ||
| 163 | method.setAccessible (true); | ||
| 164 | context = (Context) method.invoke (context, "org.gnu.emacs", | ||
| 165 | 0); | ||
| 166 | } | ||
| 167 | } | 188 | } |
| 168 | 189 | ||
| 169 | /* Don't actually start the looper or anything. Instead, obtain | 190 | /* Retrieve the LoadedApk's class loader and execute the |
| 170 | an AssetManager. */ | 191 | remaining portion of the start-up process within its version |
| 171 | assets = context.getAssets (); | 192 | of EmacsNoninteractive, which will indicate to the system |
| 172 | 193 | that it must load shared libraries from the APK's library | |
| 173 | /* Now configure Emacs. The class path should already be set. */ | 194 | search path. */ |
| 174 | 195 | ||
| 175 | filesDir = context.getFilesDir ().getCanonicalPath (); | 196 | method = loadedApkClass.getDeclaredMethod ("getClassLoader"); |
| 176 | libDir = EmacsService.getLibraryDirectory (context); | 197 | classLoader = (ClassLoader) method.invoke (loadedApk); |
| 177 | cacheDir = context.getCacheDir ().getCanonicalPath (); | ||
| 178 | } | 198 | } |
| 179 | catch (Exception e) | 199 | catch (Exception e) |
| 180 | { | 200 | { |
| @@ -188,16 +208,20 @@ public final class EmacsNoninteractive | |||
| 188 | System.exit (1); | 208 | System.exit (1); |
| 189 | } | 209 | } |
| 190 | 210 | ||
| 191 | EmacsNative.setEmacsParams (assets, filesDir, | 211 | try |
| 192 | libDir, cacheDir, 0.0f, | 212 | { |
| 193 | 0.0f, 0.0f, null, null, | 213 | emacsNoninteractiveClass |
| 194 | Build.VERSION.SDK_INT); | 214 | = classLoader.loadClass ("org.gnu.emacs.EmacsNoninteractive"); |
| 195 | 215 | method = emacsNoninteractiveClass.getMethod ("main1", String[].class, | |
| 196 | /* Now find the dump file that Emacs should use, if it has already | 216 | Context.class); |
| 197 | been dumped. */ | 217 | method.setAccessible (true); |
| 198 | EmacsApplication.findDumpFile (context); | 218 | method.invoke (null, args, context); |
| 199 | 219 | } | |
| 200 | /* Start Emacs. */ | 220 | catch (Exception e) |
| 201 | EmacsNative.initEmacs (args, EmacsApplication.dumpFileName); | 221 | { |
| 222 | System.err.println ("Internal error during startup: " + e); | ||
| 223 | e.printStackTrace (); | ||
| 224 | System.exit (1); | ||
| 225 | } | ||
| 202 | } | 226 | } |
| 203 | }; | 227 | }; |