aboutsummaryrefslogtreecommitdiffstats
path: root/java/org/gnu
diff options
context:
space:
mode:
authorPo Lu2023-01-25 18:44:47 +0800
committerPo Lu2023-01-25 18:44:47 +0800
commit0900bfbcc57c555909cb75c38eb0ed26fb6964ef (patch)
tree9a2fa4328defab79f1cb3dcfac4f3c071bf0a633 /java/org/gnu
parent6f9a2a8f29c7faf13d0d86001b140746efc455b5 (diff)
downloademacs-0900bfbcc57c555909cb75c38eb0ed26fb6964ef.tar.gz
emacs-0900bfbcc57c555909cb75c38eb0ed26fb6964ef.zip
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;
22import java.io.File; 22import java.io.File;
23import java.io.FileFilter; 23import java.io.FileFilter;
24 24
25import android.content.Context;
26
25import android.app.Application; 27import android.app.Application;
26import android.util.Log; 28import android.util.Log;
27 29
28public class EmacsApplication extends Application implements FileFilter 30public class EmacsApplication extends Application
29{ 31{
30 private static final String TAG = "EmacsApplication"; 32 private static final String TAG = "EmacsApplication";
31 33
32 /* The name of the dump file to use. */ 34 /* The name of the dump file to use. */
33 public static String dumpFileName; 35 public static String dumpFileName;
34 36
35 @Override 37 public static void
36 public boolean 38 findDumpFile (Context context)
37 accept (File file)
38 {
39 return (!file.isDirectory ()
40 && file.getName ().endsWith (".pdmp"));
41 }
42
43 @Override
44 public void
45 onCreate ()
46 { 39 {
47 File filesDirectory; 40 File filesDirectory;
48 File[] allFiles; 41 File[] allFiles;
@@ -52,13 +45,19 @@ public class EmacsApplication extends Application implements FileFilter
52 wantedDumpFile = ("emacs-" + EmacsNative.getFingerprint () 45 wantedDumpFile = ("emacs-" + EmacsNative.getFingerprint ()
53 + ".pdmp"); 46 + ".pdmp");
54 47
55 Log.d (TAG, "onCreate: looking for " + wantedDumpFile);
56
57 /* Obtain a list of all files ending with ``.pdmp''. Then, look 48 /* Obtain a list of all files ending with ``.pdmp''. Then, look
58 for a file named ``emacs-<fingerprint>.pdmp'' and delete the 49 for a file named ``emacs-<fingerprint>.pdmp'' and delete the
59 rest. */ 50 rest. */
60 filesDirectory = getFilesDir (); 51 filesDirectory = context.getFilesDir ();
61 allFiles = filesDirectory.listFiles (this); 52 allFiles = filesDirectory.listFiles (new FileFilter () {
53 @Override
54 public boolean
55 accept (File file)
56 {
57 return (!file.isDirectory ()
58 && file.getName ().endsWith (".pdmp"));
59 }
60 });
62 61
63 /* Now try to find the right dump file. */ 62 /* Now try to find the right dump file. */
64 for (i = 0; i < allFiles.length; ++i) 63 for (i = 0; i < allFiles.length; ++i)
@@ -69,9 +68,13 @@ public class EmacsApplication extends Application implements FileFilter
69 /* Delete this outdated dump file. */ 68 /* Delete this outdated dump file. */
70 allFiles[i].delete (); 69 allFiles[i].delete ();
71 } 70 }
71 }
72 72
73 Log.d (TAG, "onCreate: found " + dumpFileName); 73 @Override
74 74 public void
75 onCreate ()
76 {
77 findDumpFile (this);
75 super.onCreate (); 78 super.onCreate ();
76 } 79 }
77}; 80};
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
29 can be used to determine the dump file name. */ 29 can be used to determine the dump file name. */
30 public static native String getFingerprint (); 30 public static native String getFingerprint ();
31 31
32 /* Set certain parameters before initializing Emacs. This proves 32 /* Set certain parameters before initializing Emacs.
33 that libemacs.so is being loaded from Java code.
34 33
35 assetManager must be the asset manager associated with the 34 assetManager must be the asset manager associated with the
36 context that is loading Emacs. It is saved and remains for the 35 context that is loading Emacs. It is saved and remains for the
@@ -48,19 +47,26 @@ public class EmacsNative
48 pixelDensityX and pixelDensityY are the DPI values that will be 47 pixelDensityX and pixelDensityY are the DPI values that will be
49 used by Emacs. 48 used by Emacs.
50 49
51 emacsService must be the emacsService singleton. */ 50 classPath must be the classpath of this app_process process, or
51 NULL.
52
53 emacsService must be the EmacsService singleton, or NULL. */
52 public static native void setEmacsParams (AssetManager assetManager, 54 public static native void setEmacsParams (AssetManager assetManager,
53 String filesDir, 55 String filesDir,
54 String libDir, 56 String libDir,
55 String cacheDir, 57 String cacheDir,
56 float pixelDensityX, 58 float pixelDensityX,
57 float pixelDensityY, 59 float pixelDensityY,
60 String classPath,
58 EmacsService emacsService); 61 EmacsService emacsService);
59 62
60 /* Initialize Emacs with the argument array ARGV. Each argument 63 /* Initialize Emacs with the argument array ARGV. Each argument
61 must contain a NULL terminated string, or else the behavior is 64 must contain a NULL terminated string, or else the behavior is
62 undefined. */ 65 undefined.
63 public static native void initEmacs (String argv[]); 66
67 DUMPFILE is the dump file to use, or NULL if Emacs is to load
68 loadup.el itself. */
69 public static native void initEmacs (String argv[], String dumpFile);
64 70
65 /* Abort and generate a native core dump. */ 71 /* Abort and generate a native core dump. */
66 public static native void emacsAbort (); 72 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 @@
1/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
2
3Copyright (C) 2023 Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or (at
10your option) any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
19
20package org.gnu.emacs;
21
22import android.os.Looper;
23import android.os.Build;
24
25import android.content.Context;
26import android.content.res.AssetManager;
27
28import java.lang.reflect.Constructor;
29import java.lang.reflect.Method;
30
31/* Noninteractive Emacs.
32
33 This is the class that libandroid-emacs.so starts.
34 libandroid-emacs.so figures out the system classpath, then starts
35 dalvikvm with the framework jars.
36
37 At that point, dalvikvm calls main, which sets up the main looper,
38 creates an ActivityThread and attaches it to the main thread.
39
40 Then, it obtains an application context for the LoadedApk in the
41 application thread.
42
43 Finally, it obtains the necessary context specific objects and
44 initializes Emacs. */
45
46@SuppressWarnings ("unchecked")
47public class EmacsNoninteractive
48{
49 private static String
50 getLibraryDirectory (Context context)
51 {
52 int apiLevel;
53
54 apiLevel = Build.VERSION.SDK_INT;
55
56 if (apiLevel >= Build.VERSION_CODES.GINGERBREAD)
57 return context.getApplicationInfo().nativeLibraryDir;
58 else if (apiLevel >= Build.VERSION_CODES.DONUT)
59 return context.getApplicationInfo().dataDir + "/lib";
60
61 return "/data/data/" + context.getPackageName() + "/lib";
62 }
63
64 public static void
65 main (String[] args)
66 {
67 Object activityThread, loadedApk;
68 Class activityThreadClass, loadedApkClass, contextImplClass;
69 Class compatibilityInfoClass;
70 Method method;
71 Context context;
72 AssetManager assets;
73 String filesDir, libDir, cacheDir;
74
75 Looper.prepare ();
76 context = null;
77 assets = null;
78 filesDir = libDir = cacheDir = null;
79
80 try
81 {
82 /* Get the activity thread. */
83 activityThreadClass = Class.forName ("android.app.ActivityThread");
84
85 /* Get the systemMain method. */
86 method = activityThreadClass.getMethod ("systemMain");
87
88 /* Create and attach the activity thread. */
89 activityThread = method.invoke (null);
90
91 /* Now get an LoadedApk. */
92 loadedApkClass = Class.forName ("android.app.LoadedApk");
93
94 /* Get a LoadedApk. How to do this varies by Android version.
95 On Android 2.3.3 and earlier, there is no
96 ``compatibilityInfo'' argument to getPackageInfo. */
97
98 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD)
99 {
100 method
101 = activityThreadClass.getMethod ("getPackageInfo",
102 String.class,
103 int.class);
104 loadedApk = method.invoke (activityThread, "org.gnu.emacs",
105 0);
106 }
107 else
108 {
109 compatibilityInfoClass
110 = Class.forName ("android.content.res.CompatibilityInfo");
111
112 method
113 = activityThreadClass.getMethod ("getPackageInfo",
114 String.class,
115 compatibilityInfoClass,
116 int.class);
117 loadedApk = method.invoke (activityThread, "org.gnu.emacs", null,
118 0);
119 }
120
121 if (loadedApk == null)
122 throw new RuntimeException ("getPackageInfo returned NULL");
123
124 /* Now, get a context. */
125 contextImplClass = Class.forName ("android.app.ContextImpl");
126 method = contextImplClass.getDeclaredMethod ("createAppContext",
127 activityThreadClass,
128 loadedApkClass);
129 method.setAccessible (true);
130 context = (Context) method.invoke (null, activityThread, loadedApk);
131
132 /* Don't actually start the looper or anything. Instead, obtain
133 an AssetManager. */
134 assets = context.getAssets ();
135
136 /* Now configure Emacs. The class path should already be set. */
137
138 filesDir = context.getFilesDir ().getCanonicalPath ();
139 libDir = getLibraryDirectory (context);
140 cacheDir = context.getCacheDir ().getCanonicalPath ();
141 }
142 catch (Exception e)
143 {
144 System.err.println ("Internal error: " + e);
145 System.err.println ("This means that the Android platform changed,");
146 System.err.println ("and that Emacs needs adjustments in order to");
147 System.err.println ("obtain required system internal resources.");
148 System.err.println ("Please report this bug to bug-gnu-emacs@gnu.org.");
149
150 System.exit (1);
151 }
152
153 EmacsNative.setEmacsParams (assets, filesDir,
154 libDir, cacheDir, 0.0f,
155 0.0f, null, null);
156
157 /* Now find the dump file that Emacs should use, if it has already
158 been dumped. */
159 EmacsApplication.findDumpFile (context);
160
161 /* Start Emacs. */
162 EmacsNative.initEmacs (args, EmacsApplication.dumpFileName);
163 }
164};
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;
41 41
42import android.content.Context; 42import android.content.Context;
43import android.content.Intent; 43import android.content.Intent;
44import android.content.pm.ApplicationInfo;
45import android.content.pm.PackageManager.ApplicationInfoFlags;
46import android.content.pm.PackageManager;
44import android.content.res.AssetManager; 47import android.content.res.AssetManager;
45 48
46import android.net.Uri; 49import android.net.Uri;
@@ -118,8 +121,38 @@ public class EmacsService extends Service
118 return null; 121 return null;
119 } 122 }
120 123
124 @SuppressWarnings ("deprecation")
125 private String
126 getApkFile ()
127 {
128 PackageManager manager;
129 ApplicationInfo info;
130
131 manager = getPackageManager ();
132
133 try
134 {
135 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
136 info = manager.getApplicationInfo ("org.gnu.emacs", 0);
137 else
138 info = manager.getApplicationInfo ("org.gnu.emacs",
139 ApplicationInfoFlags.of (0));
140
141 /* Return an empty string upon failure. */
142
143 if (info.sourceDir != null)
144 return info.sourceDir;
145
146 return "";
147 }
148 catch (Exception e)
149 {
150 return "";
151 }
152 }
153
121 @TargetApi (Build.VERSION_CODES.GINGERBREAD) 154 @TargetApi (Build.VERSION_CODES.GINGERBREAD)
122 String 155 private String
123 getLibraryDirectory () 156 getLibraryDirectory ()
124 { 157 {
125 int apiLevel; 158 int apiLevel;
@@ -142,7 +175,7 @@ public class EmacsService extends Service
142 { 175 {
143 AssetManager manager; 176 AssetManager manager;
144 Context app_context; 177 Context app_context;
145 String filesDir, libDir, cacheDir; 178 String filesDir, libDir, cacheDir, classPath;
146 double pixelDensityX; 179 double pixelDensityX;
147 double pixelDensityY; 180 double pixelDensityY;
148 181
@@ -162,13 +195,18 @@ public class EmacsService extends Service
162 libDir = getLibraryDirectory (); 195 libDir = getLibraryDirectory ();
163 cacheDir = app_context.getCacheDir ().getCanonicalPath (); 196 cacheDir = app_context.getCacheDir ().getCanonicalPath ();
164 197
198 /* Now provide this application's apk file, so a recursive
199 invocation of app_process (through android-emacs) can
200 find EmacsNoninteractive. */
201 classPath = getApkFile ();
202
165 Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir 203 Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir
166 + " and libDir = " + libDir); 204 + ", libDir = " + libDir + ", and classPath = " + classPath);
167 205
168 EmacsNative.setEmacsParams (manager, filesDir, libDir, 206 EmacsNative.setEmacsParams (manager, filesDir, libDir,
169 cacheDir, (float) pixelDensityX, 207 cacheDir, (float) pixelDensityX,
170 (float) pixelDensityY, 208 (float) pixelDensityY,
171 this); 209 classPath, this);
172 210
173 /* Start the thread that runs Emacs. */ 211 /* Start the thread that runs Emacs. */
174 thread = new EmacsThread (this, needDashQ); 212 thread = new EmacsThread (this, needDashQ);
@@ -491,8 +529,6 @@ public class EmacsService extends Service
491 public static void 529 public static void
492 startEmacsService (Context context) 530 startEmacsService (Context context)
493 { 531 {
494 PendingIntent intent;
495
496 if (EmacsService.SERVICE == null) 532 if (EmacsService.SERVICE == null)
497 { 533 {
498 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) 534 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
33 this.startDashQ = startDashQ; 33 this.startDashQ = startDashQ;
34 } 34 }
35 35
36 @Override
36 public void 37 public void
37 run () 38 run ()
38 { 39 {
39 String args[]; 40 String args[];
40 41
41 if (EmacsApplication.dumpFileName == null) 42 if (!startDashQ)
42 { 43 args = new String[] { "libandroid-emacs.so", };
43 if (!startDashQ)
44 args = new String[] { "libandroid-emacs.so", };
45 else
46 args = new String[] { "libandroid-emacs.so", "-Q", };
47 }
48 else 44 else
49 { 45 args = new String[] { "libandroid-emacs.so", "-Q", };
50 if (!startDashQ)
51 args = new String[] { "libandroid-emacs.so", "--dump-file",
52 EmacsApplication.dumpFileName, };
53 else
54 args = new String[] { "libandroid-emacs.so", "-Q",
55 "--dump-file",
56 EmacsApplication.dumpFileName, };
57 }
58 46
59 /* Run the native code now. */ 47 /* Run the native code now. */
60 EmacsNative.initEmacs (args); 48 EmacsNative.initEmacs (args, EmacsApplication.dumpFileName);
61 } 49 }
62}; 50};