summaryrefslogtreecommitdiff
path: root/java/org/gnu
diff options
context:
space:
mode:
authorPo Lu <luangruo@yahoo.com>2023-01-25 18:44:47 +0800
committerPo Lu <luangruo@yahoo.com>2023-01-25 18:44:47 +0800
commit0900bfbcc57c555909cb75c38eb0ed26fb6964ef (patch)
tree9a2fa4328defab79f1cb3dcfac4f3c071bf0a633 /java/org/gnu
parent6f9a2a8f29c7faf13d0d86001b140746efc455b5 (diff)
Update Android port
* doc/emacs/android.texi (Android Startup, Android Environment): Document that restrictions on starting Emacs have been lifted. * java/README: Document Java for Emacs developers and how the Android port works. * java/org/gnu/emacs/EmacsApplication.java (EmacsApplication) (findDumpFile): New function. (onCreate): Factor out dump file finding functions to there. * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Update function declarations. * java/org/gnu/emacs/EmacsNoninteractive.java (EmacsNoninteractive): New class. * java/org/gnu/emacs/EmacsService.java (EmacsService, getApkFile) (onCreate): Pass classpath to setEmacsParams. * java/org/gnu/emacs/EmacsThread.java (EmacsThread): Make run an override. * lisp/loadup.el: Don't dump on Android when noninteractive. * lisp/shell.el (shell--command-completion-data): Handle inaccessible directories. * src/Makefile.in (android-emacs): Link with gnulib. * src/android-emacs.c (main): Implement to launch app-process and then EmacsNoninteractive. * src/android.c (setEmacsParams): New argument `class_path'. Don't set stuff up when running noninteractive. * src/android.h (initEmacs): Likewise. * src/androidfont.c (init_androidfont): * src/androidselect.c (init_androidselect): Don't initialize when running noninteractive. * src/emacs.c (load_pdump): New argument `dump_file'. (android_emacs_init): Give new argument `dump_file' to `load_pdump'. * src/sfntfont-android.c (init_sfntfont_android): Don't initialize when running noninteractive.
Diffstat (limited to 'java/org/gnu')
-rw-r--r--java/org/gnu/emacs/EmacsApplication.java39
-rw-r--r--java/org/gnu/emacs/EmacsNative.java16
-rw-r--r--java/org/gnu/emacs/EmacsNoninteractive.java164
-rw-r--r--java/org/gnu/emacs/EmacsService.java48
-rw-r--r--java/org/gnu/emacs/EmacsThread.java22
5 files changed, 243 insertions, 46 deletions
diff --git a/java/org/gnu/emacs/EmacsApplication.java b/java/org/gnu/emacs/EmacsApplication.java
index 87085c32d62..96328b99d1c 100644
--- a/java/org/gnu/emacs/EmacsApplication.java
+++ b/java/org/gnu/emacs/EmacsApplication.java
@@ -22,27 +22,20 @@ package org.gnu.emacs;
import java.io.File;
import java.io.FileFilter;
+import android.content.Context;
+
import android.app.Application;
import android.util.Log;
-public class EmacsApplication extends Application implements FileFilter
+public class EmacsApplication extends Application
{
private static final String TAG = "EmacsApplication";
/* The name of the dump file to use. */
public static String dumpFileName;
- @Override
- public boolean
- accept (File file)
- {
- return (!file.isDirectory ()
- && file.getName ().endsWith (".pdmp"));
- }
-
- @Override
- public void
- onCreate ()
+ public static void
+ findDumpFile (Context context)
{
File filesDirectory;
File[] allFiles;
@@ -52,13 +45,19 @@ public class EmacsApplication extends Application implements FileFilter
wantedDumpFile = ("emacs-" + EmacsNative.getFingerprint ()
+ ".pdmp");
- Log.d (TAG, "onCreate: looking for " + wantedDumpFile);
-
/* Obtain a list of all files ending with ``.pdmp''. Then, look
for a file named ``emacs-<fingerprint>.pdmp'' and delete the
rest. */
- filesDirectory = getFilesDir ();
- allFiles = filesDirectory.listFiles (this);
+ filesDirectory = context.getFilesDir ();
+ allFiles = filesDirectory.listFiles (new FileFilter () {
+ @Override
+ public boolean
+ accept (File file)
+ {
+ return (!file.isDirectory ()
+ && file.getName ().endsWith (".pdmp"));
+ }
+ });
/* Now try to find the right dump file. */
for (i = 0; i < allFiles.length; ++i)
@@ -69,9 +68,13 @@ public class EmacsApplication extends Application implements FileFilter
/* Delete this outdated dump file. */
allFiles[i].delete ();
}
+ }
- Log.d (TAG, "onCreate: found " + dumpFileName);
-
+ @Override
+ public void
+ onCreate ()
+ {
+ findDumpFile (this);
super.onCreate ();
}
};
diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java
index 9636561a524..a772b965301 100644
--- a/java/org/gnu/emacs/EmacsNative.java
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -29,8 +29,7 @@ public class EmacsNative
can be used to determine the dump file name. */
public static native String getFingerprint ();
- /* Set certain parameters before initializing Emacs. This proves
- that libemacs.so is being loaded from Java code.
+ /* Set certain parameters before initializing Emacs.
assetManager must be the asset manager associated with the
context that is loading Emacs. It is saved and remains for the
@@ -48,19 +47,26 @@ public class EmacsNative
pixelDensityX and pixelDensityY are the DPI values that will be
used by Emacs.
- emacsService must be the emacsService singleton. */
+ classPath must be the classpath of this app_process process, or
+ NULL.
+
+ emacsService must be the EmacsService singleton, or NULL. */
public static native void setEmacsParams (AssetManager assetManager,
String filesDir,
String libDir,
String cacheDir,
float pixelDensityX,
float pixelDensityY,
+ String classPath,
EmacsService emacsService);
/* Initialize Emacs with the argument array ARGV. Each argument
must contain a NULL terminated string, or else the behavior is
- undefined. */
- public static native void initEmacs (String argv[]);
+ undefined.
+
+ DUMPFILE is the dump file to use, or NULL if Emacs is to load
+ loadup.el itself. */
+ public static native void initEmacs (String argv[], String dumpFile);
/* Abort and generate a native core dump. */
public static native void emacsAbort ();
diff --git a/java/org/gnu/emacs/EmacsNoninteractive.java b/java/org/gnu/emacs/EmacsNoninteractive.java
new file mode 100644
index 00000000000..a3aefee5e0b
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsNoninteractive.java
@@ -0,0 +1,164 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+package org.gnu.emacs;
+
+import android.os.Looper;
+import android.os.Build;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+
+import java.lang.reflect.Constructor;
+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. */
+
+@SuppressWarnings ("unchecked")
+public class EmacsNoninteractive
+{
+ private static String
+ getLibraryDirectory (Context context)
+ {
+ int apiLevel;
+
+ apiLevel = Build.VERSION.SDK_INT;
+
+ if (apiLevel >= Build.VERSION_CODES.GINGERBREAD)
+ return context.getApplicationInfo().nativeLibraryDir;
+ else if (apiLevel >= Build.VERSION_CODES.DONUT)
+ return context.getApplicationInfo().dataDir + "/lib";
+
+ return "/data/data/" + context.getPackageName() + "/lib";
+ }
+
+ public static void
+ main (String[] args)
+ {
+ Object activityThread, loadedApk;
+ Class activityThreadClass, loadedApkClass, contextImplClass;
+ Class compatibilityInfoClass;
+ Method method;
+ Context context;
+ AssetManager assets;
+ String filesDir, libDir, cacheDir;
+
+ Looper.prepare ();
+ context = null;
+ assets = null;
+ filesDir = libDir = cacheDir = null;
+
+ try
+ {
+ /* Get the activity thread. */
+ activityThreadClass = Class.forName ("android.app.ActivityThread");
+
+ /* Get the systemMain method. */
+ method = activityThreadClass.getMethod ("systemMain");
+
+ /* Create and attach the activity thread. */
+ activityThread = method.invoke (null);
+
+ /* Now get an LoadedApk. */
+ loadedApkClass = Class.forName ("android.app.LoadedApk");
+
+ /* 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)
+ {
+ 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");
+ method = contextImplClass.getDeclaredMethod ("createAppContext",
+ activityThreadClass,
+ loadedApkClass);
+ method.setAccessible (true);
+ context = (Context) method.invoke (null, activityThread, loadedApk);
+
+ /* 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 = getLibraryDirectory (context);
+ cacheDir = context.getCacheDir ().getCanonicalPath ();
+ }
+ catch (Exception e)
+ {
+ System.err.println ("Internal error: " + e);
+ System.err.println ("This means that the Android platform changed,");
+ System.err.println ("and that Emacs needs adjustments in order to");
+ System.err.println ("obtain required system internal resources.");
+ System.err.println ("Please report this bug to bug-gnu-emacs@gnu.org.");
+
+ System.exit (1);
+ }
+
+ EmacsNative.setEmacsParams (assets, filesDir,
+ libDir, cacheDir, 0.0f,
+ 0.0f, null, null);
+
+ /* 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);
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java
index 4db1ea5359f..91db76b08e3 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -41,6 +41,9 @@ import android.app.Service;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.net.Uri;
@@ -118,8 +121,38 @@ public class EmacsService extends Service
return null;
}
+ @SuppressWarnings ("deprecation")
+ private String
+ getApkFile ()
+ {
+ PackageManager manager;
+ ApplicationInfo info;
+
+ manager = getPackageManager ();
+
+ try
+ {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
+ info = manager.getApplicationInfo ("org.gnu.emacs", 0);
+ else
+ info = manager.getApplicationInfo ("org.gnu.emacs",
+ ApplicationInfoFlags.of (0));
+
+ /* Return an empty string upon failure. */
+
+ if (info.sourceDir != null)
+ return info.sourceDir;
+
+ return "";
+ }
+ catch (Exception e)
+ {
+ return "";
+ }
+ }
+
@TargetApi (Build.VERSION_CODES.GINGERBREAD)
- String
+ private String
getLibraryDirectory ()
{
int apiLevel;
@@ -142,7 +175,7 @@ public class EmacsService extends Service
{
AssetManager manager;
Context app_context;
- String filesDir, libDir, cacheDir;
+ String filesDir, libDir, cacheDir, classPath;
double pixelDensityX;
double pixelDensityY;
@@ -162,13 +195,18 @@ public class EmacsService extends Service
libDir = getLibraryDirectory ();
cacheDir = app_context.getCacheDir ().getCanonicalPath ();
+ /* Now provide this application's apk file, so a recursive
+ invocation of app_process (through android-emacs) can
+ find EmacsNoninteractive. */
+ classPath = getApkFile ();
+
Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir
- + " and libDir = " + libDir);
+ + ", libDir = " + libDir + ", and classPath = " + classPath);
EmacsNative.setEmacsParams (manager, filesDir, libDir,
cacheDir, (float) pixelDensityX,
(float) pixelDensityY,
- this);
+ classPath, this);
/* Start the thread that runs Emacs. */
thread = new EmacsThread (this, needDashQ);
@@ -491,8 +529,6 @@ public class EmacsService extends Service
public static void
startEmacsService (Context context)
{
- PendingIntent intent;
-
if (EmacsService.SERVICE == null)
{
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
diff --git a/java/org/gnu/emacs/EmacsThread.java b/java/org/gnu/emacs/EmacsThread.java
index 5b76d11db4b..f5e9d54044a 100644
--- a/java/org/gnu/emacs/EmacsThread.java
+++ b/java/org/gnu/emacs/EmacsThread.java
@@ -33,30 +33,18 @@ public class EmacsThread extends Thread
this.startDashQ = startDashQ;
}
+ @Override
public void
run ()
{
String args[];
- if (EmacsApplication.dumpFileName == null)
- {
- if (!startDashQ)
- args = new String[] { "libandroid-emacs.so", };
- else
- args = new String[] { "libandroid-emacs.so", "-Q", };
- }
+ if (!startDashQ)
+ args = new String[] { "libandroid-emacs.so", };
else
- {
- if (!startDashQ)
- args = new String[] { "libandroid-emacs.so", "--dump-file",
- EmacsApplication.dumpFileName, };
- else
- args = new String[] { "libandroid-emacs.so", "-Q",
- "--dump-file",
- EmacsApplication.dumpFileName, };
- }
+ args = new String[] { "libandroid-emacs.so", "-Q", };
/* Run the native code now. */
- EmacsNative.initEmacs (args);
+ EmacsNative.initEmacs (args, EmacsApplication.dumpFileName);
}
};