summaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
authorPo Lu <luangruo@yahoo.com>2024-07-14 12:47:51 +0800
committerPo Lu <luangruo@yahoo.com>2024-07-14 12:47:51 +0800
commitf9dae55ccca065a6b27d00959db711faf59b9fa3 (patch)
treecc1946c13ca3bd2a63c9317c52ee48a94f374a45 /java
parentf38c42d1c7a8413f63b1e56261850d3dbe8abef8 (diff)
parentb00fc31dd1d4543f8b017e8d7fef7686cd430bcc (diff)
Merge from savannah/emacs-30
b00fc31dd1d Do not set LD_LIBRARY_PATH during Android initialization 04bf3172f03 ; Set Transient's version e6f78485aa6 ; Fix typos in 'which-key-mode' (bug#72093)
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;
/* Noninteractive Emacs.
- This is the class that libandroid-emacs.so starts.
- libandroid-emacs.so figures out the system classpath, then starts
- dalvikvm with the framework jars.
-
- At that point, dalvikvm calls main, which sets up the main looper,
- creates an ActivityThread and attaches it to the main thread.
-
- Then, it obtains an application context for the LoadedApk in the
- application thread.
-
- Finally, it obtains the necessary context specific objects and
- initializes Emacs. */
+ When started, libandroid-emacs.so invokes `app_process(64)' with a
+ command line placing Emacs's classes.dex file in the JVM class path,
+ which in turn transfers control to `main'. `main' creates a context,
+ which may be likened to a connection to the system server, and a
+ class loader derived from Emacs's application package, which it loads
+ beforehand. From this class loader, it loads another instance of
+ itself, and invokes `main1', to ensure the execution of
+ `EmacsNative''s static initializer within the application class
+ loader, where a proper library search path is in effect. */
@SuppressWarnings ("unchecked")
public final class EmacsNoninteractive
{
+ /* Prepare Emacs for startup and call `initEmacs'. This function is
+ called in an instance of `EmacsNoninteractive' loaded by the APK
+ ClassLoader acquired in `main', which guarantees that shared
+ libraries in the APK will be considered in resolving shared
+ libraries for `EmacsNative'. */
+
+ public static void
+ main1 (String[] args, Context context)
+ throws Exception
+ {
+ AssetManager assets;
+ String filesDir, libDir, cacheDir;
+
+ /* Don't actually start the looper or anything. Instead, obtain
+ an AssetManager. */
+ assets = context.getAssets ();
+
+ /* Now configure Emacs. The class path should already be set. */
+
+ filesDir = context.getFilesDir ().getCanonicalPath ();
+ libDir = EmacsService.getLibraryDirectory (context);
+ cacheDir = context.getCacheDir ().getCanonicalPath ();
+ EmacsNative.setEmacsParams (assets, filesDir,
+ libDir, cacheDir, 0.0f,
+ 0.0f, 0.0f, null, null,
+ Build.VERSION.SDK_INT);
+
+ /* Now find the dump file that Emacs should use, if it has already
+ been dumped. */
+ EmacsApplication.findDumpFile (context);
+
+ /* Start Emacs. */
+ EmacsNative.initEmacs (args, EmacsApplication.dumpFileName);
+ }
+
public static void
main (String[] args)
{
Object activityThread, loadedApk;
Class activityThreadClass, loadedApkClass, contextImplClass;
- Class compatibilityInfoClass;
+ Class compatibilityInfoClass, emacsNoninteractiveClass;
Method method;
Context context;
- AssetManager assets;
- String filesDir, libDir, cacheDir;
+ ClassLoader classLoader;
Looper.prepare ();
+
context = null;
- assets = null;
- filesDir = libDir = cacheDir = null;
+ loadedApkClass = null;
+ classLoader = null;
try
{
@@ -72,7 +104,6 @@ public final class EmacsNoninteractive
/* Create and attach the activity thread. */
activityThread = method.invoke (null);
- context = null;
/* Now get an LoadedApk. */
@@ -82,99 +113,88 @@ public final class EmacsNoninteractive
}
catch (ClassNotFoundException exception)
{
- /* Android 2.2 has no LoadedApk class, but fortunately it
- does not need to be used, since contexts can be
- directly created. */
+ /* Android 2.2 has no LoadedApk class; the several following
+ statements will load a context and an
+ ActivityThread.PackageInfo, as is appropriate on this
+ system. */
+ }
- loadedApkClass = null;
- contextImplClass = Class.forName ("android.app.ContextImpl");
+ /* Get a LoadedApk or ActivityThread.PackageInfo. How to do
+ this varies by Android version. On Android 2.3.3 and
+ earlier, there is no ``compatibilityInfo'' argument to
+ getPackageInfo. */
- 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);
+ 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",
+ (Context.CONTEXT_INCLUDE_CODE
+ | Context.CONTEXT_IGNORE_SECURITY));
}
+ 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, (Context.CONTEXT_INCLUDE_CODE
+ | Context.CONTEXT_IGNORE_SECURITY));
+ }
+
+ if (loadedApk == null)
+ throw new RuntimeException ("getPackageInfo returned NULL");
+
+ /* If loadedApkClass remains NULL, substitute the class of
+ the object returned by getPackageInfo. */
+ if (loadedApkClass == null)
+ loadedApkClass = loadedApk.getClass ();
- /* If the context has not already been created, then do what
- is appropriate for newer versions of Android. */
+ /* Now, get a context. */
+ contextImplClass = Class.forName ("android.app.ContextImpl");
- if (context == null)
+ try
{
- /* 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);
- }
+ 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 ();
-
- /* Now configure Emacs. The class path should already be set. */
+ /* Retrieve the LoadedApk's class loader and execute the
+ remaining portion of the start-up process within its version
+ of EmacsNoninteractive, which will indicate to the system
+ that it must load shared libraries from the APK's library
+ search path. */
- filesDir = context.getFilesDir ().getCanonicalPath ();
- libDir = EmacsService.getLibraryDirectory (context);
- cacheDir = context.getCacheDir ().getCanonicalPath ();
+ method = loadedApkClass.getDeclaredMethod ("getClassLoader");
+ classLoader = (ClassLoader) method.invoke (loadedApk);
}
catch (Exception e)
{
@@ -188,16 +208,20 @@ public final class EmacsNoninteractive
System.exit (1);
}
- EmacsNative.setEmacsParams (assets, filesDir,
- libDir, cacheDir, 0.0f,
- 0.0f, 0.0f, null, null,
- Build.VERSION.SDK_INT);
-
- /* Now find the dump file that Emacs should use, if it has already
- been dumped. */
- EmacsApplication.findDumpFile (context);
-
- /* Start Emacs. */
- EmacsNative.initEmacs (args, EmacsApplication.dumpFileName);
+ try
+ {
+ emacsNoninteractiveClass
+ = classLoader.loadClass ("org.gnu.emacs.EmacsNoninteractive");
+ method = emacsNoninteractiveClass.getMethod ("main1", String[].class,
+ Context.class);
+ method.setAccessible (true);
+ method.invoke (null, args, context);
+ }
+ catch (Exception e)
+ {
+ System.err.println ("Internal error during startup: " + e);
+ e.printStackTrace ();
+ System.exit (1);
+ }
}
};