aboutsummaryrefslogtreecommitdiffstats
path: root/src/android.c
diff options
context:
space:
mode:
authorPo Lu2023-07-27 17:13:39 +0800
committerPo Lu2023-07-27 17:13:39 +0800
commit65b58251b1b07b50672367c675efdfdc97354c4a (patch)
tree9e323a76c3d5f81ecce18321beca703c456b2802 /src/android.c
parentce63f592f579e8963a3e7f44b31c7df50fb0cdba (diff)
downloademacs-65b58251b1b07b50672367c675efdfdc97354c4a.tar.gz
emacs-65b58251b1b07b50672367c675efdfdc97354c4a.zip
Update Android port
* configure.ac (ANDROID_STUBIFY): Add androidvfs.o when building libemacs.so. * doc/emacs/android.texi (Android): Add `Android Document Providers'. (Android Startup): Update the location of the content identifier directory. (Android File System): Describe access to document provider directories. (Android Document Providers): New node. * doc/emacs/emacs.texi (Top): Update the menu for the Android appendix. * java/Makefile.in (filename, install_temp/assets/build_info): Make directory-tree depend on build_info. * java/org/gnu/emacs/EmacsActivity.java (onActivityResult): New function. When a document tree is accepted, persist access to it. * java/org/gnu/emacs/EmacsDirectoryEntry.java (EmacsDirectoryEntry): New struct. * java/org/gnu/emacs/EmacsOpenActivity.java (checkReadableOrCopy): Use EmacsService.buildContentName. * java/org/gnu/emacs/EmacsService.java (getEmacsView, openContentUri) (checkContentUri): Remove excessive debug logging. (buildContentName, getDocumentAuthorities, requestDirectoryAccess) (getDocumentTrees, decodeFileName, documentIdFromName, getTreeUri) (statDocument, accessDocument, openDocumentDirectory, readDirectoryEntry) (openDocument, createDocument): New functions. * lib-src/asset-directory-tool.c: Improve commentary by illustrating the difference between directory and ordinary files. * src/android.c (ANDROID_THROW, enum android_fd_table_entry_flags) (struct android_emacs_service, android_extract_long) (android_scan_directory_tree, android_is_directory) (android_get_asset_name, android_url_encode, android_content_name_p) (android_get_content_name, android_check_content_access, android_fstat) (android_fstatat, android_file_access_p, android_hack_asset_fd_fallback) (android_detect_ashmem, android_hack_asset_fd, android_close_on_exec) (android_open, android_close, android_fclose, android_create_lib_link) (android_faccessat, struct android_dir, android_opendir, android_dirfd) (android_readdir, android_closedir, android_lookup_asset_directory_fd) (android_exception_check_3, android_get_current_api_level) (android_open_asset, android_close_asset, android_asset_read_quit) (android_asset_read, android_asset_lseek, android_asset_fstat): Move content and asset related functions to androidvfs.c. (android_init_emacs_service): Obtain handles for new JNI functions. (initEmacsParams): Initialize the VFS layer. (android_request_directory_access): New function. (android_display_toast): Remove unused function. * src/android.h (android_get_current_api_level): Assume that this function never returns less than __ANDROID_API__. (struct android_emacs_service): Move `struct android_emacs_service' here. * src/androidfns.c (Fandroid_request_directory_access): New interactive function. (syms_of_androidfns): Register new subr. * src/androidvfs.c (struct android_vdir, struct android_vops) (struct android_vnode, struct android_special_vnode) (enum android_vnode_type, struct android_cursor_class) (struct emacs_directory_entry_class) (struct android_parcel_file_descriptor_class) (android_init_cursor_class, android_init_entry_class) (android_init_fd_class, android_vfs_canonicalize_name) (struct android_unix_vnode, struct android_unix_vdir, unix_vfs_ops) (android_unix_name, android_unix_vnode, android_unix_open) (android_unix_close, android_unix_unlink, android_unix_symlink) (android_unix_rmdir, android_unix_rename, android_unix_stat) (android_unix_access, android_unix_mkdir, android_unix_readdir) (android_unix_closedir, android_unix_dirfd, android_unix_opendir) (android_extract_long, android_scan_directory_tree) (android_is_directory, android_init_assets) (android_hack_asset_fd_fallback, android_detect_ashmem) (android_hack_asset_fd, struct android_afs_vnode) (struct android_afs_vdir, struct android_afs_open_fd, afs_vfs_ops) (android_afs_name, android_afs_initial, android_close_on_exec) (android_afs_open, android_afs_close, android_afs_unlink) (android_afs_symlink, android_afs_rmdir, android_afs_rename) (android_afs_stat, android_afs_access, android_afs_mkdir) (android_afs_readdir, android_afs_closedir, android_afs_dirfd) (android_afs_opendir, android_afs_get_directory_name) (struct android_content_vdir, content_vfs_ops) (content_directory_contents, android_content_name) (android_content_open, android_content_close) (android_content_unlink, android_content_symlink) (android_content_rmdir, android_content_rename) (android_content_stat, android_content_access) (android_content_mkdir, android_content_readdir) (android_content_closedir, android_content_dirfd) (android_content_opendir, android_content_get_directory_name) (android_content_initial, android_get_content_name) (android_check_content_access, struct android_authority_vnode) (authority_vfs_ops, android_authority_name, android_authority_open) (android_authority_close, android_authority_unlink) (android_authority_symlink, android_authority_rmdir) (android_authority_rename, android_authority_stat) (android_authority_access, android_authority_mkdir) (android_authority_opendir, android_authority_initial) (struct android_saf_root_vnode, struct android_saf_root_vdir) (saf_root_vfs_ops, android_saf_root_name, android_saf_root_open) (android_saf_root_close, android_saf_root_unlink) (android_saf_root_symlink, android_saf_root_rmdir) (android_saf_root_rename, android_saf_root_stat) (android_saf_root_access, android_saf_root_mkdir) (android_saf_root_readdir, android_saf_root_closedir) (android_saf_root_dirfd, android_saf_root_opendir) (android_saf_root_initial, android_saf_root_get_directory) (android_saf_stat, android_saf_access) (struct android_saf_tree_vnode, struct android_saf_tree_vdir) (saf_tree_vfs_ops, android_document_id_from_name) (android_saf_tree_name, android_saf_tree_open) (android_saf_tree_close, android_saf_tree_unlink) (android_saf_tree_symlink, android_saf_tree_rmdir) (android_saf_tree_rename, android_saf_tree_stat) (android_saf_tree_access, android_saf_tree_mkdir) (android_saf_tree_opendir_1, android_saf_tree_readdir) (android_saf_tree_closedir, android_saf_tree_dirfd) (android_saf_tree_opendir, android_saf_tree_from_name) (android_saf_tree_get_directory, android_saf_file_vnode) (saf_file_vfs_ops, android_saf_file_name, android_saf_file_open) (android_saf_file_unlink, android_saf_file_rmdir) (android_saf_file_opendir, android_close_parcel_fd) (android_saf_new_vnode, android_saf_new_name, android_saf_new_open) (android_saf_new_unlink, android_saf_new_symlink) (android_saf_new_rmdir, android_saf_new_rename) (android_saf_new_stat, android_saf_new_access) (android_saf_new_mkdir, android_saf_new_opendir, root_vfs_ops) (special_vnodes, android_root_name, android_name_file) (android_vfs_init, android_open, android_unlink, android_symlink) (android_rmdir, android_mkdir, android_renameat_noreplace) (android_rename, android_fstat, android_fstatat_1, android_fstatat) (android_faccessat, android_fdopen, android_close, android_fclose) (android_open_asset, android_close_asset, android_asset_read_quit) (android_asset_read, android_asset_lseek, android_asset_fstat) (android_opendir, android_dirfd, android_readdir) (android_closedir): Move file system emulation routines here. Introduce a new ``VFS'' layer for translating between Emacs-specific file names and the various disparate interfaces for accessing files on Android. * src/callproc.c (delete_temp_file): * src/charset.c (load_charset_map_from_file): * src/dired.c: * src/emacs.c (Fkill_emacs): * src/fileio.c (check_mutable_filename, Fcopy_file) (Fmake_directory_internal, Fdelete_directory_internal) (Fdelete_file, Frename_file, Fadd_name_to_file) (Fmake_symbolic_link, file_accessible_directory_p, Fset_file_modes) (Fset_file_times, write_region): * src/filelock.c (get_boot_time, rename_lock_file) (create_lock_file, current_lock_owner, unlock_file): * src/image.c (slurp_file, png_load_body, jpeg_load_body): * src/keyboard.c (Fopen_dribble_file): * src/lisp.h: * src/lread.c (Fload): * src/process.c (handle_child_signal): * src/sysdep.c (init_standard_fds, emacs_fopen, emacs_fdopen) (emacs_unlink, emacs_symlink, emacs_rmdir, emacs_mkdir) (emacs_renameat_noreplace, emacs_rename): * src/term.c (Fresume_tty, init_tty): Use and add new wrappers for fopen, fdopen, unlink, symlink, rmdir, mkdir, renameat_norepalce and rename.
Diffstat (limited to 'src/android.c')
-rw-r--r--src/android.c1799
1 files changed, 257 insertions, 1542 deletions
diff --git a/src/android.c b/src/android.c
index 6fcaa40b2a9..b1d7b75c129 100644
--- a/src/android.c
+++ b/src/android.c
@@ -29,6 +29,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
29#include <math.h> 29#include <math.h>
30#include <string.h> 30#include <string.h>
31#include <stdckdint.h> 31#include <stdckdint.h>
32#include <intprops.h>
32#include <timespec.h> 33#include <timespec.h>
33#include <libgen.h> 34#include <libgen.h>
34 35
@@ -56,17 +57,9 @@ bool android_init_gui;
56 57
57#ifndef ANDROID_STUBIFY 58#ifndef ANDROID_STUBIFY
58 59
59#if __ANDROID_API__ >= 9
60#include <android/asset_manager.h>
61#include <android/asset_manager_jni.h>
62#else
63#include "android-asset.h"
64#endif
65
66#include <android/bitmap.h> 60#include <android/bitmap.h>
67#include <android/log.h> 61#include <android/log.h>
68 62
69#include <linux/ashmem.h>
70#include <linux/unistd.h> 63#include <linux/unistd.h>
71 64
72#include <sys/syscall.h> 65#include <sys/syscall.h>
@@ -78,50 +71,6 @@ bool android_init_gui;
78#define ANDROID_THROW(env, class, msg) \ 71#define ANDROID_THROW(env, class, msg) \
79 ((*(env))->ThrowNew ((env), (*(env))->FindClass ((env), class), msg)) 72 ((*(env))->ThrowNew ((env), (*(env))->FindClass ((env), class), msg))
80 73
81#define ANDROID_MAX_ASSET_FD 65535
82
83struct android_fd_table_entry
84{
85 /* Various flags associated with this table. */
86 short flags;
87
88 /* The stat buffer associated with this entry. */
89 struct stat statb;
90};
91
92enum android_fd_table_entry_flags
93 {
94 ANDROID_FD_TABLE_ENTRY_IS_VALID = 1,
95 };
96
97struct android_emacs_service
98{
99 jclass class;
100 jmethodID fill_rectangle;
101 jmethodID fill_polygon;
102 jmethodID draw_rectangle;
103 jmethodID draw_line;
104 jmethodID draw_point;
105 jmethodID clear_window;
106 jmethodID clear_area;
107 jmethodID ring_bell;
108 jmethodID query_tree;
109 jmethodID get_screen_width;
110 jmethodID get_screen_height;
111 jmethodID detect_mouse;
112 jmethodID name_keysym;
113 jmethodID browse_url;
114 jmethodID restart_emacs;
115 jmethodID update_ic;
116 jmethodID reset_ic;
117 jmethodID open_content_uri;
118 jmethodID check_content_uri;
119 jmethodID query_battery;
120 jmethodID display_toast;
121 jmethodID update_extracted_text;
122 jmethodID update_cursor_anchor_info;
123};
124
125struct android_emacs_pixmap 74struct android_emacs_pixmap
126{ 75{
127 jclass class; 76 jclass class;
@@ -174,12 +123,6 @@ struct android_emacs_cursor
174/* The API level of the current device. */ 123/* The API level of the current device. */
175static int android_api_level; 124static int android_api_level;
176 125
177/* The asset manager being used. */
178static AAssetManager *asset_manager;
179
180/* Whether or not Emacs has been initialized. */
181static int emacs_initialized;
182
183/* The directory used to store site-lisp. */ 126/* The directory used to store site-lisp. */
184char *android_site_load_path; 127char *android_site_load_path;
185 128
@@ -206,11 +149,6 @@ double android_scaled_pixel_density;
206/* The Android application data directory. */ 149/* The Android application data directory. */
207static char *android_files_dir; 150static char *android_files_dir;
208 151
209/* Array of structures used to hold asset information corresponding to
210 a file descriptor. This assumes Emacs does not do funny things
211 with dup. It currently does not. */
212static struct android_fd_table_entry android_table[ANDROID_MAX_ASSET_FD];
213
214/* The Java environment being used for the main thread. */ 152/* The Java environment being used for the main thread. */
215JNIEnv *android_java_env; 153JNIEnv *android_java_env;
216 154
@@ -235,10 +173,10 @@ static jclass android_rect_class;
235static jmethodID android_rect_constructor; 173static jmethodID android_rect_constructor;
236 174
237/* The EmacsService object. */ 175/* The EmacsService object. */
238static jobject emacs_service; 176jobject emacs_service;
239 177
240/* Various methods associated with the EmacsService. */ 178/* Various methods associated with the EmacsService. */
241static struct android_emacs_service service_class; 179struct android_emacs_service service_class;
242 180
243/* Various methods associated with the EmacsPixmap class. */ 181/* Various methods associated with the EmacsPixmap class. */
244static struct android_emacs_pixmap pixmap_class; 182static struct android_emacs_pixmap pixmap_class;
@@ -881,224 +819,6 @@ android_run_debug_thread (void *data)
881 819
882 820
883 821
884/* Asset directory handling functions. ``directory-tree'' is a file in
885 the root of the assets directory describing its contents.
886
887 See lib-src/asset-directory-tool for more details. */
888
889/* The Android directory tree. */
890static const char *directory_tree;
891
892/* The size of the directory tree. */
893static size_t directory_tree_size;
894
895/* Read an unaligned (32-bit) long from the address POINTER. */
896
897static unsigned int
898android_extract_long (char *pointer)
899{
900 unsigned int number;
901
902 memcpy (&number, pointer, sizeof number);
903 return number;
904}
905
906/* Scan to the file FILE in the asset directory tree. Return a
907 pointer to the end of that file (immediately before any children)
908 in the directory tree, or NULL if that file does not exist.
909
910 If returning non-NULL, also return the offset to the end of the
911 last subdirectory or file in *LIMIT_RETURN. LIMIT_RETURN may be
912 NULL.
913
914 FILE must have less than 11 levels of nesting. If it ends with a
915 trailing slash, then NULL will be returned if it is not actually a
916 directory. */
917
918static const char *
919android_scan_directory_tree (char *file, size_t *limit_return)
920{
921 char *token, *saveptr, *copy, *copy1, *start, *max, *limit;
922 size_t token_length, ntokens, i;
923 char *tokens[10];
924
925 USE_SAFE_ALLOCA;
926
927 /* Skip past the 5 byte header. */
928 start = (char *) directory_tree + 5;
929
930 /* Figure out the current limit. */
931 limit = (char *) directory_tree + directory_tree_size;
932
933 /* Now, split `file' into tokens, with the delimiter being the file
934 name separator. Look for the file and seek past it. */
935
936 ntokens = 0;
937 saveptr = NULL;
938 copy = copy1 = xstrdup (file);
939 memset (tokens, 0, sizeof tokens);
940
941 while ((token = strtok_r (copy, "/", &saveptr)))
942 {
943 copy = NULL;
944
945 /* Make sure ntokens is within bounds. */
946 if (ntokens == ARRAYELTS (tokens))
947 {
948 xfree (copy1);
949 goto fail;
950 }
951
952 tokens[ntokens] = SAFE_ALLOCA (strlen (token) + 1);
953 memcpy (tokens[ntokens], token, strlen (token) + 1);
954 ntokens++;
955 }
956
957 /* Free the copy created for strtok_r. */
958 xfree (copy1);
959
960 /* If there are no tokens, just return the start of the directory
961 tree. */
962
963 if (!ntokens)
964 {
965 SAFE_FREE ();
966
967 /* Return the size of the directory tree as the limit.
968 Do not subtract the initial header bytes, as the limit
969 is an offset from the start of the file. */
970
971 if (limit_return)
972 *limit_return = directory_tree_size;
973
974 return start;
975 }
976
977 /* Loop through tokens, indexing the directory tree each time. */
978
979 for (i = 0; i < ntokens; ++i)
980 {
981 token = tokens[i];
982
983 /* Figure out how many bytes to compare. */
984 token_length = strlen (token);
985
986 again:
987
988 /* If this would be past the directory, return NULL. */
989 if (start + token_length > limit)
990 goto fail;
991
992 /* Now compare the file name. */
993 if (!memcmp (start, token, token_length))
994 {
995 /* They probably match. Find the NULL byte. It must be
996 either one byte past start + token_length, with the last
997 byte a trailing slash (indicating that it is a
998 directory), or just start + token_length. Return 4 bytes
999 past the next NULL byte. */
1000
1001 max = memchr (start, 0, limit - start);
1002
1003 if (max != start + token_length
1004 && !(max == start + token_length + 1
1005 && *(max - 1) == '/'))
1006 goto false_positive;
1007
1008 /* Return it if it exists and is in range, and this is the
1009 last token. Otherwise, set it as start and the limit as
1010 start + the offset and continue the loop. */
1011
1012 if (max && max + 5 <= limit)
1013 {
1014 if (i < ntokens - 1)
1015 {
1016 start = max + 5;
1017 limit = ((char *) directory_tree
1018 + android_extract_long (max + 1));
1019
1020 /* Make sure limit is still in range. */
1021 if (limit > directory_tree + directory_tree_size
1022 || start > directory_tree + directory_tree_size)
1023 goto fail;
1024
1025 continue;
1026 }
1027
1028 /* Now see if max is not a directory and file is. If
1029 file is a directory, then return NULL. */
1030 if (*(max - 1) != '/' && file[strlen (file) - 1] == '/')
1031 max = NULL;
1032 else
1033 {
1034 /* Figure out the limit. */
1035 if (limit_return)
1036 *limit_return = android_extract_long (max + 1);
1037
1038 /* Go to the end of this file. */
1039 max += 5;
1040 }
1041
1042 SAFE_FREE ();
1043 return max;
1044 }
1045
1046 /* Return NULL otherwise. */
1047 __android_log_print (ANDROID_LOG_WARN, __func__,
1048 "could not scan to end of directory tree"
1049 ": %s", file);
1050 goto fail;
1051 }
1052
1053 false_positive:
1054
1055 /* No match was found. Set start to the next sibling and try
1056 again. */
1057
1058 start = memchr (start, 0, limit - start);
1059
1060 if (!start || start + 5 > limit)
1061 goto fail;
1062
1063 start = ((char *) directory_tree
1064 + android_extract_long (start + 1));
1065
1066 /* Make sure start is still in bounds. */
1067
1068 if (start > limit)
1069 goto fail;
1070
1071 /* Continue the loop. */
1072 goto again;
1073 }
1074
1075 fail:
1076 SAFE_FREE ();
1077 return NULL;
1078}
1079
1080/* Return whether or not the directory tree entry DIR is a
1081 directory.
1082
1083 DIR should be a value returned by
1084 `android_scan_directory_tree'. */
1085
1086static bool
1087android_is_directory (const char *dir)
1088{
1089 /* If the directory is the directory tree, then it is a
1090 directory. */
1091 if (dir == directory_tree + 5)
1092 return true;
1093
1094 /* Otherwise, look 5 bytes behind. If it is `/', then it is a
1095 directory. */
1096 return (dir - 6 >= directory_tree
1097 && *(dir - 6) == '/');
1098}
1099
1100
1101
1102/* Intercept USER_FULL_NAME and return something that makes sense if 822/* Intercept USER_FULL_NAME and return something that makes sense if
1103 pw->pw_gecos is NULL. */ 823 pw->pw_gecos is NULL. */
1104 824
@@ -1158,46 +878,106 @@ android_is_special_directory (const char *name, const char *dir)
1158 } 878 }
1159} 879}
1160 880
1161/* Given a real file name, return the part that describes its asset 881#if 0
1162 path, or NULL if it is not an asset.
1163 882
1164 If FILENAME contains only `/assets', return `/' to indicate the 883/* URL-encode N bytes of the specified STRING into at most N bytes of
1165 root of the assets hierarchy. */ 884 BUFFER; STRING is assumed to be encoded in a `utf-8-emacs'
885 compatible coding system. Value is the number of bytes encoded
886 (excluding the trailing null byte placed at the end of the encoded
887 text) or -1 upon failure. */
1166 888
1167static const char * 889static ssize_t
1168android_get_asset_name (const char *filename) 890android_url_encode (const char *restrict string, size_t length,
891 char *restrict buffer, size_t n)
1169{ 892{
1170 const char *name; 893 int len, character;
894 size_t num_encoded;
895 char *end;
896 char format[1 + 25];
1171 897
1172 name = android_is_special_directory (filename, "/assets"); 898 /* For each multibyte character... */
1173 899
1174 if (!name) 900 end = string + length;
1175 return NULL; 901 num_encoded = 0;
1176 902
1177 /* If NAME is empty, return /. Otherwise, return the name relative 903 while (string < end)
1178 to /assets/. */ 904 {
905 /* XXX: Android documentation claims that URIs is encoded
906 according to the ``Unicode'' scheme, but what this means in
907 reality is that the URI is encoded in UTF-8, and then
908 each of its bytes are encoded. */
909 /* Find the length of the multibyte character at STRING. */
910 len = /* multibyte_length (string, end, true, true) */ 1;
911
912 /* 0 means that STRING is not a valid multibyte string. */
913 if (!len || string + len > end)
914 goto failure;
915
916 /* Now fetch the character and increment string. */
917 /* character = /\* STRING_CHAR ((unsigned char *) string) *\/; */
918 character = *(unsigned char *) string;
919 string += len;
920
921 /* If CHARACTER is not a letter or an unreserved character,
922 escape it. */
923
924 if (!((character >= 'A'
925 && character <= 'Z')
926 || (character >= 'a'
927 && character <= 'z')
928 || (character >= '0'
929 && character <= '9')
930 || character == '_'
931 || character == '-'
932 || character == '!'
933 || character == '.'
934 || character == '~'
935 || character == '\''
936 || character == '('
937 || character == ')'
938 || character == '*'))
939 {
940 len = sprintf (format, "%%%X", (unsigned int) character);
941 if (len < 0)
942 goto failure;
1179 943
1180 if (*name) 944 /* See if there is enough space left to hold the encoded
1181 return name; 945 string. */
1182 946
1183 return "/"; 947 if (n < len)
1184} 948 goto failure;
1185 949
1186/* Return whether or not the specified FILENAME actually resolves to a 950 n -= len;
1187 content resolver URI. */ 951 num_encoded += len;
1188 952
1189static bool 953 /* Copy the encoded string to STRING. */
1190android_content_name_p (const char *filename) 954 memcpy (buffer, format, n);
1191{ 955 buffer += len;
1192 /* Content URIs aren't supported before Android 4.4, so return 956 }
1193 false. */ 957 else
958 {
959 /* No more space within BUFFER. */
960 if (!n)
961 goto failure;
962
963 /* Don't encode this ASCII character; just store it. */
964 n--, num_encoded++;
965 *(buffer++) = character;
966 }
967 }
968
969 /* If there's no space for a trailing null byte or more bytes have
970 been encoded than representable in ssize_t, fail. */
971
972 if (!n || num_encoded > SSIZE_MAX)
973 goto failure;
1194 974
1195 if (android_api_level < 19) 975 /* Store the terminating NULL byte. */
1196 return false; 976 *buffer = '\0';
977 return num_encoded;
1197 978
1198 return (android_is_special_directory (filename, 979 failure:
1199 "/content") 980 return -1;
1200 != NULL);
1201} 981}
1202 982
1203/* Return the content URI corresponding to a `/content' file name, 983/* Return the content URI corresponding to a `/content' file name,
@@ -1209,10 +989,9 @@ static const char *
1209android_get_content_name (const char *filename) 989android_get_content_name (const char *filename)
1210{ 990{
1211 static char buffer[PATH_MAX + 1]; 991 static char buffer[PATH_MAX + 1];
1212 char *head, *token, *saveptr, *copy; 992 char *head, *token, *next, *saveptr, *copy, *mark, *mark1;
1213 size_t n; 993 ssize_t rc;
1214 994 size_t n, length;
1215 n = PATH_MAX;
1216 995
1217 /* Find the file name described if it starts with `/content'. If 996 /* Find the file name described if it starts with `/content'. If
1218 just the directory is described, return content://. */ 997 just the directory is described, return content://. */
@@ -1229,742 +1008,147 @@ android_get_content_name (const char *filename)
1229 URI. */ 1008 URI. */
1230 1009
1231 copy = xstrdup (filename); 1010 copy = xstrdup (filename);
1232 token = saveptr = NULL; 1011 mark = saveptr = NULL;
1233 head = stpcpy (buffer, "content:/"); 1012 head = stpcpy (buffer, "content:/");
1234 1013
1235 /* Split FILENAME by slashes. */ 1014 /* Split FILENAME by slashes. */
1236 1015
1237 while ((token = strtok_r (!token ? copy : NULL, 1016 token = strtok_r (copy, "/", &saveptr);
1238 "/", &saveptr)))
1239 {
1240 head = stpncpy (head, "/", n--);
1241 head = stpncpy (head, token, n);
1242
1243 /* Check that head has not overflown the buffer. */
1244 eassert ((head - buffer) <= PATH_MAX);
1245
1246 n = PATH_MAX - (head - buffer);
1247 }
1248
1249 /* Make sure the given buffer ends up NULL terminated. */
1250 buffer[PATH_MAX] = '\0';
1251 xfree (copy);
1252
1253 return buffer;
1254}
1255
1256/* Return whether or not the specified FILENAME is an accessible
1257 content URI. MODE specifies what to check. */
1258
1259static bool
1260android_check_content_access (const char *filename, int mode)
1261{
1262 const char *name;
1263 jobject string;
1264 size_t length;
1265 jboolean rc;
1266
1267 name = android_get_content_name (filename);
1268 length = strlen (name);
1269
1270 string = (*android_java_env)->NewByteArray (android_java_env,
1271 length);
1272 android_exception_check ();
1273
1274 (*android_java_env)->SetByteArrayRegion (android_java_env,
1275 string, 0, length,
1276 (jbyte *) name);
1277 rc = (*android_java_env)->CallBooleanMethod (android_java_env,
1278 emacs_service,
1279 service_class.check_content_uri,
1280 string,
1281 (jboolean) ((mode & R_OK)
1282 != 0),
1283 (jboolean) ((mode & W_OK)
1284 != 0));
1285 android_exception_check_1 (string);
1286 ANDROID_DELETE_LOCAL_REF (string);
1287
1288 return rc;
1289}
1290
1291/* Like fstat. However, look up the asset corresponding to the file
1292 descriptor. If it exists, return the right information. */
1293
1294int
1295android_fstat (int fd, struct stat *statb)
1296{
1297 if (fd < ANDROID_MAX_ASSET_FD
1298 && (android_table[fd].flags
1299 & ANDROID_FD_TABLE_ENTRY_IS_VALID))
1300 {
1301 memcpy (statb, &android_table[fd].statb,
1302 sizeof *statb);
1303 return 0;
1304 }
1305
1306 return fstat (fd, statb);
1307}
1308
1309static int android_lookup_asset_directory_fd (int,
1310 const char *restrict *,
1311 const char *restrict);
1312
1313/* Like fstatat. However, if dirfd is AT_FDCWD and PATHNAME is an
1314 asset, find the information for the corresponding asset, and if
1315 dirfd is an offset into directory_tree as returned by
1316 `android_dirfd', find the information within the corresponding
1317 directory tree entry. */
1318
1319int
1320android_fstatat (int dirfd, const char *restrict pathname,
1321 struct stat *restrict statbuf, int flags)
1322{
1323 AAsset *asset_desc;
1324 const char *asset;
1325 const char *asset_dir;
1326 int fd, rc;
1327
1328 /* Look up whether or not DIRFD belongs to an open struct
1329 android_dir. */
1330
1331 if (dirfd != AT_FDCWD)
1332 dirfd
1333 = android_lookup_asset_directory_fd (dirfd, &pathname,
1334 pathname);
1335
1336 if (dirfd == AT_FDCWD
1337 && asset_manager
1338 && (asset = android_get_asset_name (pathname)))
1339 {
1340 /* Look up whether or not PATHNAME happens to be a
1341 directory. */
1342 asset_dir = android_scan_directory_tree ((char *) asset,
1343 NULL);
1344
1345 if (!asset_dir)
1346 {
1347 errno = ENOENT;
1348 return -1;
1349 }
1350
1351 if (android_is_directory (asset_dir))
1352 {
1353 memset (statbuf, 0, sizeof *statbuf);
1354
1355 /* Fill in the stat buffer. */
1356 statbuf->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
1357 return 0;
1358 }
1359
1360 /* AASSET_MODE_STREAMING is fastest here. */
1361 asset_desc = AAssetManager_open (asset_manager, asset,
1362 AASSET_MODE_STREAMING);
1363
1364 if (!asset_desc)
1365 return ENOENT;
1366
1367 memset (statbuf, 0, sizeof *statbuf);
1368
1369 /* Fill in the stat buffer. */
1370 statbuf->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
1371 statbuf->st_size = AAsset_getLength (asset_desc);
1372
1373 /* Close the asset. */
1374 AAsset_close (asset_desc);
1375 return 0;
1376 }
1377
1378 if (dirfd == AT_FDCWD
1379 && android_init_gui
1380 && android_content_name_p (pathname))
1381 {
1382 /* This is actually a content:// URI. Open that file and call
1383 stat on it. */
1384
1385 fd = android_open (pathname, O_RDONLY, 0);
1386
1387 if (fd < 0)
1388 return -1;
1389
1390 rc = fstat (fd, statbuf);
1391 android_close (fd);
1392 return rc;
1393 }
1394
1395 return fstatat (dirfd, pathname, statbuf, flags);
1396}
1397
1398/* Return if NAME, a file name relative to the /assets directory, is
1399 accessible, as long as !(amode & W_OK). */
1400
1401static bool
1402android_file_access_p (const char *name, int amode)
1403{
1404 if (!asset_manager)
1405 return false;
1406
1407 if (!(amode & W_OK))
1408 {
1409 if (!strcmp (name, "") || !strcmp (name, "/"))
1410 /* /assets always exists. */
1411 return true;
1412
1413 /* Check if the file exists by looking in the ``directory tree''
1414 asset generated during the build process. This is used
1415 instead of the AAsset functions, because the latter are
1416 buggy and treat directories inconsistently. */
1417 return android_scan_directory_tree ((char *) name, NULL) != NULL;
1418 }
1419
1420 return false;
1421}
1422
1423/* Do the same as android_hack_asset_fd, but use an unlinked temporary
1424 file to cater to old Android kernels where ashmem files are not
1425 readable. */
1426
1427static int
1428android_hack_asset_fd_fallback (AAsset *asset)
1429{
1430 int fd;
1431 char filename[PATH_MAX];
1432 size_t size;
1433 void *mem;
1434
1435 /* Assets must be small enough to fit in size_t, if off_t is
1436 larger. */
1437 size = AAsset_getLength (asset);
1438
1439 /* Get an unlinked file descriptor from a file in the cache
1440 directory, which is guaranteed to only be written to by Emacs.
1441 Creating an ashmem file descriptor and reading from it doesn't
1442 work on these old Android versions. */
1443
1444 snprintf (filename, PATH_MAX, "%s/temp~unlinked.%d",
1445 android_cache_dir, getpid ());
1446 fd = open (filename, O_CREAT | O_RDWR | O_TRUNC,
1447 S_IRUSR | S_IWUSR);
1448 1017
1449 if (fd < 0) 1018 while (token)
1450 return -1;
1451
1452 if (unlink (filename))
1453 goto fail;
1454
1455 if (ftruncate (fd, size))
1456 goto fail;
1457
1458 mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0);
1459 if (mem == MAP_FAILED)
1460 { 1019 {
1461 __android_log_print (ANDROID_LOG_ERROR, __func__, 1020 /* Compute the number of bytes remaining in buffer excluding a
1462 "mmap: %s", strerror (errno)); 1021 trailing null byte. */
1463 goto fail; 1022 n = PATH_MAX - (head - buffer);
1464 }
1465
1466 if (AAsset_read (asset, mem, size) != size)
1467 {
1468 /* Too little was read. Close the file descriptor and
1469 report an error. */
1470 __android_log_print (ANDROID_LOG_ERROR, __func__,
1471 "AAsset_read: %s", strerror (errno));
1472 goto fail;
1473 }
1474
1475 munmap (mem, size);
1476 return fd;
1477
1478 fail:
1479 close (fd);
1480 return -1;
1481}
1482
1483/* Pointer to the `ASharedMemory_create' function which is loaded
1484 dynamically. */
1485static int (*asharedmemory_create) (const char *, size_t);
1486 1023
1487/* Return whether or not shared memory file descriptors can also be 1024 /* Write / to the buffer. Return failure if there is no space
1488 read from, and are thus suitable for creating asset files. 1025 for it. */
1489 1026
1490 This does not work on some ancient Android systems running old 1027 if (!n)
1491 versions of the kernel. */ 1028 goto failure;
1492 1029
1493static bool 1030 *head++ = '/';
1494android_detect_ashmem (void) 1031 n--;
1495{
1496 int fd, rc;
1497 void *mem;
1498 char test_buffer[10];
1499 1032
1500 memcpy (test_buffer, "abcdefghi", 10); 1033 /* Find the next token now. */
1034 next = strtok_r (NULL, "/", &saveptr);
1501 1035
1502 /* Create the file descriptor to be used for the test. */ 1036 /* Detect and avoid encoding an encoded URL query affixed to the
1037 end of the last component within the content file name.
1503 1038
1504 /* Android 28 and earlier let Emacs access /dev/ashmem directly, so 1039 Content URIs can include a query describing parameters that
1505 prefer that over using ASharedMemory. */ 1040 must be provided to the content provider. They are separated
1041 from the rest of the URI by a single question mark character,
1042 which should not be encoded.
1506 1043
1507 if (android_api_level <= 28) 1044 However, the distinction between the separator and question
1508 { 1045 marks that appear inside file name components is lost when a
1509 fd = open ("/dev/ashmem", O_RDWR); 1046 content URI is decoded into a content path. To compensate
1047 for this loss of information, Emacs assumes that the last
1048 question mark is always a URI separator, and suffixes content
1049 file names which contain question marks with a trailing
1050 question mark. */
1510 1051
1511 if (fd < 0) 1052 if (!next)
1512 return false;
1513
1514 /* An empty name means the memory area will exist until the file
1515 descriptor is closed, because no other process can
1516 attach. */
1517 rc = ioctl (fd, ASHMEM_SET_NAME, "");
1518
1519 if (rc < 0)
1520 { 1053 {
1521 close (fd); 1054 /* Find the last question mark character. */
1522 return false;
1523 }
1524 1055
1525 rc = ioctl (fd, ASHMEM_SET_SIZE, sizeof test_buffer); 1056 mark1 = strchr (token, '?');
1526 1057
1527 if (rc < 0) 1058 while (mark1)
1528 {
1529 close (fd);
1530 return false;
1531 }
1532 }
1533 else
1534 {
1535 /* On the other hand, SELinux restrictions on Android 29 and
1536 later require that Emacs use a system service to obtain
1537 shared memory. Load this dynamically, as this service is not
1538 available on all versions of the NDK. */
1539
1540 if (!asharedmemory_create)
1541 {
1542 *(void **) (&asharedmemory_create)
1543 = dlsym (RTLD_DEFAULT, "ASharedMemory_create");
1544
1545 if (!asharedmemory_create)
1546 { 1059 {
1547 __android_log_print (ANDROID_LOG_FATAL, __func__, 1060 mark = mark1;
1548 "dlsym: %s\n", 1061 mark1 = strchr (mark + 1, '?');
1549 strerror (errno));
1550 emacs_abort ();
1551 } 1062 }
1552 } 1063 }
1553 1064
1554 fd = asharedmemory_create ("", sizeof test_buffer); 1065 if (mark)
1555
1556 if (fd < 0)
1557 return false;
1558 }
1559
1560 /* Now map the resource and write the test contents. */
1561
1562 mem = mmap (NULL, sizeof test_buffer, PROT_WRITE,
1563 MAP_SHARED, fd, 0);
1564 if (mem == MAP_FAILED)
1565 {
1566 close (fd);
1567 return false;
1568 }
1569
1570 /* Copy over the test contents. */
1571 memcpy (mem, test_buffer, sizeof test_buffer);
1572
1573 /* Return anyway even if munmap fails. */
1574 munmap (mem, sizeof test_buffer);
1575
1576 /* Try to read the content back into test_buffer. If this does not
1577 compare equal to the original string, or the read fails, then
1578 ashmem descriptors are not readable on this system. */
1579
1580 if ((read (fd, test_buffer, sizeof test_buffer)
1581 != sizeof test_buffer)
1582 || memcmp (test_buffer, "abcdefghi", sizeof test_buffer))
1583 {
1584 __android_log_print (ANDROID_LOG_WARN, __func__,
1585 "/dev/ashmem does not produce real"
1586 " temporary files on this system, so"
1587 " Emacs will fall back to creating"
1588 " unlinked temporary files.");
1589 close (fd);
1590 return false;
1591 }
1592
1593 close (fd);
1594 return true;
1595}
1596
1597/* Get a file descriptor backed by a temporary in-memory file for the
1598 given asset. */
1599
1600static int
1601android_hack_asset_fd (AAsset *asset)
1602{
1603 static bool ashmem_readable_p;
1604 static bool ashmem_initialized;
1605 int fd, rc;
1606 unsigned char *mem;
1607 size_t size;
1608
1609 /* The first time this function is called, try to determine whether
1610 or not ashmem file descriptors can be read from. */
1611
1612 if (!ashmem_initialized)
1613 ashmem_readable_p
1614 = android_detect_ashmem ();
1615 ashmem_initialized = true;
1616
1617 /* If it isn't, fall back. */
1618
1619 if (!ashmem_readable_p)
1620 return android_hack_asset_fd_fallback (asset);
1621
1622 /* Assets must be small enough to fit in size_t, if off_t is
1623 larger. */
1624 size = AAsset_getLength (asset);
1625
1626 /* Android 28 and earlier let Emacs access /dev/ashmem directly, so
1627 prefer that over using ASharedMemory. */
1628
1629 if (android_api_level <= 28)
1630 {
1631 fd = open ("/dev/ashmem", O_RDWR);
1632
1633 if (fd < 0)
1634 return -1;
1635
1636 /* An empty name means the memory area will exist until the file
1637 descriptor is closed, because no other process can
1638 attach. */
1639 rc = ioctl (fd, ASHMEM_SET_NAME, "");
1640
1641 if (rc < 0)
1642 { 1066 {
1643 __android_log_print (ANDROID_LOG_ERROR, __func__, 1067 /* First, encode the part leading to the question mark
1644 "ioctl ASHMEM_SET_NAME: %s", 1068 character. */
1645 strerror (errno));
1646 close (fd);
1647 return -1;
1648 }
1649 1069
1650 rc = ioctl (fd, ASHMEM_SET_SIZE, size); 1070 rc = 0;
1651 1071 if (mark > token)
1652 if (rc < 0) 1072 rc = android_url_encode (token, mark - token,
1653 { 1073 head, n + 1);
1654 __android_log_print (ANDROID_LOG_ERROR, __func__,
1655 "ioctl ASHMEM_SET_SIZE: %s",
1656 strerror (errno));
1657 close (fd);
1658 return -1;
1659 }
1660 1074
1661 if (!size) 1075 /* If this fails, bail out. */
1662 return fd;
1663 1076
1664 /* Now map the resource. */ 1077 if (rc < 0)
1665 mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0); 1078 goto failure;
1666 if (mem == MAP_FAILED)
1667 {
1668 __android_log_print (ANDROID_LOG_ERROR, __func__,
1669 "mmap: %s", strerror (errno));
1670 close (fd);
1671 return -1;
1672 }
1673 1079
1674 if (AAsset_read (asset, mem, size) != size) 1080 /* Copy mark to the file name. */
1675 {
1676 /* Too little was read. Close the file descriptor and
1677 report an error. */
1678 __android_log_print (ANDROID_LOG_ERROR, __func__,
1679 "AAsset_read: %s", strerror (errno));
1680 close (fd);
1681 return -1;
1682 }
1683 1081
1684 /* Return anyway even if munmap fails. */ 1082 n -= rc, head += rc;
1685 munmap (mem, size); 1083 length = strlen (mark);
1686 return fd;
1687 }
1688 1084
1689 /* On the other hand, SELinux restrictions on Android 29 and later 1085 if (n < length)
1690 require that Emacs use a system service to obtain shared memory. 1086 goto failure;
1691 Load this dynamically, as this service is not available on all
1692 versions of the NDK. */
1693 1087
1694 if (!asharedmemory_create) 1088 strcpy (head, mark);
1695 {
1696 *(void **) (&asharedmemory_create)
1697 = dlsym (RTLD_DEFAULT, "ASharedMemory_create");
1698 1089
1699 if (!asharedmemory_create) 1090 /* Now break out of the loop, since this is the last
1700 { 1091 component anyway. */
1701 __android_log_print (ANDROID_LOG_FATAL, __func__, 1092 break;
1702 "dlsym: %s\n",
1703 strerror (errno));
1704 emacs_abort ();
1705 } 1093 }
1706 } 1094 else
1707 1095 /* Now encode this file name component into the buffer. */
1708 fd = asharedmemory_create ("", size); 1096 rc = android_url_encode (token, strlen (token),
1709 1097 head, n + 1);
1710 if (fd < 0)
1711 {
1712 __android_log_print (ANDROID_LOG_ERROR, __func__,
1713 "ASharedMemory_create: %s",
1714 strerror (errno));
1715 return -1;
1716 }
1717
1718 /* Now map the resource. */
1719 mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0);
1720 if (mem == MAP_FAILED)
1721 {
1722 __android_log_print (ANDROID_LOG_ERROR, __func__,
1723 "mmap: %s", strerror (errno));
1724 close (fd);
1725 return -1;
1726 }
1727
1728 if (AAsset_read (asset, mem, size) != size)
1729 {
1730 /* Too little was read. Close the file descriptor and
1731 report an error. */
1732 __android_log_print (ANDROID_LOG_ERROR, __func__,
1733 "AAsset_read: %s", strerror (errno));
1734 close (fd);
1735 return -1;
1736 }
1737
1738 /* Return anyway even if munmap fails. */
1739 munmap (mem, size);
1740 return fd;
1741}
1742
1743/* Make FD close-on-exec. If any system call fails, do not abort, but
1744 log a warning to the system log. */
1745
1746static void
1747android_close_on_exec (int fd)
1748{
1749 int flags, rc;
1750 1098
1751 flags = fcntl (fd, F_GETFD); 1099 if (rc < 0)
1100 goto failure;
1752 1101
1753 if (flags < 0) 1102 head += rc;
1754 { 1103 token = next;
1755 __android_log_print (ANDROID_LOG_WARN, __func__,
1756 "fcntl: %s", strerror (errno));
1757 return;
1758 } 1104 }
1759 1105
1760 rc = fcntl (fd, F_SETFD, flags | O_CLOEXEC); 1106 /* buffer must have been null terminated by
1107 `android_url_encode'. */
1108 xfree (copy);
1109 return buffer;
1761 1110
1762 if (rc < 0) 1111 failure:
1763 { 1112 xfree (copy);
1764 __android_log_print (ANDROID_LOG_WARN, __func__, 1113 return NULL;
1765 "fcntl: %s", strerror (errno));
1766 return;
1767 }
1768} 1114}
1769 1115
1770/* `open' and such are modified even though they exist on Android, 1116/* Return whether or not the specified FILENAME is an accessible
1771 because Emacs treats "/assets/" as a special directory that must 1117 content URI. MODE specifies what to check. */
1772 contain all assets in the application package. */
1773 1118
1774int 1119static bool
1775android_open (const char *filename, int oflag, mode_t mode) 1120android_check_content_access (const char *filename, int mode)
1776{ 1121{
1777 const char *name; 1122 const char *name;
1778 AAsset *asset;
1779 int fd;
1780 size_t length;
1781 jobject string; 1123 jobject string;
1124 size_t length;
1125 jboolean rc;
1782 1126
1783 if (asset_manager && (name = android_get_asset_name (filename))) 1127 name = android_get_content_name (filename);
1784 { 1128 length = strlen (name);
1785 /* If Emacs is trying to write to the file, return NULL. */
1786
1787 if (oflag & O_WRONLY || oflag & O_RDWR)
1788 {
1789 errno = EROFS;
1790 return -1;
1791 }
1792
1793 if (oflag & O_DIRECTORY)
1794 {
1795 errno = ENOTSUP;
1796 return -1;
1797 }
1798
1799 /* If given AASSET_MODE_BUFFER (which is what Emacs probably
1800 does, given that a file descriptor is not always available),
1801 the framework fails to uncompress the data before it returns
1802 a file descriptor. */
1803 asset = AAssetManager_open (asset_manager, name,
1804 AASSET_MODE_STREAMING);
1805
1806 if (!asset)
1807 {
1808 errno = ENOENT;
1809 return -1;
1810 }
1811
1812 /* Create a shared memory file descriptor containing the asset
1813 contents.
1814
1815 The documentation misleads people into thinking that
1816 AAsset_openFileDescriptor does precisely this. However, it
1817 instead returns an offset into any uncompressed assets in the
1818 ZIP archive. This cannot be found in its documentation. */
1819
1820 fd = android_hack_asset_fd (asset);
1821
1822 if (fd == -1)
1823 {
1824 AAsset_close (asset);
1825 errno = ENXIO;
1826 return -1;
1827 }
1828
1829 /* If O_CLOEXEC is specified, make the file descriptor close on
1830 exec too. */
1831 if (oflag & O_CLOEXEC)
1832 android_close_on_exec (fd);
1833
1834 if (fd >= ANDROID_MAX_ASSET_FD || fd < 0)
1835 {
1836 /* Too bad. Pretend this is an out of memory error. */
1837 errno = ENOMEM;
1838
1839 if (fd >= 0)
1840 close (fd);
1841
1842 fd = -1;
1843 }
1844 else
1845 {
1846 assert (!(android_table[fd].flags
1847 & ANDROID_FD_TABLE_ENTRY_IS_VALID));
1848 android_table[fd].flags = ANDROID_FD_TABLE_ENTRY_IS_VALID;
1849 memset (&android_table[fd].statb, 0,
1850 sizeof android_table[fd].statb);
1851
1852 /* Fill in some information that will be reported to
1853 callers of android_fstat, among others. */
1854 android_table[fd].statb.st_mode
1855 = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
1856
1857 /* Owned by root. */
1858 android_table[fd].statb.st_uid = 0;
1859 android_table[fd].statb.st_gid = 0;
1860
1861 /* Size of the file. */
1862 android_table[fd].statb.st_size
1863 = AAsset_getLength (asset);
1864 }
1865
1866 AAsset_close (asset);
1867 return fd;
1868 }
1869
1870 if (android_init_gui && android_content_name_p (filename))
1871 {
1872 /* This is a content:// URI. Ask the system for a descriptor to
1873 that file. */
1874
1875 name = android_get_content_name (filename);
1876 length = strlen (name);
1877
1878 /* Check if the mode is valid. */
1879
1880 if (oflag & O_DIRECTORY)
1881 {
1882 errno = ENOTSUP;
1883 return -1;
1884 }
1885
1886 /* Allocate a buffer to hold the file name. */
1887 string = (*android_java_env)->NewByteArray (android_java_env,
1888 length);
1889 if (!string)
1890 {
1891 (*android_java_env)->ExceptionClear (android_java_env);
1892 errno = ENOMEM;
1893 return -1;
1894 }
1895 (*android_java_env)->SetByteArrayRegion (android_java_env,
1896 string, 0, length,
1897 (jbyte *) name);
1898
1899 /* Try to open the file descriptor. */
1900
1901 fd
1902 = (*android_java_env)->CallIntMethod (android_java_env,
1903 emacs_service,
1904 service_class.open_content_uri,
1905 string,
1906 (jboolean) ((mode & O_WRONLY
1907 || mode & O_RDWR)
1908 != 0),
1909 (jboolean) !(mode & O_WRONLY),
1910 (jboolean) ((mode & O_TRUNC)
1911 != 0));
1912
1913 if ((*android_java_env)->ExceptionCheck (android_java_env))
1914 {
1915 (*android_java_env)->ExceptionClear (android_java_env);
1916 errno = ENOMEM;
1917 ANDROID_DELETE_LOCAL_REF (string);
1918 return -1;
1919 }
1920
1921 /* If fd is -1, just assume that the file does not exist,
1922 and return -1 with errno set to ENOENT. */
1923
1924 if (fd == -1)
1925 {
1926 errno = ENOENT;
1927 goto skip;
1928 }
1929
1930 if (mode & O_CLOEXEC)
1931 android_close_on_exec (fd);
1932
1933 skip:
1934 ANDROID_DELETE_LOCAL_REF (string);
1935 return fd;
1936 }
1937
1938 return open (filename, oflag, mode);
1939}
1940 1129
1941/* Like close. However, remove the file descriptor from the asset 1130 string = (*android_java_env)->NewByteArray (android_java_env,
1942 table as well. */ 1131 length);
1132 android_exception_check ();
1943 1133
1944int 1134 (*android_java_env)->SetByteArrayRegion (android_java_env,
1945android_close (int fd) 1135 string, 0, length,
1946{ 1136 (jbyte *) name);
1947 if (fd < ANDROID_MAX_ASSET_FD) 1137 rc = (*android_java_env)->CallBooleanMethod (android_java_env,
1948 android_table[fd].flags = 0; 1138 emacs_service,
1139 service_class.check_content_uri,
1140 string,
1141 (jboolean) ((mode & R_OK)
1142 != 0),
1143 (jboolean) ((mode & W_OK)
1144 != 0));
1145 android_exception_check_1 (string);
1146 ANDROID_DELETE_LOCAL_REF (string);
1949 1147
1950 return close (fd); 1148 return rc;
1951} 1149}
1952 1150
1953/* Like fclose. However, remove any information associated with 1151#endif /* 0 */
1954 FILE's file descriptor from the asset table as well. */
1955
1956int
1957android_fclose (FILE *stream)
1958{
1959 int fd;
1960
1961 fd = fileno (stream);
1962
1963 if (fd != -1 && fd < ANDROID_MAX_ASSET_FD)
1964 android_table[fd].flags = 0;
1965
1966 return fclose (stream);
1967}
1968 1152
1969/* Return the current user's ``home'' directory, which is actually the 1153/* Return the current user's ``home'' directory, which is actually the
1970 app data directory on Android. */ 1154 app data directory on Android. */
@@ -2015,7 +1199,6 @@ android_create_lib_link (void)
2015 char *filename; 1199 char *filename;
2016 char lib_directory[PATH_MAX]; 1200 char lib_directory[PATH_MAX];
2017 int fd; 1201 int fd;
2018 struct stat statb;
2019 1202
2020 /* Find the directory containing the files directory. */ 1203 /* Find the directory containing the files directory. */
2021 filename = dirname (android_files_dir); 1204 filename = dirname (android_files_dir);
@@ -2105,16 +1288,8 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
2105 int pipefd[2]; 1288 int pipefd[2];
2106 pthread_t thread; 1289 pthread_t thread;
2107 const char *java_string; 1290 const char *java_string;
2108 AAsset *asset;
2109 1291
2110 /* This may be called from multiple threads. setEmacsParams should 1292 /* This function should only be called from the main thread. */
2111 only ever be called once. */
2112 if (__atomic_fetch_add (&emacs_initialized, -1, __ATOMIC_SEQ_CST))
2113 {
2114 ANDROID_THROW (env, "java/lang/IllegalArgumentException",
2115 "Emacs was already initialized!");
2116 return;
2117 }
2118 1293
2119 android_pixel_density_x = pixel_density_x; 1294 android_pixel_density_x = pixel_density_x;
2120 android_pixel_density_y = pixel_density_y; 1295 android_pixel_density_y = pixel_density_y;
@@ -2124,40 +1299,6 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
2124 "Initializing "PACKAGE_STRING"...\nPlease report bugs to " 1299 "Initializing "PACKAGE_STRING"...\nPlease report bugs to "
2125 PACKAGE_BUGREPORT". Thanks.\n"); 1300 PACKAGE_BUGREPORT". Thanks.\n");
2126 1301
2127 /* Set the asset manager. */
2128 asset_manager = AAssetManager_fromJava (env, local_asset_manager);
2129
2130 /* Initialize the directory tree. */
2131 asset = AAssetManager_open (asset_manager, "directory-tree",
2132 AASSET_MODE_BUFFER);
2133
2134 if (!asset)
2135 {
2136 __android_log_print (ANDROID_LOG_FATAL, __func__,
2137 "Failed to open directory tree");
2138 emacs_abort ();
2139 }
2140
2141 directory_tree = AAsset_getBuffer (asset);
2142
2143 if (!directory_tree)
2144 emacs_abort ();
2145
2146 /* Now figure out how big the directory tree is, and compare the
2147 first few bytes. */
2148 directory_tree_size = AAsset_getLength (asset);
2149 if (directory_tree_size < 5
2150 || memcmp (directory_tree, "EMACS", 5))
2151 {
2152 __android_log_print (ANDROID_LOG_FATAL, __func__,
2153 "Directory tree has bad magic");
2154 emacs_abort ();
2155 }
2156
2157 /* Hold a VM reference to the asset manager to prevent the native
2158 object from being deleted. */
2159 (*env)->NewGlobalRef (env, local_asset_manager);
2160
2161 if (emacs_service_object) 1302 if (emacs_service_object)
2162 { 1303 {
2163 /* Create a pipe and duplicate it to stdout and stderr. Next, 1304 /* Create a pipe and duplicate it to stdout and stderr. Next,
@@ -2304,7 +1445,10 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
2304 /* Set up events. */ 1445 /* Set up events. */
2305 android_init_events (); 1446 android_init_events ();
2306 1447
2307 /* OK, setup is now complete. The caller may start the Emacs thread 1448 /* Set up the Android virtual filesystem layer. */
1449 android_vfs_init (env, local_asset_manager);
1450
1451 /* OK, setup is now complete. The caller may call initEmacs
2308 now. */ 1452 now. */
2309} 1453}
2310 1454
@@ -2405,6 +1549,33 @@ android_init_emacs_service (void)
2405 "Landroid/view/inputmethod/ExtractedText;I)V"); 1549 "Landroid/view/inputmethod/ExtractedText;I)V");
2406 FIND_METHOD (update_cursor_anchor_info, "updateCursorAnchorInfo", 1550 FIND_METHOD (update_cursor_anchor_info, "updateCursorAnchorInfo",
2407 "(Lorg/gnu/emacs/EmacsWindow;FFFF)V"); 1551 "(Lorg/gnu/emacs/EmacsWindow;FFFF)V");
1552 FIND_METHOD (get_document_authorities, "getDocumentAuthorities",
1553 "()[Ljava/lang/String;");
1554 FIND_METHOD (request_directory_access, "requestDirectoryAccess",
1555 "()I");
1556 FIND_METHOD (get_document_trees, "getDocumentTrees",
1557 "([B)[Ljava/lang/String;");
1558 FIND_METHOD (document_id_from_name, "documentIdFromName",
1559 "(Ljava/lang/String;[B[Ljava/lang/String;)I");
1560 FIND_METHOD (get_tree_uri, "getTreeUri",
1561 "(Ljava/lang/String;Ljava/lang/String;)"
1562 "Ljava/lang/String;");
1563 FIND_METHOD (stat_document, "statDocument",
1564 "(Ljava/lang/String;Ljava/lang/String;)[J");
1565 FIND_METHOD (access_document, "accessDocument",
1566 "(Ljava/lang/String;Ljava/lang/String;Z)I");
1567 FIND_METHOD (open_document_directory, "openDocumentDirectory",
1568 "(Ljava/lang/String;Ljava/lang/String;)"
1569 "Landroid/database/Cursor;");
1570 FIND_METHOD (read_directory_entry, "readDirectoryEntry",
1571 "(Landroid/database/Cursor;)Lorg/gnu/emacs/"
1572 "EmacsDirectoryEntry;");
1573 FIND_METHOD (open_document, "openDocument",
1574 "(Ljava/lang/String;Ljava/lang/String;ZZ)"
1575 "Landroid/os/ParcelFileDescriptor;");
1576 FIND_METHOD (create_document, "createDocument",
1577 "(Ljava/lang/String;Ljava/lang/String;"
1578 "Ljava/lang/String;)Ljava/lang/String;");
2408#undef FIND_METHOD 1579#undef FIND_METHOD
2409} 1580}
2410 1581
@@ -6255,329 +5426,6 @@ android_toggle_on_screen_keyboard (android_window window, bool show)
6255 5426
6256 5427
6257 5428
6258/* Like faccessat, except it also understands DIRFD opened using
6259 android_dirfd. */
6260
6261int
6262android_faccessat (int dirfd, const char *pathname, int mode, int flags)
6263{
6264 const char *asset;
6265
6266 if (dirfd != AT_FDCWD)
6267 dirfd
6268 = android_lookup_asset_directory_fd (dirfd, &pathname,
6269 pathname);
6270
6271 /* Check if pathname is actually an asset. If that is the case,
6272 simply fall back to android_file_access_p. */
6273
6274 if (dirfd == AT_FDCWD
6275 && asset_manager
6276 && (asset = android_get_asset_name (pathname)))
6277 {
6278 if (android_file_access_p (asset, mode))
6279 return 0;
6280
6281 /* Set errno to an appropriate value. */
6282 errno = ENOENT;
6283 return 1;
6284 }
6285
6286 /* Check if pathname is actually a content resolver URI. */
6287
6288 if (dirfd == AT_FDCWD
6289 && android_init_gui
6290 && android_content_name_p (pathname))
6291 {
6292 if (android_check_content_access (pathname, mode))
6293 return 0;
6294
6295 /* Set errno to an appropriate value. */
6296 errno = ENOENT;
6297 return 1;
6298 }
6299
6300#if __ANDROID_API__ >= 16
6301 /* When calling `faccessat', make sure to clear the flag AT_EACCESS.
6302
6303 Android's faccessat simply fails if FLAGS contains AT_EACCESS, so
6304 replace it with zero here. This isn't caught at configuration-time
6305 as Emacs is being cross compiled.
6306
6307 This takes place only when building for Android 16 and later,
6308 because earlier versions use a Gnulib replacement that lacks these
6309 issues. */
6310
6311 return faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS);
6312#else /* __ANDROID_API__ < 16 */
6313 return faccessat (dirfd, pathname, mode, flags);
6314#endif /* __ANDROID_API__ >= 16 */
6315}
6316
6317
6318
6319/* Directory listing emulation. */
6320
6321struct android_dir
6322{
6323 /* The real DIR *, if it exists. */
6324 DIR *dir;
6325
6326 /* Otherwise, the pointer to the directory in directory_tree. */
6327 char *asset_dir;
6328
6329 /* And the end of the files in asset_dir. */
6330 char *asset_limit;
6331
6332 /* The next struct android_dir. */
6333 struct android_dir *next;
6334
6335 /* Path to the directory relative to /. */
6336 char *asset_file;
6337
6338 /* File descriptor used when asset_dir is set. */
6339 int fd;
6340};
6341
6342/* List of all struct android_dir's corresponding to an asset
6343 directory that are currently open. */
6344static struct android_dir *android_dirs;
6345
6346/* Like opendir. However, return an asset directory if NAME points to
6347 an asset. */
6348
6349struct android_dir *
6350android_opendir (const char *name)
6351{
6352 struct android_dir *dir;
6353 char *asset_dir;
6354 const char *asset_name;
6355 size_t limit, length;
6356
6357 asset_name = android_get_asset_name (name);
6358
6359 /* If the asset manager exists and NAME is an asset, return an asset
6360 directory. */
6361 if (asset_manager && asset_name)
6362 {
6363 asset_dir
6364 = (char *) android_scan_directory_tree ((char *) asset_name,
6365 &limit);
6366
6367 if (!asset_dir)
6368 {
6369 errno = ENOENT;
6370 return NULL;
6371 }
6372
6373 length = strlen (name);
6374
6375 dir = xmalloc (sizeof *dir);
6376 dir->dir = NULL;
6377 dir->asset_dir = asset_dir;
6378 dir->asset_limit = (char *) directory_tree + limit;
6379 dir->fd = -1;
6380 dir->asset_file = xzalloc (length + 2);
6381
6382 /* Make sure dir->asset_file is terminated with /. */
6383 strcpy (dir->asset_file, name);
6384 if (dir->asset_file[length - 1] != '/')
6385 dir->asset_file[length] = '/';
6386
6387 /* Make sure dir->asset_limit is within bounds. It is a limit,
6388 and as such can be exactly one byte past directory_tree. */
6389 if (dir->asset_limit > directory_tree + directory_tree_size)
6390 {
6391 xfree (dir);
6392 __android_log_print (ANDROID_LOG_VERBOSE, __func__,
6393 "Invalid dir tree, limit %zu, size %zu\n",
6394 limit, directory_tree_size);
6395 dir = NULL;
6396 errno = EACCES;
6397 }
6398
6399 dir->next = android_dirs;
6400 android_dirs = dir;
6401
6402 return dir;
6403 }
6404
6405 /* Otherwise, open the directory normally. */
6406 dir = xmalloc (sizeof *dir);
6407 dir->asset_dir = NULL;
6408 dir->dir = opendir (name);
6409
6410 if (!dir->dir)
6411 {
6412 xfree (dir);
6413 return NULL;
6414 }
6415
6416 return dir;
6417}
6418
6419/* Like dirfd. However, value is not a real directory file descriptor
6420 if DIR is an asset directory. */
6421
6422int
6423android_dirfd (struct android_dir *dirp)
6424{
6425 int fd;
6426
6427 if (dirp->dir)
6428 return dirfd (dirp->dir);
6429 else if (dirp->fd != -1)
6430 return dirp->fd;
6431
6432 fd = open ("/dev/null", O_RDONLY | O_CLOEXEC);
6433
6434 /* Record this file descriptor in dirp. */
6435 dirp->fd = fd;
6436 return fd;
6437}
6438
6439/* Like readdir, except it understands asset directories. */
6440
6441struct dirent *
6442android_readdir (struct android_dir *dir)
6443{
6444 static struct dirent dirent;
6445 const char *last;
6446
6447 if (dir->asset_dir)
6448 {
6449 /* There are no more files to read. */
6450 if (dir->asset_dir >= dir->asset_limit)
6451 return NULL;
6452
6453 /* Otherwise, scan forward looking for the next NULL byte. */
6454 last = memchr (dir->asset_dir, 0,
6455 dir->asset_limit - dir->asset_dir);
6456
6457 /* No more NULL bytes remain. */
6458 if (!last)
6459 return NULL;
6460
6461 /* Forward last past the NULL byte. */
6462 last++;
6463
6464 /* Make sure it is still within the directory tree. */
6465 if (last >= directory_tree + directory_tree_size)
6466 return NULL;
6467
6468 /* Now, fill in the dirent with the name. */
6469 memset (&dirent, 0, sizeof dirent);
6470 dirent.d_ino = 0;
6471 dirent.d_off = 0;
6472 dirent.d_reclen = sizeof dirent;
6473
6474 /* If this is not a directory, return DT_UNKNOWN. Otherwise,
6475 return DT_DIR. */
6476
6477 if (android_is_directory (dir->asset_dir))
6478 dirent.d_type = DT_DIR;
6479 else
6480 dirent.d_type = DT_UNKNOWN;
6481
6482 /* Note that dir->asset_dir is actually a NULL terminated
6483 string. */
6484 memcpy (dirent.d_name, dir->asset_dir,
6485 MIN (sizeof dirent.d_name,
6486 last - dir->asset_dir));
6487 dirent.d_name[sizeof dirent.d_name - 1] = '\0';
6488
6489 /* Strip off the trailing slash, if any. */
6490 if (dirent.d_name[MIN (sizeof dirent.d_name,
6491 last - dir->asset_dir)
6492 - 2] == '/')
6493 dirent.d_name[MIN (sizeof dirent.d_name,
6494 last - dir->asset_dir)
6495 - 2] = '\0';
6496
6497 /* Finally, forward dir->asset_dir to the file past last. */
6498 dir->asset_dir = ((char *) directory_tree
6499 + android_extract_long ((char *) last));
6500
6501 return &dirent;
6502 }
6503
6504 return readdir (dir->dir);
6505}
6506
6507/* Like closedir, but it also closes asset manager directories. */
6508
6509void
6510android_closedir (struct android_dir *dir)
6511{
6512 struct android_dir **next, *tem;
6513
6514 if (dir->dir)
6515 closedir (dir->dir);
6516 else
6517 {
6518 if (dir->fd != -1)
6519 close (dir->fd);
6520
6521 /* Unlink this directory from the list of all asset manager
6522 directories. */
6523
6524 for (next = &android_dirs; (tem = *next);)
6525 {
6526 if (tem == dir)
6527 *next = dir->next;
6528 else
6529 next = &(*next)->next;
6530 }
6531
6532 /* Free the asset file name. */
6533 xfree (dir->asset_file);
6534 }
6535
6536 /* There is no need to close anything else, as the directory tree
6537 lies in statically allocated memory. */
6538
6539 xfree (dir);
6540}
6541
6542/* Subroutine used by android_fstatat and android_faccessat. If DIRFD
6543 belongs to an open asset directory and FILE is a relative file
6544 name, then return AT_FDCWD and the absolute file name of the
6545 directory prepended to FILE in *PATHNAME. Else, return DIRFD. */
6546
6547int
6548android_lookup_asset_directory_fd (int dirfd,
6549 const char *restrict *pathname,
6550 const char *restrict file)
6551{
6552 struct android_dir *dir;
6553 static char *name;
6554
6555 if (file[0] == '/')
6556 return dirfd;
6557
6558 for (dir = android_dirs; dir; dir = dir->next)
6559 {
6560 if (dir->fd == dirfd && dirfd != -1)
6561 {
6562 if (name)
6563 xfree (name);
6564
6565 /* dir->asset_file is always separator terminated. */
6566 name = xzalloc (strlen (dir->asset_file)
6567 + strlen (file) + 1);
6568 strcpy (name, dir->asset_file);
6569 strcpy (name + strlen (dir->asset_file),
6570 file);
6571 *pathname = name;
6572 return AT_FDCWD;
6573 }
6574 }
6575
6576 return dirfd;
6577}
6578
6579
6580
6581/* emacs_abort implementation for Android. This logs a stack 5429/* emacs_abort implementation for Android. This logs a stack
6582 trace. */ 5430 trace. */
6583 5431
@@ -6787,6 +5635,28 @@ android_exception_check_2 (jobject object, jobject object1)
6787 memory_full (0); 5635 memory_full (0);
6788} 5636}
6789 5637
5638/* Like android_exception_check_2, except it takes more than two local
5639 reference arguments. */
5640
5641void
5642android_exception_check_3 (jobject object, jobject object1,
5643 jobject object2)
5644{
5645 if (likely (!(*android_java_env)->ExceptionCheck (android_java_env)))
5646 return;
5647
5648 __android_log_print (ANDROID_LOG_WARN, __func__,
5649 "Possible out of memory error. "
5650 " The Java exception follows: ");
5651 /* Describe exactly what went wrong. */
5652 (*android_java_env)->ExceptionDescribe (android_java_env);
5653 (*android_java_env)->ExceptionClear (android_java_env);
5654 ANDROID_DELETE_LOCAL_REF (object);
5655 ANDROID_DELETE_LOCAL_REF (object1);
5656 ANDROID_DELETE_LOCAL_REF (object2);
5657 memory_full (0);
5658}
5659
6790/* Check for JNI problems based on the value of OBJECT. 5660/* Check for JNI problems based on the value of OBJECT.
6791 5661
6792 Signal out of memory if OBJECT is NULL. OBJECT1 means the 5662 Signal out of memory if OBJECT is NULL. OBJECT1 means the
@@ -7160,7 +6030,7 @@ android_restart_emacs (void)
7160 turn which APIs Emacs can safely use. */ 6030 turn which APIs Emacs can safely use. */
7161 6031
7162int 6032int
7163android_get_current_api_level (void) 6033(android_get_current_api_level) (void)
7164{ 6034{
7165 return android_api_level; 6035 return android_api_level;
7166} 6036}
@@ -7206,30 +6076,25 @@ android_query_battery (struct android_battery_state *status)
7206 return 0; 6076 return 0;
7207} 6077}
7208 6078
7209/* Display a small momentary notification on screen containing 6079/* Display a file panel and grant Emacs access to the SAF directory
7210 TEXT, which must be in the modified UTF encoding used by the 6080 within it. Value is 1 upon failure and 0 upon success (which only
7211 JVM. */ 6081 indicates that the panel has been displayed successfully; the panel
6082 may still be dismissed without a file being selected.) */
7212 6083
7213void 6084int
7214android_display_toast (const char *text) 6085android_request_directory_access (void)
7215{ 6086{
7216 jstring string; 6087 jint rc;
6088 jmethodID method;
7217 6089
7218 /* Make the string. */ 6090 method = service_class.request_directory_access;
7219 string = (*android_java_env)->NewStringUTF (android_java_env, 6091 rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
7220 text); 6092 emacs_service,
6093 service_class.class,
6094 method);
7221 android_exception_check (); 6095 android_exception_check ();
7222 6096
7223 /* Display the toast. */ 6097 return rc;
7224 (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
7225 emacs_service,
7226 service_class.class,
7227 service_class.display_toast,
7228 string);
7229 android_exception_check_1 (string);
7230
7231 /* Delete the local reference to the string. */
7232 ANDROID_DELETE_LOCAL_REF (string);
7233} 6098}
7234 6099
7235 6100
@@ -7709,156 +6574,6 @@ android_set_fullscreen (android_window window, bool fullscreen)
7709 6574
7710 6575
7711 6576
7712/* External asset management interface. By using functions here
7713 to read and write from files, Emacs can avoid opening a
7714 shared memory file descriptor for each ``asset'' file. */
7715
7716/* Like android_open. However, return a structure that can
7717 either directly hold an AAsset or a file descriptor.
7718
7719 Value is the structure upon success. Upon failure, value
7720 consists of an uninitialized file descriptor, but its asset
7721 field is set to -1, and errno is set accordingly. */
7722
7723struct android_fd_or_asset
7724android_open_asset (const char *filename, int oflag, mode_t mode)
7725{
7726 const char *name;
7727 struct android_fd_or_asset fd;
7728 AAsset *asset;
7729
7730 /* Initialize FD by setting its asset to an invalid
7731 pointer. */
7732 fd.asset = (void *) -1;
7733
7734 /* See if this is an asset. */
7735
7736 if (asset_manager && (name = android_get_asset_name (filename)))
7737 {
7738 /* Return failure for unsupported flags. */
7739
7740 if (oflag & O_WRONLY || oflag & O_RDWR)
7741 {
7742 errno = EROFS;
7743 return fd;
7744 }
7745
7746 if (oflag & O_DIRECTORY)
7747 {
7748 errno = ENOTSUP;
7749 return fd;
7750 }
7751
7752 /* Now try to open the asset. */
7753 asset = AAssetManager_open (asset_manager, name,
7754 AASSET_MODE_STREAMING);
7755
7756 if (!asset)
7757 {
7758 errno = ENOENT;
7759 return fd;
7760 }
7761
7762 /* Return the asset. */
7763 fd.asset = asset;
7764 return fd;
7765 }
7766
7767 /* If the file is not an asset, fall back to android_open and
7768 get a regular file descriptor. */
7769
7770 fd.fd = android_open (filename, oflag, mode);
7771 if (fd.fd < 0)
7772 return fd;
7773
7774 /* Set fd.asset to NULL, signifying that it is a file
7775 descriptor. */
7776 fd.asset = NULL;
7777 return fd;
7778}
7779
7780/* Like android_close. However, it takes a ``file descriptor''
7781 opened using android_open_asset. */
7782
7783int
7784android_close_asset (struct android_fd_or_asset asset)
7785{
7786 if (!asset.asset)
7787 return android_close (asset.fd);
7788
7789 AAsset_close (asset.asset);
7790 return 0;
7791}
7792
7793/* Like `emacs_read_quit'. However, it handles file descriptors
7794 opened using `android_open_asset' as well. */
7795
7796ssize_t
7797android_asset_read_quit (struct android_fd_or_asset asset,
7798 void *buffer, size_t size)
7799{
7800 if (!asset.asset)
7801 return emacs_read_quit (asset.fd, buffer, size);
7802
7803 /* It doesn't seem possible to quit from inside AAsset_read,
7804 sadly. */
7805 return AAsset_read (asset.asset, buffer, size);
7806}
7807
7808/* Like `read'. However, it handles file descriptors opened
7809 using `android_open_asset' as well. */
7810
7811ssize_t
7812android_asset_read (struct android_fd_or_asset asset,
7813 void *buffer, size_t size)
7814{
7815 if (!asset.asset)
7816 return read (asset.fd, buffer, size);
7817
7818 /* It doesn't seem possible to quit from inside AAsset_read,
7819 sadly. */
7820 return AAsset_read (asset.asset, buffer, size);
7821}
7822
7823/* Like `lseek', but it handles ``file descriptors'' opened with
7824 android_open_asset. */
7825
7826off_t
7827android_asset_lseek (struct android_fd_or_asset asset, off_t off,
7828 int whence)
7829{
7830 if (!asset.asset)
7831 return lseek (asset.fd, off, whence);
7832
7833 return AAsset_seek (asset.asset, off, whence);
7834}
7835
7836/* Like `fstat'. */
7837
7838int
7839android_asset_fstat (struct android_fd_or_asset asset,
7840 struct stat *statb)
7841{
7842 if (!asset.asset)
7843 return fstat (asset.fd, statb);
7844
7845 /* Clear statb. */
7846 memset (statb, 0, sizeof *statb);
7847
7848 /* Set the mode. */
7849 statb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
7850
7851 /* Owned by root. */
7852 statb->st_uid = 0;
7853 statb->st_gid = 0;
7854
7855 /* Size of the file. */
7856 statb->st_size = AAsset_getLength (asset.asset);
7857 return 0;
7858}
7859
7860
7861
7862/* Window cursor support. */ 6577/* Window cursor support. */
7863 6578
7864android_cursor 6579android_cursor
@@ -8088,4 +6803,4 @@ android_project_image_nearest (struct android_image *image,
8088 emacs_abort (); 6803 emacs_abort ();
8089} 6804}
8090 6805
8091#endif 6806#endif /* !ANDROID_STUBIFY */