From e41349dd93ffec2b1e383cb4c4dfdb59f6e7edac Mon Sep 17 00:00:00 2001 From: Po Lu Date: Tue, 1 Aug 2023 21:06:06 +0800 Subject: [PATCH] Update Android port * doc/emacs/android.texi (Android File System): Describe how to access real files named /assets or /contents if so required. * java/org/gnu/emacs/EmacsService.java (validAuthority): * src/android.c (android_init_emacs_service): * src/android.h: New function. * src/androidvfs.c (android_saf_valid_authority_p): New function. Wrap the Java function. (android_saf_root_stat, android_saf_root_access): Don't return success if no authority by vp->authority's name exists. (android_saf_tree_from_name): Check validity of string data before giving it to JNI. --- doc/emacs/android.texi | 15 +++-- java/org/gnu/emacs/EmacsService.java | 25 ++++++++ src/android.c | 2 + src/android.h | 1 + src/androidvfs.c | 91 +++++++++++++++++++++++++--- 5 files changed, 123 insertions(+), 11 deletions(-) diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 0330e9b5890..4b8f36a65eb 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -219,6 +219,15 @@ containing files provided by external programs (@pxref{Android Document Providers}.) @end itemize + Despite ordinary installations of Android not having files within +the (normally read-only) root directory named @file{content} or +@file{assets}, you may want to access real files by these names if the +Android installation in use has been customized. These files will +conflict with the aformentioned special directories, but can +nevertheless be accessed by writing their names relative to the +``parent'' directory of the root directory, as so illustrated: +@file{/../content}, @file{/../assets}. + The external storage directory is found at @file{/sdcard}. The other directories are not found at any fixed location (but see below), although the app data directory is typically symlinked to @@ -268,10 +277,8 @@ System -> Apps -> Special App Access -> All files access -> Emacs After you disable or enable this setting as appropriate and grant Emacs the ``Files and Media'' permission, it will be able to access -files under @file{/sdcard} as usual. - - These settings are not present on many proprietary versions of -Android. +files under @file{/sdcard} as usual. These settings are not present +on some proprietary versions of Android. @node Android Document Providers @section Accessing files from other programs under Android diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 3c1bb0855f4..8554dadd06e 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -1769,4 +1769,29 @@ public final class EmacsService extends Service ? DocumentsContract.getDocumentId (name) : null); } + + /* Return if there is a content provider by the name of AUTHORITY + supplying at least one tree URI Emacs retains persistent rights + to access. */ + + public boolean + validAuthority (String authority) + { + List permissions; + Uri uri; + + permissions = resolver.getPersistedUriPermissions (); + + for (UriPermission permission : permissions) + { + uri = permission.getUri (); + + if (DocumentsContract.isTreeUri (uri) + && permission.isReadPermission () + && uri.getAuthority ().equals (authority)) + return true; + } + + return false; + } }; diff --git a/src/android.c b/src/android.c index 2b785319549..c30d7b58979 100644 --- a/src/android.c +++ b/src/android.c @@ -1592,6 +1592,8 @@ android_init_emacs_service (void) "(Ljava/lang/String;Ljava/lang/String;" "Ljava/lang/String;Ljava/lang/String;" "Ljava/lang/String;)Ljava/lang/String;"); + FIND_METHOD (valid_authority, "validAuthority", + "(Ljava/lang/String;)Z"); #undef FIND_METHOD } diff --git a/src/android.h b/src/android.h index 8440fb9bc75..945bd649c18 100644 --- a/src/android.h +++ b/src/android.h @@ -284,6 +284,7 @@ struct android_emacs_service jmethodID delete_document; jmethodID rename_document; jmethodID move_document; + jmethodID valid_authority; }; extern JNIEnv *android_java_env; diff --git a/src/androidvfs.c b/src/androidvfs.c index eeef5ea5db0..e3b0b895df3 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -3249,6 +3249,43 @@ static struct android_saf_root_vdir *all_saf_root_vdirs; static struct android_vnode *android_saf_tree_from_name (char *, const char *, const char *); +/* Forward declaration. */ +static int android_verify_jni_string (const char *); + +/* Ascertain and return whether or not AUTHORITY designates a content + provider offering at least one directory tree accessible to + Emacs. */ + +static bool +android_saf_valid_authority_p (const char *authority) +{ + jobject string; + jboolean valid; + jmethodID method; + + /* Make certain AUTHORITY can actually be represented as a Java + string. */ + + if (android_verify_jni_string (authority)) + return false; + + /* Build a string containing AUTHORITY. */ + + string = (*android_java_env)->NewStringUTF (android_java_env, + authority); + android_exception_check (); + + method = service_class.valid_authority; + valid + = (*android_java_env)->CallNonvirtualBooleanMethod (android_java_env, + emacs_service, + service_class.class, + method, string); + android_exception_check_1 (string); + ANDROID_DELETE_LOCAL_REF (string); + return valid; +} + static struct android_vnode * android_saf_root_name (struct android_vnode *vnode, char *name, size_t length) @@ -3311,9 +3348,6 @@ android_saf_root_name (struct android_vnode *vnode, char *name, return android_saf_tree_from_name (component_end, component, vp->authority); - /* Otherwise, find the first component of NAME and create a vnode - representing it as an authority. */ - /* Create the vnode. */ vp = xmalloc (sizeof *vp); vp->vnode.ops = &saf_root_vfs_ops; @@ -3414,6 +3448,22 @@ static int android_saf_root_stat (struct android_vnode *vnode, struct stat *statb) { + struct android_saf_root_vnode *vp; + + /* Verify that the authority actually exists and return ENOENT + otherwise, lest `locate-dominating-file' & co call an operation + that doesn't require listing URIs under this authority, such as + access. */ + + vp = (struct android_saf_root_vnode *) vnode; + + if (vp->authority + && !android_saf_valid_authority_p (vp->authority)) + { + errno = ENOENT; + return -1; + } + /* Make up some imaginary statistics for this vnode. */ memset (statb, 0, sizeof *statb); @@ -3428,6 +3478,8 @@ android_saf_root_stat (struct android_vnode *vnode, static int android_saf_root_access (struct android_vnode *vnode, int mode) { + struct android_saf_root_vnode *vp; + /* Validate MODE. */ if (mode != F_OK && !(mode & (W_OK | X_OK | R_OK))) @@ -3444,6 +3496,20 @@ android_saf_root_access (struct android_vnode *vnode, int mode) return -1; } + /* Verify that the authority actually exists and return ENOENT + otherwise, lest `locate-dominating-file' & co call an operation + that doesn't require listing URIs under this authority, such as + access. */ + + vp = (struct android_saf_root_vnode *) vnode; + + if (vp->authority + && !android_saf_valid_authority_p (vp->authority)) + { + errno = ENOENT; + return -1; + } + return 0; } @@ -5309,9 +5375,9 @@ android_saf_tree_opendir (struct android_vnode *vnode) AUTHORITY is the name of the content provider authority that is offering TREE. - Value is NULL if no document tree or provider by those names - exists, or some other error takes place (for example, if TREE and - AUTHORITY aren't encoded correctly.) */ + Value is NULL and errno is set if no document tree or provider by + those names exists, or some other error takes place (for example, + if TREE and AUTHORITY aren't encoded correctly.) */ static struct android_vnode * android_saf_tree_from_name (char *name, const char *tree, @@ -5323,7 +5389,18 @@ android_saf_tree_from_name (char *name, const char *tree, const char *uri; struct android_vnode *vp; - /* Assume that TREE and NAME are in ``modified UTF-8 format''. */ + /* It's not a given that NAME and TREE are actually in the modified + UTF-8 format used by the JVM to encode strings, and the JVM + aborts when encountering a string that is not. Make sure they + are valid before continuing. */ + + if (android_verify_jni_string (name) + || android_verify_jni_string (authority)) + { + errno = ENOENT; + return NULL; + } + tree_string = (*android_java_env)->NewStringUTF (android_java_env, tree); android_exception_check (); -- 2.39.2