]> git.eshelyaron.com Git - emacs.git/commitdiff
Do not set LD_LIBRARY_PATH during Android initialization
authorPo Lu <luangruo@yahoo.com>
Sun, 14 Jul 2024 04:46:23 +0000 (12:46 +0800)
committerEshel Yaron <me@eshelyaron.com>
Wed, 17 Jul 2024 17:39:04 +0000 (19:39 +0200)
* 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.

(cherry picked from commit b00fc31dd1d4543f8b017e8d7fef7686cd430bcc)

doc/emacs/android.texi
java/org/gnu/emacs/EmacsNoninteractive.java
src/android-emacs.c
src/android.c

index 250e07d9279113cd229e1ce4cdb0252b921b4d3b..a374d13d67e1b078bd383a53a00caef29201892b 100644 (file)
@@ -378,20 +378,18 @@ definition documents, so your mileage may vary.
 
 @cindex EMACS_CLASS_PATH environment variable, Android
   Even when the location of the @command{libandroid-emacs.so} command is
-known in advance, special configuration is required to run Emacs from
+known in advance, special preparation is required to run Emacs from
 elsewhere than a subprocess of an existing Emacs session, as it must be
 made to understand the location of resources and shared libraries in or
 extracted from the installed application package.  The OS command
 @command{pm path org.gnu.emacs} will print the location of the
-application package, and the adjacent @file{lib} directory will hold
-shared libraries extracted from the same, though the said command must
-be invoked in a peculiar manner to satisfy system restrictions on
-communication between pseudoterminal devices created by user
-applications and system services such as the package manager, which is
-to say, with the standard IO streams redirected to a real file or a
-pipe.  Such values, once established, must be specified in the
-environment variables @code{EMACS_CLASS_PATH} and
-@code{EMACS_LD_LIBRARY_PATH}, so that this sample shell script may be
+application package, though the said command must be invoked in a
+peculiar manner to satisfy system restrictions on communication between
+pseudoterminal devices created by user applications and system services
+such as the package manager, which is to say, with the standard IO
+streams redirected to a real file or a pipe.  This value, once
+established, must be specified in the environment variables
+@code{EMACS_CLASS_PATH}, so that this sample shell script may be
 installed as @code{emacs} in any location that is accessible:
 
 @example
@@ -400,7 +398,6 @@ installed as @code{emacs} in any location that is accessible:
 package_name=`pm path org.gnu.emacs 2>/dev/null </dev/null \
                | sed 's/^package://'`
 emacs=
-ld_path=
 EMACS_CLASS_PATH=$package_name
 
 for libdir in `dirname $package_name`/lib/*; do
@@ -409,10 +406,7 @@ for libdir in `dirname $package_name`/lib/*; do
     && emacs="$libdir"/libandroid-emacs.so
 done
 
-EMACS_LD_LIBRARY_PATH=$ld_path
-
 export EMACS_CLASS_PATH
-export EMACS_LD_LIBRARY_PATH
 test -x "$emacs" || exit 1
 exec $emacs "$@@"
 @end example
index ba23399cb3ef4cdfa77ed21828bbb39db0f0d9a1..9f2b9fa8b56ca3592f61061437d28f596754b300 100644 (file)
@@ -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);
+      }
   }
 };
index d68734da1bd101c51321ceeddd260229ed164768..28b46ca1a4b5be9c37356861da4d7f1bdcc492b1 100644 (file)
@@ -37,7 +37,7 @@ main (int argc, char **argv)
 {
   char **args;
   int i;
-  char *bootclasspath, *emacs_class_path, *ld_library_path;
+  char *bootclasspath, *emacs_class_path;
 
   /* Allocate enough to hold the arguments to app_process.  */
   args = alloca ((10 + argc) * sizeof *args);
@@ -63,15 +63,6 @@ main (int argc, char **argv)
       return 1;
     }
 
-  /* Restore LD_LIBRARY_PATH to its original value, the app library
-     directory, to guarantee that it is possible for Java to find the
-     Emacs C code later.  */
-
-  ld_library_path = getenv ("EMACS_LD_LIBRARY_PATH");
-
-  if (ld_library_path)
-    setenv ("LD_LIBRARY_PATH", ld_library_path, 1);
-
   if (asprintf (&bootclasspath, "-Djava.class.path=%s",
                emacs_class_path) < 0)
     {
index f90ebc049259eff7ab0d704cf4ac85bfeb9e2c4e..3c96867a6b519f7e66187b0d8aad95cda689acaa 100644 (file)
@@ -1338,7 +1338,7 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
 
   int pipefd[2];
   pthread_t thread;
-  const char *java_string;
+  const char *java_string, *tem;
   struct stat statb;
 
 #ifdef THREADS_ENABLED
@@ -1491,15 +1491,6 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
        EmacsNoninteractive can be found.  */
     setenv ("EMACS_CLASS_PATH", android_class_path, 1);
 
-  /* Set LD_LIBRARY_PATH to an appropriate value.  */
-  setenv ("LD_LIBRARY_PATH", android_lib_dir, 1);
-
-  /* EMACS_LD_LIBRARY_PATH records the location of the app library
-     directory.  android-emacs refers to this, since users have valid
-     reasons for changing LD_LIBRARY_PATH to a value that precludes
-     the possibility of Java locating libemacs later.  */
-  setenv ("EMACS_LD_LIBRARY_PATH", android_lib_dir, 1);
-
   /* If the system is Android 5.0 or later, set LANG to en_US.utf8,
      which is understood by the C library.  In other instances set it
      to C, a meaningless value, for good measure.  */