aboutsummaryrefslogtreecommitdiffstats
path: root/java
diff options
context:
space:
mode:
authorPo Lu2024-07-14 12:46:23 +0800
committerPo Lu2024-07-14 12:46:23 +0800
commitb00fc31dd1d4543f8b017e8d7fef7686cd430bcc (patch)
treebc88bc250ff41feed860c4d2c90a3a2990de09d3 /java
parent04bf3172f03497eef6985311c90fd6557ace42b2 (diff)
downloademacs-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.java250
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")
47public final class EmacsNoninteractive 44public 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};