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.
This commit is contained in:
Po Lu 2023-08-01 21:06:06 +08:00
parent b022398b8f
commit e41349dd93
5 changed files with 123 additions and 11 deletions

View file

@ -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

View file

@ -1769,4 +1769,29 @@ In addition, arbitrary runtime exceptions (such as
? 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<UriPermission> 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;
}
};

View file

@ -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
}

View file

@ -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;

View file

@ -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 ();