diff options
| author | Po Lu | 2023-07-27 17:13:39 +0800 |
|---|---|---|
| committer | Po Lu | 2023-07-27 17:13:39 +0800 |
| commit | 65b58251b1b07b50672367c675efdfdc97354c4a (patch) | |
| tree | 9e323a76c3d5f81ecce18321beca703c456b2802 /src/android.c | |
| parent | ce63f592f579e8963a3e7f44b31c7df50fb0cdba (diff) | |
| download | emacs-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.c | 1799 |
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 | |||
| 83 | struct 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 | |||
| 92 | enum android_fd_table_entry_flags | ||
| 93 | { | ||
| 94 | ANDROID_FD_TABLE_ENTRY_IS_VALID = 1, | ||
| 95 | }; | ||
| 96 | |||
| 97 | struct 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 | |||
| 125 | struct android_emacs_pixmap | 74 | struct 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. */ |
| 175 | static int android_api_level; | 124 | static int android_api_level; |
| 176 | 125 | ||
| 177 | /* The asset manager being used. */ | ||
| 178 | static AAssetManager *asset_manager; | ||
| 179 | |||
| 180 | /* Whether or not Emacs has been initialized. */ | ||
| 181 | static int emacs_initialized; | ||
| 182 | |||
| 183 | /* The directory used to store site-lisp. */ | 126 | /* The directory used to store site-lisp. */ |
| 184 | char *android_site_load_path; | 127 | char *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. */ |
| 207 | static char *android_files_dir; | 150 | static 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. */ | ||
| 212 | static 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. */ |
| 215 | JNIEnv *android_java_env; | 153 | JNIEnv *android_java_env; |
| 216 | 154 | ||
| @@ -235,10 +173,10 @@ static jclass android_rect_class; | |||
| 235 | static jmethodID android_rect_constructor; | 173 | static jmethodID android_rect_constructor; |
| 236 | 174 | ||
| 237 | /* The EmacsService object. */ | 175 | /* The EmacsService object. */ |
| 238 | static jobject emacs_service; | 176 | jobject emacs_service; |
| 239 | 177 | ||
| 240 | /* Various methods associated with the EmacsService. */ | 178 | /* Various methods associated with the EmacsService. */ |
| 241 | static struct android_emacs_service service_class; | 179 | struct android_emacs_service service_class; |
| 242 | 180 | ||
| 243 | /* Various methods associated with the EmacsPixmap class. */ | 181 | /* Various methods associated with the EmacsPixmap class. */ |
| 244 | static struct android_emacs_pixmap pixmap_class; | 182 | static 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. */ | ||
| 890 | static const char *directory_tree; | ||
| 891 | |||
| 892 | /* The size of the directory tree. */ | ||
| 893 | static size_t directory_tree_size; | ||
| 894 | |||
| 895 | /* Read an unaligned (32-bit) long from the address POINTER. */ | ||
| 896 | |||
| 897 | static unsigned int | ||
| 898 | android_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 | |||
| 918 | static const char * | ||
| 919 | android_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 | |||
| 1086 | static bool | ||
| 1087 | android_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 | ||
| 1167 | static const char * | 889 | static ssize_t |
| 1168 | android_get_asset_name (const char *filename) | 890 | android_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 | ||
| 1189 | static bool | 953 | /* Copy the encoded string to STRING. */ |
| 1190 | android_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 * | |||
| 1209 | android_get_content_name (const char *filename) | 989 | android_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 | |||
| 1259 | static bool | ||
| 1260 | android_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 | |||
| 1294 | int | ||
| 1295 | android_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 | |||
| 1309 | static 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 | |||
| 1319 | int | ||
| 1320 | android_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 | |||
| 1401 | static bool | ||
| 1402 | android_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 | |||
| 1427 | static int | ||
| 1428 | android_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. */ | ||
| 1485 | static 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 | ||
| 1493 | static bool | 1030 | *head++ = '/'; |
| 1494 | android_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 | |||
| 1600 | static int | ||
| 1601 | android_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 | |||
| 1746 | static void | ||
| 1747 | android_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 | ||
| 1774 | int | 1119 | static bool |
| 1775 | android_open (const char *filename, int oflag, mode_t mode) | 1120 | android_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 | ||
| 1944 | int | 1134 | (*android_java_env)->SetByteArrayRegion (android_java_env, |
| 1945 | android_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 | |||
| 1956 | int | ||
| 1957 | android_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 | |||
| 6261 | int | ||
| 6262 | android_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 | |||
| 6321 | struct 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. */ | ||
| 6344 | static struct android_dir *android_dirs; | ||
| 6345 | |||
| 6346 | /* Like opendir. However, return an asset directory if NAME points to | ||
| 6347 | an asset. */ | ||
| 6348 | |||
| 6349 | struct android_dir * | ||
| 6350 | android_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 | |||
| 6422 | int | ||
| 6423 | android_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 | |||
| 6441 | struct dirent * | ||
| 6442 | android_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 | |||
| 6509 | void | ||
| 6510 | android_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 | |||
| 6547 | int | ||
| 6548 | android_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 | |||
| 5641 | void | ||
| 5642 | android_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 | ||
| 7162 | int | 6032 | int |
| 7163 | android_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 | ||
| 7213 | void | 6084 | int |
| 7214 | android_display_toast (const char *text) | 6085 | android_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 | |||
| 7723 | struct android_fd_or_asset | ||
| 7724 | android_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 | |||
| 7783 | int | ||
| 7784 | android_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 | |||
| 7796 | ssize_t | ||
| 7797 | android_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 | |||
| 7811 | ssize_t | ||
| 7812 | android_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 | |||
| 7826 | off_t | ||
| 7827 | android_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 | |||
| 7838 | int | ||
| 7839 | android_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 | ||
| 7864 | android_cursor | 6579 | android_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 */ |