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;
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__
50 args[0] = (char *) "/system/bin/app_process64";
51 #else
52 args[0] = (char *) "/system/bin/app_process";
53 #endif
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
76 if (__ANDROID_API__ < 21)
77 {
78 bootclasspath = NULL;
79 goto skip_setup;
80 }
81 #endif
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 if (bootclasspath)
110 {
111 if (asprintf (&bootclasspath, "-Djava.class.path=%s:%s",
112 bootclasspath, emacs_class_path) < 0)
113 {
114 perror ("asprintf");
115 return 1;
116 }
117 }
118 else
119 {
120 if (asprintf (&bootclasspath, "-Djava.class.path=%s",
121 emacs_class_path) < 0)
122 {
123 perror ("asprintf");
124 return 1;
125 }
126 }
127
128 args[1] = bootclasspath;
129 args[2] = (char *) "/system/bin";
130
131 #if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL
132 /* I don't know exactly when --nice-name was introduced; this is
133 just a guess. */
134 if (android_get_device_api_level () >= 26)
135 {
136 args[3] = (char *) "--nice-name=emacs";
137 args[4] = (char *) "org.gnu.emacs.EmacsNoninteractive";
138
139 /* Arguments from here on are passed to main in
140 EmacsNoninteractive.java. */
141 args[5] = argv[0];
142
143 /* Now copy the rest of the arguments over. */
144 for (i = 1; i < argc; ++i)
145 args[5 + i] = argv[i];
146 }
147 else
148 {
149 #endif
150 args[3] = (char *) "org.gnu.emacs.EmacsNoninteractive";
151
152 /* Arguments from here on are passed to main in
153 EmacsNoninteractive.java. */
154 args[4] = argv[0];
155
156 /* Now copy the rest of the arguments over. */
157 for (i = 1; i < argc; ++i)
158 args[4 + i] = argv[i];
159 #if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL
160 }
161 #endif
162
163 /* Finally, try to start the app_process. */
164 execvp (args[0], args);
165
166 /* If exit fails, return an error indication. */
167 perror ("exec");
168 return 1;
169 }