aboutsummaryrefslogtreecommitdiffstats
path: root/src/android-asset.h
diff options
context:
space:
mode:
authorPo Lu2023-02-17 16:27:00 +0800
committerPo Lu2023-02-17 16:27:00 +0800
commit88afd96e36e62017c9c1f2229e2748b6dfbdb39a (patch)
tree218845ef248e9ba2131abd22404e3de9dfbf3cbd /src/android-asset.h
parent759e6a24ab9690541acc6ece1adebaf524d6e5ae (diff)
downloademacs-88afd96e36e62017c9c1f2229e2748b6dfbdb39a.tar.gz
emacs-88afd96e36e62017c9c1f2229e2748b6dfbdb39a.zip
Fix build and running on Android 2.2
* INSTALL.android: Document that Android 2.2 is now supported, with caveats. * configure.ac (ANDROID_MIN_SDK, ANDROID_SDK_18_OR_EARLIER) (SYSTEM_TYPE, ANDROID_STUBIFY, SIZEOF_LONG): Correctly detect things missing on Android 2.2. * java/Makefile.in (ANDROID_JAR, JARSIGNER_FLAGS): * java/debug.sh (jdb, gdbserver, line): * java/org/gnu/emacs/EmacsApplication.java (findDumpFile): * java/org/gnu/emacs/EmacsService.java (onCreate): * java/org/gnu/emacs/EmacsThread.java (EmacsThread, run): Run parameter initialization on main thread. * src/android-asset.h (struct android_asset_manager) (struct android_asset, AAssetManager_fromJava, AAssetManager_open) (AAsset_close, android_asset_create_stream) (android_asset_read_internal, AAsset_openFileDescriptor) (AAsset_getLength, AAsset_getBuffer, AAsset_read): New file. * src/android.c (android_user_full_name, android_hack_asset_fd) (android_check_compressed_file): Implement for Android 2.2. * src/process.c (Fprocess_send_eof): Don't call tcdrain if unavailable. * src/sfntfont-android.c (system_font_directories): Fix compiler warning. * src/sfntfont.c (sfntfont_read_cmap): Correctly test rc of emacs_open. * src/textconv.c (handle_pending_conversion_events_1): Mark buffer UNINIT.
Diffstat (limited to 'src/android-asset.h')
-rw-r--r--src/android-asset.h423
1 files changed, 423 insertions, 0 deletions
diff --git a/src/android-asset.h b/src/android-asset.h
new file mode 100644
index 00000000000..b0e83bbf424
--- /dev/null
+++ b/src/android-asset.h
@@ -0,0 +1,423 @@
1/* Android initialization for GNU Emacs.
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
20#include <android/log.h>
21
22/* This file contains an emulation of the Android asset manager API
23 used on builds for Android 2.2. It is included by android.c
24 whenever appropriate.
25
26 The replacements in this file are not thread safe and must only be
27 called from the creating thread. */
28
29struct android_asset_manager
30{
31 /* JNI environment. */
32 JNIEnv *env;
33
34 /* Asset manager class and functions. */
35 jclass class;
36 jmethodID open_fd;
37
38 /* Asset file descriptor class and functions. */
39 jclass fd_class;
40 jmethodID get_length;
41 jmethodID create_input_stream;
42 jmethodID close;
43
44 /* Input stream class and functions. */
45 jclass input_stream_class;
46 jmethodID read;
47 jmethodID stream_close;
48
49 /* Associated asset manager object. */
50 jobject asset_manager;
51};
52
53typedef struct android_asset_manager AAssetManager;
54
55struct android_asset
56{
57 /* The asset manager. */
58 AAssetManager *manager;
59
60 /* The length of the asset, or -1. */
61 jlong length;
62
63 /* The asset file descriptor and input stream. */
64 jobject fd, stream;
65
66 /* The mode. */
67 int mode;
68};
69
70typedef struct android_asset AAsset;
71
72static AAssetManager *
73AAssetManager_fromJava (JNIEnv *env, jobject java_manager)
74{
75 AAssetManager *manager;
76 jclass temp;
77
78 manager = malloc (sizeof *manager);
79
80 if (!manager)
81 return NULL;
82
83 manager->env = env;
84 manager->asset_manager
85 = (*env)->NewGlobalRef (env, java_manager);
86
87 if (!manager->asset_manager)
88 {
89 free (manager);
90 return NULL;
91 }
92
93 manager->class
94 = (*env)->FindClass (env, "android/content/res/AssetManager");
95 assert (manager->class);
96
97 manager->open_fd
98 = (*env)->GetMethodID (env, manager->class, "openFd",
99 "(Ljava/lang/String;)"
100 "Landroid/content/res/AssetFileDescriptor;");
101 assert (manager->open);
102
103 manager->fd_class
104 = (*env)->FindClass (env, "android/content/res/AssetFileDescriptor");
105 assert (manager->fd_class);
106
107 manager->get_length
108 = (*env)->GetMethodID (env, manager->fd_class, "getLength",
109 "()J");
110 assert (manager->get_length);
111
112 manager->create_input_stream
113 = (*env)->GetMethodID (env, manager->fd_class,
114 "createInputStream",
115 "()Ljava/io/FileInputStream;");
116 assert (manager->create_input_stream);
117
118 manager->close
119 = (*env)->GetMethodID (env, manager->fd_class,
120 "close", "()V");
121 assert (manager->close);
122
123 manager->input_stream_class
124 = (*env)->FindClass (env, "java/io/InputStream");
125 assert (manager->input_stream_class);
126
127 manager->read
128 = (*env)->GetMethodID (env, manager->input_stream_class,
129 "read", "([B)I");
130 assert (manager->read);
131
132 manager->stream_close
133 = (*env)->GetMethodID (env, manager->input_stream_class,
134 "close", "()V");
135 assert (manager->stream_close);
136
137 /* Now convert all the class references to global ones. */
138 temp = manager->class;
139 manager->class
140 = (*env)->NewGlobalRef (env, temp);
141 assert (manager->class);
142 (*env)->DeleteLocalRef (env, temp);
143 temp = manager->fd_class;
144 manager->fd_class
145 = (*env)->NewGlobalRef (env, temp);
146 assert (manager->fd_class);
147 (*env)->DeleteLocalRef (env, temp);
148 temp = manager->input_stream_class;
149 manager->input_stream_class
150 = (*env)->NewGlobalRef (env, temp);
151 assert (manager->input_stream_class);
152 (*env)->DeleteLocalRef (env, temp);
153
154 /* Return the asset manager. */
155 return manager;
156}
157
158enum
159 {
160 AASSET_MODE_STREAMING = 0,
161 AASSET_MODE_BUFFER = 1,
162 };
163
164static AAsset *
165AAssetManager_open (AAssetManager *manager, const char *c_name,
166 int mode)
167{
168 jobject desc;
169 jstring name;
170 AAsset *asset;
171
172 /* Push a local frame. */
173 asset = NULL;
174
175 (*(manager->env))->PushLocalFrame (manager->env, 3);
176
177 if ((*(manager->env))->ExceptionCheck (manager->env))
178 goto fail;
179
180 /* Encoding issues can be ignored for now as there are only ASCII
181 file names in Emacs. */
182 name = (*(manager->env))->NewStringUTF (manager->env, c_name);
183
184 if (!name)
185 goto fail;
186
187 /* Now try to open an ``AssetFileDescriptor''. */
188 desc = (*(manager->env))->CallObjectMethod (manager->env,
189 manager->asset_manager,
190 manager->open_fd,
191 name);
192
193 if (!desc)
194 goto fail;
195
196 /* Allocate the asset. */
197 asset = calloc (1, sizeof *asset);
198
199 if (!asset)
200 {
201 (*(manager->env))->CallVoidMethod (manager->env,
202 desc,
203 manager->close);
204 goto fail;
205 }
206
207 /* Pop the local frame and return desc. */
208 desc = (*(manager->env))->NewGlobalRef (manager->env, desc);
209
210 if (!desc)
211 goto fail;
212
213 (*(manager->env))->PopLocalFrame (manager->env, NULL);
214
215 asset->manager = manager;
216 asset->length = -1;
217 asset->fd = desc;
218 asset->mode = mode;
219
220 return asset;
221
222 fail:
223 (*(manager->env))->ExceptionClear (manager->env);
224 (*(manager->env))->PopLocalFrame (manager->env, NULL);
225 free (asset);
226
227 return NULL;
228}
229
230static AAsset *
231AAsset_close (AAsset *asset)
232{
233 JNIEnv *env;
234
235 env = asset->manager->env;
236
237 (*env)->CallVoidMethod (asset->manager->env,
238 asset->fd,
239 asset->manager->close);
240 (*env)->DeleteGlobalRef (asset->manager->env,
241 asset->fd);
242
243 if (asset->stream)
244 {
245 (*env)->CallVoidMethod (asset->manager->env,
246 asset->stream,
247 asset->manager->stream_close);
248 (*env)->DeleteGlobalRef (asset->manager->env,
249 asset->stream);
250 }
251
252 free (asset);
253}
254
255/* Create an input stream associated with the given ASSET. Set
256 ASSET->stream to its global reference.
257
258 Value is 1 upon failure, else 0. ASSET must not already have an
259 input stream. */
260
261static int
262android_asset_create_stream (AAsset *asset)
263{
264 jobject stream;
265 JNIEnv *env;
266
267 env = asset->manager->env;
268 stream
269 = (*env)->CallObjectMethod (env, asset->fd,
270 asset->manager->create_input_stream);
271
272 if (!stream)
273 {
274 (*env)->ExceptionClear (env);
275 return 1;
276 }
277
278 asset->stream
279 = (*env)->NewGlobalRef (env, stream);
280
281 if (!asset->stream)
282 {
283 (*env)->ExceptionClear (env);
284 (*env)->DeleteLocalRef (env, stream);
285 return 1;
286 }
287
288 (*env)->DeleteLocalRef (env, stream);
289 return 0;
290}
291
292/* Read NBYTES from the specified asset into the given BUFFER;
293
294 Internally, allocate a Java byte array containing 4096 elements and
295 copy the data to and from that array.
296
297 Value is the number of bytes actually read, 0 at EOF, or -1 upon
298 failure, in which case errno is set accordingly. If NBYTES is
299 zero, behavior is undefined. */
300
301static int
302android_asset_read_internal (AAsset *asset, int nbytes, char *buffer)
303{
304 jbyteArray stash;
305 JNIEnv *env;
306 jint bytes_read, total;
307
308 /* Allocate a suitable amount of storage. Either nbytes or 4096,
309 whichever is larger. */
310 env = asset->manager->env;
311 stash = (*env)->NewByteArray (env, MIN (nbytes, 4096));
312
313 if (!stash)
314 {
315 (*env)->ExceptionClear (env);
316 errno = ENOMEM;
317 return -1;
318 }
319
320 /* Try to create an input stream. */
321
322 if (!asset->stream
323 && android_asset_create_stream (asset))
324 {
325 (*env)->DeleteLocalRef (env, stash);
326 errno = ENOMEM;
327 return -1;
328 }
329
330 /* Start reading. */
331
332 total = 0;
333
334 while (nbytes)
335 {
336 bytes_read = (*env)->CallIntMethod (env, asset->stream,
337 asset->manager->read,
338 stash);
339
340 /* Detect error conditions. */
341
342 if ((*env)->ExceptionCheck (env))
343 goto out;
344
345 /* Detect EOF. */
346
347 if (bytes_read == -1)
348 goto out;
349
350 /* Finally write out the amount that was read. */
351 bytes_read = MIN (bytes_read, nbytes);
352 (*env)->GetByteArrayRegion (env, stash, 0, bytes_read, buffer);
353
354 buffer += bytes_read;
355 total += bytes_read;
356 nbytes -= bytes_read;
357 }
358
359 /* Make sure the value of nbytes still makes sense. */
360 assert (nbytes >= 0);
361
362 out:
363 (*env)->ExceptionClear (env);
364 (*env)->DeleteLocalRef (env, stash);
365 return total;
366}
367
368static int
369AAsset_openFileDescriptor (AAsset *asset, off_t *out_start,
370 off_t *out_end)
371{
372 *out_start = 0;
373 *out_end = 0;
374 return -1;
375}
376
377static long
378AAsset_getLength (AAsset *asset)
379{
380 JNIEnv *env;
381
382 if (asset->length != -1)
383 return asset->length;
384
385 env = asset->manager->env;
386 asset->length
387 = (*env)->CallLongMethod (env, asset->fd,
388 asset->manager->get_length);
389 return asset->length;
390}
391
392static char *
393AAsset_getBuffer (AAsset *asset)
394{
395 long length;
396 char *buffer;
397
398 length = AAsset_getLength (asset);
399
400 if (!length)
401 return NULL;
402
403 buffer = malloc (length);
404
405 if (!buffer)
406 return NULL;
407
408 if (android_asset_read_internal (asset, length, buffer)
409 != length)
410 {
411 xfree (buffer);
412 return NULL;
413 }
414
415 return buffer;
416}
417
418static size_t
419AAsset_read (AAsset *asset, void *buffer, size_t size)
420{
421 return android_asset_read_internal (asset, MIN (size, INT_MAX),
422 buffer);
423}