1 /* Android initialization for GNU Emacs.
2
3 Copyright (C) 2023 Free Software Foundation, Inc.
4
5 This file is part of GNU Emacs.
6
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or (at
10 your option) any later version.
11
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <alloca.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 /* android-emacs is a wrapper around /system/bin/app_process(64).
27 It invokes app_process(64) with the right class path and then
28 starts org.gnu.emacs.EmacsNoninteractive.
29
30 The main function in that class tries to load an activity thread
31 and obtain a context and asset manager before calling
32 android_emacs_init, which is required for Emacs to find required
33 preloaded Lisp. */
34
35 int
36 main (int argc, char **argv)
37 {
38 char **args;
39 int i;
40 char *bootclasspath, *emacs_class_path, *ld_library_path;
41
42 /* Allocate enough to hold the arguments to app_process. */
43 args = alloca ((10 + argc) * sizeof *args);
44
45 /* Clear args. */
46 memset (args, 0, (10 + argc) * sizeof *args);
47
48 /* First, figure out what program to start. */
49 #if defined __x86_64__ || defined __aarch64__ || defined __mips64
50 args[0] = (char *) "/system/bin/app_process64";
51 #else /* i386 || regular mips || arm */
52 args[0] = (char *) "/system/bin/app_process";
53 #endif /* __x86_64__ || __aarch64__ || __mips64 */
54
55 /* Machines with ART require the boot classpath to be manually
56 specified. Machines with Dalvik however refuse to do so, as they
57 open the jars inside the BOOTCLASSPATH environment variable at
58 startup, resulting in the following crash:
59
60 W/dalvikvm( 1608): Refusing to reopen boot DEX
61 '/system/framework/core.jar'
62 W/dalvikvm( 1608): Refusing to reopen boot DEX
63 '/system/framework/bouncycastle.jar'
64 E/dalvikvm( 1608): Too many exceptions during init (failed on
65 'Ljava/io/IOException;' 'Re-opening BOOTCLASSPATH DEX files is
66 not allowed')
67 E/dalvikvm( 1608): VM aborting */
68
69 #if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL
70 if (android_get_device_api_level () < 21)
71 {
72 bootclasspath = NULL;
73 goto skip_setup;
74 }
75 #else /* !HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL */
76 if (__ANDROID_API__ < 21)
77 {
78 bootclasspath = NULL;
79 goto skip_setup;
80 }
81 #endif /* HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL */
82
83 /* Next, obtain the boot class path. */
84 bootclasspath = getenv ("BOOTCLASSPATH");
85
86 if (!bootclasspath)
87 {
88 fprintf (stderr, "The BOOTCLASSPATH environment variable"
89 " is not set. As a result, Emacs does not know"
90 " how to start app_process.\n"
91 "This is likely a change in the Android platform."
92 " Please report this to bug-gnu-emacs@gnu.org.\n");
93 return 1;
94 }
95
96 skip_setup:
97
98 /* And the Emacs class path. */
99 emacs_class_path = getenv ("EMACS_CLASS_PATH");
100
101 if (!emacs_class_path)
102 {
103 fprintf (stderr, "EMACS_CLASS_PATH not set."
104 " Please make sure Emacs is being started"
105 " from within a running copy of Emacs.\n");
106 return 1;
107 }
108
109 /* Restore LD_LIBRARY_PATH to its original value, the app library
110 directory, to guarantee that it is possible for Java to find the
111 Emacs C code later. */
112
113 ld_library_path = getenv ("EMACS_LD_LIBRARY_PATH");
114
115 if (ld_library_path)
116 setenv ("LD_LIBRARY_PATH", ld_library_path, 1);
117
118 if (bootclasspath)
119 {
120 if (asprintf (&bootclasspath, "-Djava.class.path=%s:%s",
121 bootclasspath, emacs_class_path) < 0)
122 {
123 perror ("asprintf");
124 return 1;
125 }
126 }
127 else
128 {
129 if (asprintf (&bootclasspath, "-Djava.class.path=%s",
130 emacs_class_path) < 0)
131 {
132 perror ("asprintf");
133 return 1;
134 }
135 }
136
137 args[1] = bootclasspath;
138 args[2] = (char *) "/system/bin";
139
140 #if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL
141 /* I don't know exactly when --nice-name was introduced; this is
142 just a guess. */
143 if (android_get_device_api_level () >= 26)
144 {
145 args[3] = (char *) "--nice-name=emacs";
146 args[4] = (char *) "org.gnu.emacs.EmacsNoninteractive";
147
148 /* Arguments from here on are passed to main in
149 EmacsNoninteractive.java. */
150 args[5] = argv[0];
151
152 /* Now copy the rest of the arguments over. */
153 for (i = 1; i < argc; ++i)
154 args[5 + i] = argv[i];
155 }
156 else
157 {
158 #endif /* HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL */
159 args[3] = (char *) "org.gnu.emacs.EmacsNoninteractive";
160
161 /* Arguments from here on are passed to main in
162 EmacsNoninteractive.java. */
163 args[4] = argv[0];
164
165 /* Now copy the rest of the arguments over. */
166 for (i = 1; i < argc; ++i)
167 args[4 + i] = argv[i];
168 #if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL
169 }
170 #endif /* HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL */
171
172 /* Finally, try to start the app_process. */
173 execvp (args[0], args);
174
175 /* If exit fails, return an error indication. */
176 perror ("exec");
177 return 1;
178 }