From: Po Lu Date: Wed, 12 Jun 2024 08:44:21 +0000 (+0800) Subject: Restore functionality on Android 2.2 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=f4ab96a068a23968a339bab4448b92faefd24c21;p=emacs.git Restore functionality on Android 2.2 * java/Makefile.in (install_temp): Do not compress directory-tree and generate such files in a special format that stores file sizes. * lib-src/asset-directory-tool.c (struct directory_tree): New field st_size. (need_file_size): New variable. (main_1, main_2, main): Write file sizes before sibling offsets if `--api-8' is specified. * src/android-asset.h (struct android_asset_manager): New field open. (struct android_asset): New field name. (AAssetManager_fromJava): Load AssetManager#open. (AAssetManager_open): If a directory tree has already been loaded, search for a matching asset and load its size thence, to avoid the requirement of an AssetFileDescriptor. (AAsset_close): Don't assume asset->fd exists. Release asset->name. (AAsset_getLength): Likewise. (android_asset_create_stream): If asset->name exists, call AssetManager#open, in order to open compressed files. * src/androidvfs.c (OLD_ANDROID_ASSETS): Define to 1 on API 8. (android_extract_long, android_scan_directory_tree): Mark arguments as const. Adjust offsets when OLD_ANDROID_ASSETS. (android_is_directory, android_init_assets, android_afs_readdir): Likewise. * src/lread.c (lread_fstat): Define to sys_fstat, not fstat. (cherry picked from commit 02e70821b3800a082aec215a9ab8adbfafe9ad76) --- diff --git a/java/Makefile.in b/java/Makefile.in index b14fc3c2fff..3b378857650 100644 --- a/java/Makefile.in +++ b/java/Makefile.in @@ -257,7 +257,8 @@ install_temp: $(CROSS_BINS) $(CROSS_LIBS) $(RESOURCE_FILES) { hostname; date +%s; } > install_temp/assets/build_info # Produce the file index. $(AM_V_SILENT) $(libsrc)/asset-directory-tool \ - install_temp/assets install_temp/assets/directory-tree + install_temp/assets install_temp/assets/directory-tree\ + $(if $(ANDROID_SDK_8_OR_EARLIER),--api-8) # If the package targets Android 2.2, move compressable and # non-compressable assets to separate directories. $(AM_V_SILENT) \ @@ -265,6 +266,7 @@ install_temp: $(CROSS_BINS) $(CROSS_LIBS) $(RESOURCE_FILES) echo "Moving large and gzipped files to separate directories...">&2;\ mkdir -p install_temp/assets_raw; \ cd install_temp/assets; \ + mv directory-tree ../assets_raw; \ find . \( -size +1536 -o -size 1536 \) \ \( \! -name '*.gz' \) -type f > files.txt; \ tar cf ../assets_raw/largefiles.tar -T files.txt; \ diff --git a/lib-src/asset-directory-tool.c b/lib-src/asset-directory-tool.c index 31735586193..23f4655448c 100644 --- a/lib-src/asset-directory-tool.c +++ b/lib-src/asset-directory-tool.c @@ -20,6 +20,7 @@ along with GNU Emacs. If not, see . */ #include #include +#include #include #include #include @@ -35,17 +36,19 @@ along with GNU Emacs. If not, see . */ application package. Such a file records the layout of the `assets' directory in the - package. Emacs records this information itself and uses it in the - Android emulation of readdir, because the system asset manager APIs - are routinely buggy, and are often unable to locate directories or - files. - - The file is packed, with no data alignment guarantees made. The - file starts with the bytes "EMACS", following which is the name of - the first file or directory, a NULL byte and an unsigned int - indicating the offset from the start of the file to the start of - the next sibling. Following that is a list of subdirectories or - files in the same format. The long is stored LSB first. + package, and, in packages targeting Android 2.2, the size of each of + its members. Emacs records this information itself and uses it in + the Android emulation of readdir, because the system asset manager + APIs are often unable to locate directories or files, or provide + corresponding metadata. + + The file is packed, with no data alignment guarantees made. The file + starts with the bytes "EMACS", or EMACS____ on Android 2.2, following + which is the name of the first file or directory, a NULL byte, an + unsigned int holding its size (on Android 2.2), and an unsigned int + indicating the offset from the start of the file to the start of the + next sibling. Following that is a list of subdirectories or files in + the same format. The long is stored LSB first. Directories can be distinguished from ordinary files through the last bytes of their file names (immediately previous to their @@ -62,10 +65,19 @@ struct directory_tree /* The name of this directory or file. */ char *name; + /* st_size of this entry. */ + off_t st_size; + /* Subdirectories and files inside this directory. */ struct directory_tree *children, *next; }; +/* Whether the size of each entry should be prepended to the start + pointer. */ +static bool need_file_size; + + + /* Exit with EXIT_FAILURE, after printing a description of a failing function WHAT along with the details of the error. */ @@ -138,11 +150,14 @@ main_1 (DIR *dir, struct directory_tree *parent) last = &this->next; this->name = xmalloc (length + 2); strcpy (this->name, dirent->d_name); + this->st_size = 0; /* Now record the offset to the end of this directory. This - is length + 1, for the file name, and 5 more bytes for - the trailing NULL and long. */ - this->offset = parent->offset + length + 6; + is length + 1, for the file name, 5 more bytes for the + trailing NULL and long, and 4 further bytes if a file size + is required. */ + this->offset = (parent->offset + + length + 6 + (need_file_size ? 4 : 0)); /* Terminate that with a slash and trailing NULL byte. */ this->name[length] = '/'; @@ -175,11 +190,22 @@ main_1 (DIR *dir, struct directory_tree *parent) *last = this; last = &this->next; this->name = xmalloc (length + 1); + this->st_size = statb.st_size; strcpy (this->name, dirent->d_name); - /* This is one byte shorter because there is no trailing + if (this->st_size >= 0x1ffffff) + { + fprintf (stderr, + "asset-directory-tool: file size exceeds maximum" + " representable in a directory-tree: %s\n", + dirent->d_name); + exit (EXIT_FAILURE); + } + + /* This is one byte the shorter because there is no trailing slash. */ - this->offset = parent->offset + length + 5; + this->offset = (parent->offset + length + 5 + + (need_file_size ? 4 : 0)); parent->offset = this->offset; } } @@ -194,7 +220,7 @@ main_2 (int fd, struct directory_tree *tree, size_t *offset) { ssize_t size; struct directory_tree *child; - unsigned int output; + unsigned int output[2]; /* Write tree->name with the trailing NULL byte. */ size = strlen (tree->name) + 1; @@ -203,13 +229,26 @@ main_2 (int fd, struct directory_tree *tree, size_t *offset) /* Write the offset. */ #ifdef WORDS_BIGENDIAN - output = bswap_32 (tree->offset); -#else - output = tree->offset; -#endif - if (write (fd, &output, 4) < 1) - croak ("write"); - size += 4; + output[1] = bswap_32 (tree->offset); + output[0] = bswap_32 ((unsigned int) tree->st_size); +#else /* !WORDS_BIGENDIAN */ + output[1] = tree->offset; + output[0] = (unsigned int) tree->st_size; +#endif /* !WORDS_BIGENDIAN */ + + verify (sizeof output == 8 && sizeof output[0] == 4); + if (!need_file_size) + { + if (write (fd, output + 1, 4) < 1) + croak ("write"); + size += 4; + } + else + { + if (write (fd, output, 8) < 1) + croak ("write"); + size += 8; + } /* Now update offset. */ *offset += size; @@ -240,13 +279,16 @@ main (int argc, char **argv) struct directory_tree tree; size_t offset; - if (argc != 3) + if (argc != 3 && argc != 4) { - fprintf (stderr, "usage: %s directory output-file\n", - argv[0]); + fprintf (stderr, "usage: %s directory output-file " + "[--api-8]\n", argv[0]); return EXIT_FAILURE; } + if (argc == 4 && !strcmp (argv[3], "--api-8")) + need_file_size = true; + fd = open (argv[2], O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); @@ -272,16 +314,23 @@ main (int argc, char **argv) return EXIT_FAILURE; } + /* And a further 4 bytes of padding if need_file_size. */ + if (need_file_size && write (fd, "____", 4) < 4) + { + perror ("write"); + return EXIT_FAILURE; + } + /* Now iterate through children of INDIR, building the directory tree. */ - tree.offset = 5; + tree.offset = 5 + (need_file_size ? 4 : 0); tree.children = NULL; main_1 (indir, &tree); closedir (indir); /* Finally, write the directory tree to the output file. */ - offset = 5; + offset = 5 + (need_file_size ? 4 : 0); for (; tree.children; tree.children = tree.children->next) main_2 (fd, tree.children, &offset); diff --git a/src/android-asset.h b/src/android-asset.h index a6b5aa3366c..273ab1fa734 100644 --- a/src/android-asset.h +++ b/src/android-asset.h @@ -19,6 +19,17 @@ along with GNU Emacs. If not, see . */ #include + + +/* Forward declarations. */ + +static const char *directory_tree; + +static const char *android_scan_directory_tree (const char *, size_t *); +static unsigned int android_extract_long (const char *); + + + /* This file contains an emulation of the Android asset manager API used on builds for Android 2.2. It is included by android.c whenever appropriate. @@ -34,6 +45,7 @@ struct android_asset_manager /* Asset manager class and functions. */ jclass class; jmethodID open_fd; + jmethodID open; /* Asset file descriptor class and functions. */ jclass fd_class; @@ -63,6 +75,9 @@ struct android_asset /* The asset file descriptor and input stream. */ jobject fd, stream; + /* Alternatively, the name of the file. */ + jstring name; + /* The mode. */ int mode; }; @@ -98,6 +113,12 @@ AAssetManager_fromJava (JNIEnv *env, jobject java_manager) = (*env)->GetMethodID (env, manager->class, "openFd", "(Ljava/lang/String;)" "Landroid/content/res/AssetFileDescriptor;"); + assert (manager->open_fd); + + manager->open + = (*env)->GetMethodID (env, manager->class, "open", + "(Ljava/lang/String;)" + "Ljava/io/InputStream;"); assert (manager->open); manager->fd_class @@ -168,6 +189,8 @@ AAssetManager_open (AAssetManager *manager, const char *c_name, jobject desc; jstring name; AAsset *asset; + const char *asset_dir; + jlong st_size = -1; /* Push a local frame. */ asset = NULL; @@ -177,53 +200,86 @@ AAssetManager_open (AAssetManager *manager, const char *c_name, if ((*(manager->env))->ExceptionCheck (manager->env)) goto fail; - /* Encoding issues can be ignored for now as there are only ASCII - file names in Emacs. */ + /* If the directory tree has been initialized, it is possible to avoid + opening an AssetFileDescriptor and thereby access compressed + assets, without sacrificing the possibility of reading the file + size. */ + if (directory_tree) + { + /* Search for a matching asset. */ + asset_dir = android_scan_directory_tree (c_name, NULL); + if (!asset_dir) + goto fail; + + /* Extract the size of the asset from this directory. */ + st_size = android_extract_long (asset_dir - 8); + } + + /* Encoding issues can be ignored for the time being as there are only + ASCII file names in Emacs. */ name = (*(manager->env))->NewStringUTF (manager->env, c_name); if (!name) goto fail; - /* Now try to open an ``AssetFileDescriptor''. */ - desc = (*(manager->env))->CallObjectMethod (manager->env, - manager->asset_manager, - manager->open_fd, - name); + /* If st_size has been set, it ought to be possible to open an input + stream directly upon the first attempt to read from the asset, + sidestepping the intermediate AssetFileDescriptor. */ - if (!desc) - goto fail; + desc = NULL; + + if (st_size < 0) + /* Otherwise attempt to open an ``AssetFileDescriptor''. */ + desc = (*(manager->env))->CallObjectMethod (manager->env, + manager->asset_manager, + manager->open_fd, + name); /* Allocate the asset. */ asset = calloc (1, sizeof *asset); if (!asset) + goto fail; + + if (desc) { - (*(manager->env))->CallVoidMethod (manager->env, - desc, - manager->close); - goto fail; - } + /* Pop the local frame and return desc. */ + desc = (*(manager->env))->NewGlobalRef (manager->env, desc); - /* Pop the local frame and return desc. */ - desc = (*(manager->env))->NewGlobalRef (manager->env, desc); + if (!desc) + goto fail; - if (!desc) - goto fail; + /* Will be released by PopLocalFrame. */ + name = NULL; + } + else /* if (name) */ + { + /* Pop the local frame and return name. */ + name = (*(manager->env))->NewGlobalRef (manager->env, name); + + if (!name) + goto fail; + } (*(manager->env))->PopLocalFrame (manager->env, NULL); asset->manager = manager; - asset->length = -1; + asset->length = st_size; asset->fd = desc; + asset->name = name; asset->mode = mode; return asset; fail: + if (desc) + (*(manager->env))->CallVoidMethod (manager->env, + desc, + manager->close); + (*(manager->env))->ExceptionClear (manager->env); (*(manager->env))->PopLocalFrame (manager->env, NULL); free (asset); - return NULL; } @@ -234,11 +290,14 @@ AAsset_close (AAsset *asset) env = asset->manager->env; - (*env)->CallVoidMethod (asset->manager->env, - asset->fd, - asset->manager->close); - (*env)->DeleteGlobalRef (asset->manager->env, - asset->fd); + if (asset->fd) + { + (*env)->CallVoidMethod (asset->manager->env, + asset->fd, + asset->manager->close); + (*env)->DeleteGlobalRef (asset->manager->env, + asset->fd); + } if (asset->stream) { @@ -249,6 +308,10 @@ AAsset_close (AAsset *asset) asset->stream); } + if (asset->name) + (*env)->DeleteGlobalRef (asset->manager->env, + asset->name); + free (asset); } @@ -264,10 +327,17 @@ android_asset_create_stream (AAsset *asset) jobject stream; JNIEnv *env; + assert (asset->fd || asset->name); + env = asset->manager->env; - stream - = (*env)->CallObjectMethod (env, asset->fd, - asset->manager->create_input_stream); + + if (asset->name) + stream = (*env)->CallObjectMethod (env, asset->manager->asset_manager, + asset->manager->open, asset->name); + else + stream + = (*env)->CallObjectMethod (env, asset->fd, + asset->manager->create_input_stream); if (!stream) { @@ -380,6 +450,8 @@ AAsset_getLength (AAsset *asset) if (asset->length != -1) return asset->length; + if (!asset->fd) + return 0; env = asset->manager->env; asset->length diff --git a/src/androidvfs.c b/src/androidvfs.c index c0bd86e54b8..d28a74918f6 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -46,8 +46,10 @@ along with GNU Emacs. If not, see . */ #if __ANDROID_API__ >= 9 #include #include +#define OLD_ANDROID_ASSETS 0 #else /* __ANDROID_API__ < 9 */ #include "android-asset.h" +#define OLD_ANDROID_ASSETS 1 #endif /* __ANDROID_API__ >= 9 */ #include @@ -1001,7 +1003,7 @@ static AAssetManager *asset_manager; /* Read an unaligned (32-bit) long from the address POINTER. */ static unsigned int -android_extract_long (char *pointer) +android_extract_long (const char *pointer) { unsigned int number; @@ -1022,16 +1024,20 @@ android_extract_long (char *pointer) directory. */ static const char * -android_scan_directory_tree (char *file, size_t *limit_return) +android_scan_directory_tree (const char *file, size_t *limit_return) { char *token, *saveptr, *copy, *start, *max, *limit; size_t token_length, ntokens, i, len; - char *tokens[10]; + char *tokens[20]; USE_SAFE_ALLOCA; - /* Skip past the 5 byte header. */ + /* Skip past the 5 or 9 byte header. */ +#if !OLD_ANDROID_ASSETS start = (char *) directory_tree + 5; +#else /* OLD_ANDROID_ASSETS */ + start = (char *) directory_tree + 9; +#endif /* OLD_ANDROID_ASSETS */ /* Figure out the current limit. */ limit = (char *) directory_tree + directory_tree_size; @@ -1098,9 +1104,9 @@ android_scan_directory_tree (char *file, size_t *limit_return) { /* They probably match. Find the NULL byte. It must be either one byte past start + token_length, with the last - byte a trailing slash (indicating that it is a - directory), or just start + token_length. Return 4 bytes - past the next NULL byte. */ + byte a trailing slash (indicating that it is a directory), + or just start + token_length. Return 4 or 8 bytes past the + next NULL byte. */ max = memchr (start, 0, limit - start); @@ -1113,13 +1119,14 @@ android_scan_directory_tree (char *file, size_t *limit_return) last token. Otherwise, set it as start and the limit as start + the offset and continue the loop. */ - if (max && max + 5 <= limit) + if (max && max + (OLD_ANDROID_ASSETS ? 9 : 5) <= limit) { if (i < ntokens - 1) { - start = max + 5; + start = max + (OLD_ANDROID_ASSETS ? 9 : 5); limit = ((char *) directory_tree - + android_extract_long (max + 1)); + + android_extract_long (max + (OLD_ANDROID_ASSETS + ? 5 : 1))); /* Make sure limit is still in range. */ if (limit > directory_tree + directory_tree_size @@ -1137,10 +1144,12 @@ android_scan_directory_tree (char *file, size_t *limit_return) { /* Figure out the limit. */ if (limit_return) - *limit_return = android_extract_long (max + 1); + *limit_return + = android_extract_long (max + (OLD_ANDROID_ASSETS + ? 5 : 1)); /* Go to the end of this file. */ - max += 5; + max += (OLD_ANDROID_ASSETS ? 9 : 5); } SAFE_FREE (); @@ -1161,11 +1170,12 @@ android_scan_directory_tree (char *file, size_t *limit_return) start = memchr (start, 0, limit - start); - if (!start || start + 5 > limit) + if (!start || start + (OLD_ANDROID_ASSETS ? 9 : 5) > limit) goto fail; start = ((char *) directory_tree - + android_extract_long (start + 1)); + + android_extract_long (start + + (OLD_ANDROID_ASSETS ? 5 : 1))); /* Make sure start is still in bounds. */ @@ -1192,13 +1202,20 @@ android_is_directory (const char *dir) { /* If the directory is the directory tree, then it is a directory. */ - if (dir == directory_tree + 5) + if (dir == directory_tree + (OLD_ANDROID_ASSETS ? 9 : 5)) return true; +#if !OLD_ANDROID_ASSETS /* Otherwise, look 5 bytes behind. If it is `/', then it is a directory. */ return (dir - 6 >= directory_tree && *(dir - 6) == '/'); +#else /* OLD_ANDROID_ASSETS */ + /* Otherwise, look 9 bytes behind. If it is `/', then it is a + directory. */ + return (dir - 10 >= directory_tree + && *(dir - 10) == '/'); +#endif /* OLD_ANDROID_ASSETS */ } /* Initialize asset retrieval. ENV should be a JNI environment for @@ -1232,6 +1249,7 @@ android_init_assets (JNIEnv *env, jobject manager) /* Now figure out how big the directory tree is, and compare the first few bytes. */ directory_tree_size = AAsset_getLength (asset); +#if !OLD_ANDROID_ASSETS if (directory_tree_size < 5 || memcmp (directory_tree, "EMACS", 5)) { @@ -1239,6 +1257,15 @@ android_init_assets (JNIEnv *env, jobject manager) "Directory tree has bad magic"); emacs_abort (); } +#else /* OLD_ANDROID_ASSETS */ + if (directory_tree_size < 9 + || memcmp (directory_tree, "EMACS____", 9)) + { + __android_log_print (ANDROID_LOG_FATAL, __func__, + "Directory tree has bad magic"); + emacs_abort (); + } +#endif /* OLD_ANDROID_ASSETS */ /* Hold a VM reference to the asset manager to prevent the native object from being deleted. */ @@ -2287,8 +2314,13 @@ android_afs_readdir (struct android_vdir *vdir) dirent.d_type = DT_REG; /* Forward dir->asset_dir to the file past last. */ +#if !OLD_ANDROID_ASSETS dir->asset_dir = ((char *) directory_tree + android_extract_long ((char *) last)); +#else /* OLD_ANDROID_ASSETS */ + dir->asset_dir = ((char *) directory_tree + + android_extract_long ((char *) last + 4)); +#endif /* OLD_ANDROID_ASSETS */ return &dirent; } diff --git a/src/lread.c b/src/lread.c index f5c79a8c0ea..1bc5b0c993d 100644 --- a/src/lread.c +++ b/src/lread.c @@ -69,7 +69,7 @@ along with GNU Emacs. If not, see . */ #define lread_fd_cmp(n) (fd == (n)) #define lread_fd_p (fd >= 0) #define lread_close emacs_close -#define lread_fstat fstat +#define lread_fstat sys_fstat #define lread_read_quit emacs_read_quit #define lread_lseek lseek