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.
This commit is contained in:
Po Lu 2024-07-14 12:46:23 +08:00
parent 04bf3172f0
commit b00fc31dd1
4 changed files with 147 additions and 147 deletions

View file

@ -30,37 +30,69 @@
/* 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");
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 ();
/* 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
= contextImplClass.getDeclaredMethod ("createPackageContext",
String.class,
int.class);
method.setAccessible (true);
context = (Context) method.invoke (context, "org.gnu.emacs",
0);
}
/* If the context has not already been created, then do what
is appropriate for newer versions of Android. */
/* 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. */
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 ();
/* Now configure Emacs. The class path should already be set. */
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);
}
}
};