From de0e0939f01a747b8201e06bda5cd50dfa95187f Mon Sep 17 00:00:00 2001 From: Po Lu Date: Thu, 27 Jul 2023 21:59:58 +0800 Subject: [PATCH] Update Android port * doc/emacs/android.texi (Android Document Providers): Improve wording of paragraph clarifying limits on subprocesses. * java/org/gnu/emacs/EmacsService.java (getDocumentTrees): Use Java standard US-ASCII coding standard instead of the undocumented ``ASCII'' alias. (decodeFileName): Remove unused function. (documentIdFromName): * src/android.c (android_init_emacs_service): Take a String for NAME instead of a byte array. * src/androidvfs.c (android_verify_jni_string): New function. (android_document_id_from_name): Verify that STRING is a valid Modified UTF-8 string. --- doc/emacs/android.texi | 11 ++-- java/org/gnu/emacs/EmacsService.java | 34 +++---------- src/android.c | 3 +- src/androidvfs.c | 75 ++++++++++++++++++++++++++-- 4 files changed, 85 insertions(+), 38 deletions(-) diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 1f32fdfc1d2..b86c71cea49 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -298,11 +298,12 @@ subsequently made available within a new directory named a unique identifier assigned to the directory by the document provider. - Because these directories do not exist within the Unix file-system, -sub-processes cannot be created within them, just as with the -@file{/assets} directory (@pxref{Android File System}.) In addition, -although Emacs can normally write and create files inside these -directories, it cannot create symlinks or hard links. + The same limitations applied to the @file{/assets} directory +(@pxref{Android File System}) are applied when creating sub-processes +within those directories, because they do not exist within the Unix +file-system. In addition, although Emacs can normally write and +create files inside these directories, it cannot create symlinks or +hard links. @c TODO: fix this! Since document providers are allowed to perform expensive network diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 6059439551f..bc62e050345 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -1282,7 +1282,7 @@ public final class EmacsService extends Service try { - providerName = new String (provider, "ASCII"); + providerName = new String (provider, "US-ASCII"); } catch (UnsupportedEncodingException exception) { @@ -1306,24 +1306,6 @@ public final class EmacsService extends Service return treeList.toArray (new String[0]); } - /* Decode the specified STRING into a String object using the UTF-8 - format. If an exception is thrown, return null. */ - - private String - decodeFileName (byte[] string) - { - try - { - return new String (string, "UTF-8"); - } - catch (Exception e) /* UnsupportedEncodingException, etc. */ - { - ;; - } - - return null; - } - /* Find the document ID of the file within TREE_URI designated by NAME. @@ -1342,11 +1324,10 @@ public final class EmacsService extends Service If the designated file can't be located, return -1. */ private int - documentIdFromName (String tree_uri, byte name[], - String[] id_return) + documentIdFromName (String tree_uri, String name, String[] id_return) { Uri uri, treeUri; - String nameString, id, type; + String id, type; String[] components, projection; Cursor cursor; int column; @@ -1360,11 +1341,8 @@ public final class EmacsService extends Service /* Parse the URI identifying the tree first. */ uri = Uri.parse (tree_uri); - /* Next, decode NAME. */ - nameString = decodeFileName (name); - /* Now, split NAME into its individual components. */ - components = nameString.split ("/"); + components = name.split ("/"); /* Set id and type to the value at the root of the tree. */ type = id = null; @@ -1462,7 +1440,7 @@ public final class EmacsService extends Service try { - nameString = cursor.getString (column); + name = cursor.getString (column); } catch (Exception exception) { @@ -1473,7 +1451,7 @@ public final class EmacsService extends Service /* Break out of the loop only once a matching component is found. */ - if (nameString.equals (component)) + if (name.equals (component)) break; } diff --git a/src/android.c b/src/android.c index d8b264a8491..098fa6c383d 100644 --- a/src/android.c +++ b/src/android.c @@ -1556,7 +1556,8 @@ android_init_emacs_service (void) FIND_METHOD (get_document_trees, "getDocumentTrees", "([B)[Ljava/lang/String;"); FIND_METHOD (document_id_from_name, "documentIdFromName", - "(Ljava/lang/String;[B[Ljava/lang/String;)I"); + "(Ljava/lang/String;Ljava/lang/String;" + "[Ljava/lang/String;)I"); FIND_METHOD (get_tree_uri, "getTreeUri", "(Ljava/lang/String;Ljava/lang/String;)" "Ljava/lang/String;"); diff --git a/src/androidvfs.c b/src/androidvfs.c index bab3977ed5a..c174c35f02b 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -3936,6 +3936,66 @@ static struct android_vops saf_new_vfs_ops; /* Chain of all open SAF directory streams. */ static struct android_saf_tree_vdir *all_saf_tree_vdirs; +/* Verify that the specified NULL-terminated STRING is a valid JNI + ``UTF-8'' string. Return 0 if so, 1 otherwise. + + The native coding system used by the JVM to store strings derives + from UTF-8, but deviates from it in two aspects in an attempt to + better represent the UCS-16 based Java String format, and to let + strings contain NULL characters while remaining valid C strings: + NULL bytes are encoded as two-byte sequences, and Unicode surrogate + pairs encoded as two-byte sequences are prefered to four-byte + sequences when encoding characters above the BMP. */ + +static int +android_verify_jni_string (const char *name) +{ + const unsigned char *chars; + + chars = (unsigned char *) name; + while (*chars) + { + /* Switch on the high 4 bits. */ + + switch (*chars++ >> 4) + { + case 0 ... 7: + /* The 8th bit is clean, so this is a regular C + character. */ + break; + + case 8 ... 0xb: + /* Invalid starting byte! */ + return 1; + + case 0xf: + /* The start of a four byte sequence. These aren't allowed + in Java. */ + return 1; + + case 0xe: + /* The start of a three byte sequence. Verify that its + continued. */ + + if ((*chars++ & 0xc0) != 0x80) + return 1; + + FALLTHROUGH; + + case 0xc ... 0xd: + /* The start of a two byte sequence. Verify that the + next byte exists and has its high bit set. */ + + if ((*chars++ & 0xc0) != 0x80) + return 1; + + break; + } + } + + return 0; +} + /* Find the document ID of the file within TREE_URI designated by NAME. @@ -3943,6 +4003,9 @@ static struct android_saf_tree_vdir *all_saf_tree_vdirs; individual files. Each constituent component prior to the last must name a directory file within TREE_URI. + If NAME is not correct for the Java ``modified UTF-8'' coding + system, return -1. + Upon success, return 0 or 1 (contingent upon whether or not the last component within NAME is a directory) and place the document ID of the named file in ID. @@ -3965,6 +4028,12 @@ android_document_id_from_name (const char *tree_uri, char *name, jmethodID method; const char *doc_id; + /* Verify the format of NAME. Don't allow creating files that + contain characters that can't be encoded in Java. */ + + if (android_verify_jni_string (name)) + return -1; + /* First, create the array that will hold the result. */ result = (*android_java_env)->NewObjectArray (android_java_env, 1, java_string_class, @@ -3972,11 +4041,9 @@ android_document_id_from_name (const char *tree_uri, char *name, android_exception_check (); /* Next, create the string for the tree URI and name. */ - length = strlen (name); - java_name = (*android_java_env)->NewByteArray (android_java_env, length); + java_name = (*android_java_env)->NewStringUTF (android_java_env, + name); android_exception_check_1 (result); - (*android_java_env)->SetByteArrayRegion (android_java_env, java_name, - 0, length, (jbyte *) name); uri = (*android_java_env)->NewStringUTF (android_java_env, tree_uri); android_exception_check_2 (result, java_name); -- 2.39.2