diff options
| author | Po Lu | 2023-02-21 15:28:06 +0800 |
|---|---|---|
| committer | Po Lu | 2023-02-21 15:28:06 +0800 |
| commit | 7aa4ffddd842e495d1ae388afff12075317ecb07 (patch) | |
| tree | 9ba8cb2c232665ee59113494f7e35eeb33ab8619 | |
| parent | d197d7349121b972d50e37d66017567bf6cba652 (diff) | |
| download | emacs-7aa4ffddd842e495d1ae388afff12075317ecb07.tar.gz emacs-7aa4ffddd842e495d1ae388afff12075317ecb07.zip | |
Update Android port
* doc/emacs/android.texi (Android Startup): Document `content'
special directory.
* java/debug.sh (is_root): Improve /bin/tee detection.
* java/org/gnu/emacs/EmacsNative.java (EmacsNative): New
function `dup'.
* java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity)
(checkReadableOrCopy, onCreate): Create content directory names
when the file is not readable.
* java/org/gnu/emacs/EmacsService.java (EmacsService)
(openContentUri, checkContentUri): New functions.
* src/android.c (struct android_emacs_service): New methods.
(android_content_name_p, android_get_content_name)
(android_check_content_access): New function.
(android_fstatat, android_open): Implement opening content URIs.
(dup): Export to Java.
(android_init_emacs_service): Initialize new methods.
(android_faccessat): Implement content file names.
| -rw-r--r-- | doc/emacs/android.texi | 12 | ||||
| -rwxr-xr-x | java/debug.sh | 2 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsNative.java | 4 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsOpenActivity.java | 42 | ||||
| -rw-r--r-- | java/org/gnu/emacs/EmacsService.java | 119 | ||||
| -rw-r--r-- | src/android.c | 213 |
6 files changed, 379 insertions, 13 deletions
diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index d070199d325..f176d68ae67 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi | |||
| @@ -129,9 +129,15 @@ file, it invokes @command{emacsclient} with the options | |||
| 129 | and the name of the file being opened. Then, upon success, the focus | 129 | and the name of the file being opened. Then, upon success, the focus |
| 130 | is transferred to any open Emacs frame. | 130 | is transferred to any open Emacs frame. |
| 131 | 131 | ||
| 132 | It is sadly impossible to open certain kinds of files which are | 132 | @cindex /content directory, android |
| 133 | provided by a ``content provider''. When that is the case, a dialog | 133 | Some files are given to Emacs as ``content identifiers'', which the |
| 134 | is displayed with an explanation of the error. | 134 | system provides access to outside the normal filesystem APIs. Emacs |
| 135 | internally supports a temporary @file{/content} directory which is | ||
| 136 | used to access those files. Do not make any assumptions about the | ||
| 137 | contents of this directory, or try to open files in it yourself. | ||
| 138 | |||
| 139 | This feature is not provided on Android 4.3 and earlier, in which | ||
| 140 | case the file is copied to a temporary directory instead. | ||
| 135 | 141 | ||
| 136 | @node Android File System | 142 | @node Android File System |
| 137 | @section What files Emacs can access under Android | 143 | @section What files Emacs can access under Android |
diff --git a/java/debug.sh b/java/debug.sh index 30e5a94eee5..f07bb98ed7d 100755 --- a/java/debug.sh +++ b/java/debug.sh | |||
| @@ -281,7 +281,7 @@ else | |||
| 281 | # Upload the specified gdbserver binary to the device. | 281 | # Upload the specified gdbserver binary to the device. |
| 282 | adb -s $device push "$gdbserver" "$gdbserver_bin" | 282 | adb -s $device push "$gdbserver" "$gdbserver_bin" |
| 283 | 283 | ||
| 284 | if (adb -s $device pull /system/bin/tee /dev/null &> /dev/null); then | 284 | if adb -s $device shell ls /system/bin/tee; then |
| 285 | # Copy it to the user directory. | 285 | # Copy it to the user directory. |
| 286 | adb -s $device shell "$gdbserver_cat" | 286 | adb -s $device shell "$gdbserver_cat" |
| 287 | adb -s $device shell "run-as $package chmod +x gdbserver" | 287 | adb -s $device shell "run-as $package chmod +x gdbserver" |
diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index ef1a0e75a5c..f0917a68120 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java | |||
| @@ -31,6 +31,10 @@ public class EmacsNative | |||
| 31 | initialization. */ | 31 | initialization. */ |
| 32 | private static final String[] libraryDeps; | 32 | private static final String[] libraryDeps; |
| 33 | 33 | ||
| 34 | |||
| 35 | /* Like `dup' in C. */ | ||
| 36 | public static native int dup (int fd); | ||
| 37 | |||
| 34 | /* Obtain the fingerprint of this build of Emacs. The fingerprint | 38 | /* Obtain the fingerprint of this build of Emacs. The fingerprint |
| 35 | can be used to determine the dump file name. */ | 39 | can be used to determine the dump file name. */ |
| 36 | public static native String getFingerprint (); | 40 | public static native String getFingerprint (); |
diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java index c8501d91025..87ce454a816 100644 --- a/java/org/gnu/emacs/EmacsOpenActivity.java +++ b/java/org/gnu/emacs/EmacsOpenActivity.java | |||
| @@ -57,6 +57,8 @@ import android.os.Build; | |||
| 57 | import android.os.Bundle; | 57 | import android.os.Bundle; |
| 58 | import android.os.ParcelFileDescriptor; | 58 | import android.os.ParcelFileDescriptor; |
| 59 | 59 | ||
| 60 | import android.util.Log; | ||
| 61 | |||
| 60 | import java.io.File; | 62 | import java.io.File; |
| 61 | import java.io.FileInputStream; | 63 | import java.io.FileInputStream; |
| 62 | import java.io.FileNotFoundException; | 64 | import java.io.FileNotFoundException; |
| @@ -69,6 +71,8 @@ import java.io.UnsupportedEncodingException; | |||
| 69 | public class EmacsOpenActivity extends Activity | 71 | public class EmacsOpenActivity extends Activity |
| 70 | implements DialogInterface.OnClickListener | 72 | implements DialogInterface.OnClickListener |
| 71 | { | 73 | { |
| 74 | private static final String TAG = "EmacsOpenActivity"; | ||
| 75 | |||
| 72 | private class EmacsClientThread extends Thread | 76 | private class EmacsClientThread extends Thread |
| 73 | { | 77 | { |
| 74 | private ProcessBuilder builder; | 78 | private ProcessBuilder builder; |
| @@ -178,12 +182,16 @@ public class EmacsOpenActivity extends Activity | |||
| 178 | dialog.show (); | 182 | dialog.show (); |
| 179 | } | 183 | } |
| 180 | 184 | ||
| 181 | /* Check that the specified FILE is readable. If it is not, then | 185 | /* Check that the specified FILE is readable. If Android 4.4 or |
| 182 | copy the file in FD to a location in the system cache | 186 | later is being used, return URI formatted into a `/content/' file |
| 183 | directory and return the name of that file. */ | 187 | name. |
| 188 | |||
| 189 | If it is not, then copy the file in FD to a location in the | ||
| 190 | system cache directory and return the name of that file. */ | ||
| 184 | 191 | ||
| 185 | private String | 192 | private String |
| 186 | checkReadableOrCopy (String file, ParcelFileDescriptor fd) | 193 | checkReadableOrCopy (String file, ParcelFileDescriptor fd, |
| 194 | Uri uri) | ||
| 187 | throws IOException, FileNotFoundException | 195 | throws IOException, FileNotFoundException |
| 188 | { | 196 | { |
| 189 | File inFile; | 197 | File inFile; |
| @@ -191,12 +199,34 @@ public class EmacsOpenActivity extends Activity | |||
| 191 | InputStream stream; | 199 | InputStream stream; |
| 192 | byte buffer[]; | 200 | byte buffer[]; |
| 193 | int read; | 201 | int read; |
| 202 | String content; | ||
| 203 | |||
| 204 | Log.d (TAG, "checkReadableOrCopy: " + file); | ||
| 194 | 205 | ||
| 195 | inFile = new File (file); | 206 | inFile = new File (file); |
| 196 | 207 | ||
| 197 | if (inFile.setReadable (true)) | 208 | if (inFile.canRead ()) |
| 198 | return file; | 209 | return file; |
| 199 | 210 | ||
| 211 | Log.d (TAG, "checkReadableOrCopy: NO"); | ||
| 212 | |||
| 213 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) | ||
| 214 | { | ||
| 215 | content = "/content/" + uri.getEncodedAuthority (); | ||
| 216 | |||
| 217 | for (String segment : uri.getPathSegments ()) | ||
| 218 | content += "/" + Uri.encode (segment); | ||
| 219 | |||
| 220 | /* Append the URI query. */ | ||
| 221 | |||
| 222 | if (uri.getEncodedQuery () != null) | ||
| 223 | content += "?" + uri.getEncodedQuery (); | ||
| 224 | |||
| 225 | Log.d (TAG, "checkReadableOrCopy: " + content); | ||
| 226 | |||
| 227 | return content; | ||
| 228 | } | ||
| 229 | |||
| 200 | /* inFile is now the file being written to. */ | 230 | /* inFile is now the file being written to. */ |
| 201 | inFile = new File (getCacheDir (), inFile.getName ()); | 231 | inFile = new File (getCacheDir (), inFile.getName ()); |
| 202 | buffer = new byte[4098]; | 232 | buffer = new byte[4098]; |
| @@ -398,7 +428,7 @@ public class EmacsOpenActivity extends Activity | |||
| 398 | if (names != null) | 428 | if (names != null) |
| 399 | fileName = new String (names, "UTF-8"); | 429 | fileName = new String (names, "UTF-8"); |
| 400 | 430 | ||
| 401 | fileName = checkReadableOrCopy (fileName, fd); | 431 | fileName = checkReadableOrCopy (fileName, fd, uri); |
| 402 | } | 432 | } |
| 403 | catch (FileNotFoundException exception) | 433 | catch (FileNotFoundException exception) |
| 404 | { | 434 | { |
diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index ba6ec485d62..c9701ff2990 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java | |||
| @@ -19,7 +19,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 19 | 19 | ||
| 20 | package org.gnu.emacs; | 20 | package org.gnu.emacs; |
| 21 | 21 | ||
| 22 | import java.io.FileNotFoundException; | ||
| 22 | import java.io.IOException; | 23 | import java.io.IOException; |
| 24 | import java.io.UnsupportedEncodingException; | ||
| 25 | |||
| 23 | import java.util.List; | 26 | import java.util.List; |
| 24 | import java.util.ArrayList; | 27 | import java.util.ArrayList; |
| 25 | 28 | ||
| @@ -41,22 +44,31 @@ import android.app.Service; | |||
| 41 | 44 | ||
| 42 | import android.content.ClipboardManager; | 45 | import android.content.ClipboardManager; |
| 43 | import android.content.Context; | 46 | import android.content.Context; |
| 47 | import android.content.ContentResolver; | ||
| 44 | import android.content.Intent; | 48 | import android.content.Intent; |
| 45 | import android.content.pm.ApplicationInfo; | 49 | import android.content.pm.ApplicationInfo; |
| 46 | import android.content.pm.PackageManager.ApplicationInfoFlags; | 50 | import android.content.pm.PackageManager.ApplicationInfoFlags; |
| 47 | import android.content.pm.PackageManager; | 51 | import android.content.pm.PackageManager; |
| 48 | import android.content.res.AssetManager; | 52 | import android.content.res.AssetManager; |
| 49 | 53 | ||
| 54 | import android.database.Cursor; | ||
| 55 | import android.database.MatrixCursor; | ||
| 56 | |||
| 57 | |||
| 50 | import android.net.Uri; | 58 | import android.net.Uri; |
| 51 | 59 | ||
| 52 | import android.os.Build; | 60 | import android.os.Build; |
| 53 | import android.os.Looper; | 61 | import android.os.Looper; |
| 54 | import android.os.IBinder; | 62 | import android.os.IBinder; |
| 55 | import android.os.Handler; | 63 | import android.os.Handler; |
| 64 | import android.os.ParcelFileDescriptor; | ||
| 56 | import android.os.Vibrator; | 65 | import android.os.Vibrator; |
| 57 | import android.os.VibratorManager; | 66 | import android.os.VibratorManager; |
| 58 | import android.os.VibrationEffect; | 67 | import android.os.VibrationEffect; |
| 59 | 68 | ||
| 69 | import android.provider.DocumentsContract; | ||
| 70 | import android.provider.DocumentsContract.Document; | ||
| 71 | |||
| 60 | import android.util.Log; | 72 | import android.util.Log; |
| 61 | import android.util.DisplayMetrics; | 73 | import android.util.DisplayMetrics; |
| 62 | 74 | ||
| @@ -79,6 +91,7 @@ public class EmacsService extends Service | |||
| 79 | 91 | ||
| 80 | private EmacsThread thread; | 92 | private EmacsThread thread; |
| 81 | private Handler handler; | 93 | private Handler handler; |
| 94 | private ContentResolver resolver; | ||
| 82 | 95 | ||
| 83 | /* Keep this in synch with androidgui.h. */ | 96 | /* Keep this in synch with androidgui.h. */ |
| 84 | public static final int IC_MODE_NULL = 0; | 97 | public static final int IC_MODE_NULL = 0; |
| @@ -193,6 +206,7 @@ public class EmacsService extends Service | |||
| 193 | metrics = getResources ().getDisplayMetrics (); | 206 | metrics = getResources ().getDisplayMetrics (); |
| 194 | pixelDensityX = metrics.xdpi; | 207 | pixelDensityX = metrics.xdpi; |
| 195 | pixelDensityY = metrics.ydpi; | 208 | pixelDensityY = metrics.ydpi; |
| 209 | resolver = getContentResolver (); | ||
| 196 | 210 | ||
| 197 | try | 211 | try |
| 198 | { | 212 | { |
| @@ -643,4 +657,109 @@ public class EmacsService extends Service | |||
| 643 | window.view.setICMode (icMode); | 657 | window.view.setICMode (icMode); |
| 644 | window.view.imManager.restartInput (window.view); | 658 | window.view.imManager.restartInput (window.view); |
| 645 | } | 659 | } |
| 660 | |||
| 661 | /* Open a content URI described by the bytes BYTES, a non-terminated | ||
| 662 | string; make it writable if WRITABLE, and readable if READABLE. | ||
| 663 | Truncate the file if TRUNCATE. | ||
| 664 | |||
| 665 | Value is the resulting file descriptor or -1 upon failure. */ | ||
| 666 | |||
| 667 | public int | ||
| 668 | openContentUri (byte[] bytes, boolean writable, boolean readable, | ||
| 669 | boolean truncate) | ||
| 670 | { | ||
| 671 | String name, mode; | ||
| 672 | ParcelFileDescriptor fd; | ||
| 673 | int i; | ||
| 674 | |||
| 675 | /* Figure out the file access mode. */ | ||
| 676 | |||
| 677 | mode = ""; | ||
| 678 | |||
| 679 | if (readable) | ||
| 680 | mode += "r"; | ||
| 681 | |||
| 682 | if (writable) | ||
| 683 | mode += "w"; | ||
| 684 | |||
| 685 | if (truncate) | ||
| 686 | mode += "t"; | ||
| 687 | |||
| 688 | /* Try to open an associated ParcelFileDescriptor. */ | ||
| 689 | |||
| 690 | try | ||
| 691 | { | ||
| 692 | /* The usual file name encoding question rears its ugly head | ||
| 693 | again. */ | ||
| 694 | name = new String (bytes, "UTF-8"); | ||
| 695 | Log.d (TAG, "openContentUri: " + Uri.parse (name)); | ||
| 696 | |||
| 697 | fd = resolver.openFileDescriptor (Uri.parse (name), mode); | ||
| 698 | |||
| 699 | /* Use detachFd on newer versions of Android or plain old | ||
| 700 | dup. */ | ||
| 701 | |||
| 702 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) | ||
| 703 | { | ||
| 704 | i = fd.detachFd (); | ||
| 705 | fd.close (); | ||
| 706 | |||
| 707 | return i; | ||
| 708 | } | ||
| 709 | else | ||
| 710 | { | ||
| 711 | i = EmacsNative.dup (fd.getFd ()); | ||
| 712 | fd.close (); | ||
| 713 | |||
| 714 | return i; | ||
| 715 | } | ||
| 716 | } | ||
| 717 | catch (Exception exception) | ||
| 718 | { | ||
| 719 | return -1; | ||
| 720 | } | ||
| 721 | } | ||
| 722 | |||
| 723 | public boolean | ||
| 724 | checkContentUri (byte[] string, boolean readable, boolean writable) | ||
| 725 | { | ||
| 726 | String mode, name; | ||
| 727 | ParcelFileDescriptor fd; | ||
| 728 | |||
| 729 | /* Decode this into a URI. */ | ||
| 730 | |||
| 731 | try | ||
| 732 | { | ||
| 733 | /* The usual file name encoding question rears its ugly head | ||
| 734 | again. */ | ||
| 735 | name = new String (string, "UTF-8"); | ||
| 736 | Log.d (TAG, "checkContentUri: " + Uri.parse (name)); | ||
| 737 | } | ||
| 738 | catch (UnsupportedEncodingException exception) | ||
| 739 | { | ||
| 740 | name = null; | ||
| 741 | throw new RuntimeException (exception); | ||
| 742 | } | ||
| 743 | |||
| 744 | mode = "r"; | ||
| 745 | |||
| 746 | if (writable) | ||
| 747 | mode += "w"; | ||
| 748 | |||
| 749 | try | ||
| 750 | { | ||
| 751 | fd = resolver.openFileDescriptor (Uri.parse (name), mode); | ||
| 752 | fd.close (); | ||
| 753 | |||
| 754 | Log.d (TAG, "checkContentUri: YES"); | ||
| 755 | |||
| 756 | return true; | ||
| 757 | } | ||
| 758 | catch (Exception exception) | ||
| 759 | { | ||
| 760 | Log.d (TAG, "checkContentUri: NO"); | ||
| 761 | Log.d (TAG, exception.toString ()); | ||
| 762 | return false; | ||
| 763 | } | ||
| 764 | } | ||
| 646 | }; | 765 | }; |
diff --git a/src/android.c b/src/android.c index fc5e6d278ed..75a97f9db33 100644 --- a/src/android.c +++ b/src/android.c | |||
| @@ -108,6 +108,8 @@ struct android_emacs_service | |||
| 108 | jmethodID restart_emacs; | 108 | jmethodID restart_emacs; |
| 109 | jmethodID update_ic; | 109 | jmethodID update_ic; |
| 110 | jmethodID reset_ic; | 110 | jmethodID reset_ic; |
| 111 | jmethodID open_content_uri; | ||
| 112 | jmethodID check_content_uri; | ||
| 111 | }; | 113 | }; |
| 112 | 114 | ||
| 113 | struct android_emacs_pixmap | 115 | struct android_emacs_pixmap |
| @@ -941,6 +943,102 @@ android_get_asset_name (const char *filename) | |||
| 941 | return NULL; | 943 | return NULL; |
| 942 | } | 944 | } |
| 943 | 945 | ||
| 946 | /* Return whether or not the specified FILENAME actually resolves to a | ||
| 947 | content resolver URI. */ | ||
| 948 | |||
| 949 | static bool | ||
| 950 | android_content_name_p (const char *filename) | ||
| 951 | { | ||
| 952 | return (!strcmp (filename, "/content") | ||
| 953 | || !strncmp (filename, "/content/", | ||
| 954 | sizeof "/content/" - 1)); | ||
| 955 | } | ||
| 956 | |||
| 957 | /* Return the content URI corresponding to a `/content' file name, | ||
| 958 | or NULL if it is not a content URI. | ||
| 959 | |||
| 960 | This function is not reentrant. */ | ||
| 961 | |||
| 962 | static const char * | ||
| 963 | android_get_content_name (const char *filename) | ||
| 964 | { | ||
| 965 | static char buffer[PATH_MAX + 1]; | ||
| 966 | char *head, *token, *saveptr, *copy; | ||
| 967 | size_t n; | ||
| 968 | |||
| 969 | n = PATH_MAX; | ||
| 970 | |||
| 971 | /* First handle content ``URIs'' without a provider. */ | ||
| 972 | |||
| 973 | if (!strcmp (filename, "/content") | ||
| 974 | || !strcmp (filename, "/content/")) | ||
| 975 | return "content://"; | ||
| 976 | |||
| 977 | /* Next handle ordinary file names. */ | ||
| 978 | |||
| 979 | if (strncmp (filename, "/content/", sizeof "/content/" - 1)) | ||
| 980 | return NULL; | ||
| 981 | |||
| 982 | /* Forward past the first directory specifying the schema. */ | ||
| 983 | |||
| 984 | copy = xstrdup (filename + sizeof "/content"); | ||
| 985 | token = saveptr = NULL; | ||
| 986 | head = stpcpy (buffer, "content:/"); | ||
| 987 | |||
| 988 | /* Split FILENAME by slashes. */ | ||
| 989 | |||
| 990 | while ((token = strtok_r (!token ? copy : NULL, | ||
| 991 | "/", &saveptr))) | ||
| 992 | { | ||
| 993 | head = stpncpy (head, "/", n--); | ||
| 994 | head = stpncpy (head, token, n); | ||
| 995 | assert ((head - buffer) >= PATH_MAX); | ||
| 996 | |||
| 997 | n = PATH_MAX - (head - buffer); | ||
| 998 | } | ||
| 999 | |||
| 1000 | /* Make sure the given buffer ends up NULL terminated. */ | ||
| 1001 | buffer[PATH_MAX] = '\0'; | ||
| 1002 | xfree (copy); | ||
| 1003 | |||
| 1004 | return buffer; | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | /* Return whether or not the specified FILENAME is an accessible | ||
| 1008 | content URI. MODE specifies what to check. */ | ||
| 1009 | |||
| 1010 | static bool | ||
| 1011 | android_check_content_access (const char *filename, int mode) | ||
| 1012 | { | ||
| 1013 | const char *name; | ||
| 1014 | jobject string; | ||
| 1015 | size_t length; | ||
| 1016 | jboolean rc; | ||
| 1017 | |||
| 1018 | name = android_get_content_name (filename); | ||
| 1019 | length = strlen (name); | ||
| 1020 | |||
| 1021 | string = (*android_java_env)->NewByteArray (android_java_env, | ||
| 1022 | length); | ||
| 1023 | android_exception_check (); | ||
| 1024 | |||
| 1025 | (*android_java_env)->SetByteArrayRegion (android_java_env, | ||
| 1026 | string, 0, length, | ||
| 1027 | (jbyte *) name); | ||
| 1028 | rc = (*android_java_env)->CallBooleanMethod (android_java_env, | ||
| 1029 | emacs_service, | ||
| 1030 | service_class.check_content_uri, | ||
| 1031 | string, | ||
| 1032 | (jboolean) ((mode & R_OK) | ||
| 1033 | != 0), | ||
| 1034 | (jboolean) ((mode & W_OK) | ||
| 1035 | != 0)); | ||
| 1036 | android_exception_check (); | ||
| 1037 | ANDROID_DELETE_LOCAL_REF (string); | ||
| 1038 | |||
| 1039 | return rc; | ||
| 1040 | } | ||
| 1041 | |||
| 944 | /* Like fstat. However, look up the asset corresponding to the file | 1042 | /* Like fstat. However, look up the asset corresponding to the file |
| 945 | descriptor. If it exists, return the right information. */ | 1043 | descriptor. If it exists, return the right information. */ |
| 946 | 1044 | ||
| @@ -976,6 +1074,7 @@ android_fstatat (int dirfd, const char *restrict pathname, | |||
| 976 | AAsset *asset_desc; | 1074 | AAsset *asset_desc; |
| 977 | const char *asset; | 1075 | const char *asset; |
| 978 | const char *asset_dir; | 1076 | const char *asset_dir; |
| 1077 | int fd, rc; | ||
| 979 | 1078 | ||
| 980 | /* Look up whether or not DIRFD belongs to an open struct | 1079 | /* Look up whether or not DIRFD belongs to an open struct |
| 981 | android_dir. */ | 1080 | android_dir. */ |
| @@ -1027,6 +1126,23 @@ android_fstatat (int dirfd, const char *restrict pathname, | |||
| 1027 | return 0; | 1126 | return 0; |
| 1028 | } | 1127 | } |
| 1029 | 1128 | ||
| 1129 | if (dirfd == AT_FDCWD | ||
| 1130 | && android_init_gui | ||
| 1131 | && android_content_name_p (pathname)) | ||
| 1132 | { | ||
| 1133 | /* This is actually a content:// URI. Open that file and call | ||
| 1134 | stat on it. */ | ||
| 1135 | |||
| 1136 | fd = android_open (pathname, O_RDONLY, 0); | ||
| 1137 | |||
| 1138 | if (fd < 0) | ||
| 1139 | return -1; | ||
| 1140 | |||
| 1141 | rc = fstat (fd, statbuf); | ||
| 1142 | android_close (fd); | ||
| 1143 | return rc; | ||
| 1144 | } | ||
| 1145 | |||
| 1030 | return fstatat (dirfd, pathname, statbuf, flags); | 1146 | return fstatat (dirfd, pathname, statbuf, flags); |
| 1031 | } | 1147 | } |
| 1032 | 1148 | ||
| @@ -1316,6 +1432,8 @@ android_open (const char *filename, int oflag, int mode) | |||
| 1316 | AAsset *asset; | 1432 | AAsset *asset; |
| 1317 | int fd; | 1433 | int fd; |
| 1318 | off_t out_start, out_length; | 1434 | off_t out_start, out_length; |
| 1435 | size_t length; | ||
| 1436 | jobject string; | ||
| 1319 | 1437 | ||
| 1320 | if (asset_manager && (name = android_get_asset_name (filename))) | 1438 | if (asset_manager && (name = android_get_asset_name (filename))) |
| 1321 | { | 1439 | { |
| @@ -1329,7 +1447,7 @@ android_open (const char *filename, int oflag, int mode) | |||
| 1329 | 1447 | ||
| 1330 | if (oflag & O_DIRECTORY) | 1448 | if (oflag & O_DIRECTORY) |
| 1331 | { | 1449 | { |
| 1332 | errno = EINVAL; | 1450 | errno = ENOTSUP; |
| 1333 | return -1; | 1451 | return -1; |
| 1334 | } | 1452 | } |
| 1335 | 1453 | ||
| @@ -1396,7 +1514,7 @@ android_open (const char *filename, int oflag, int mode) | |||
| 1396 | /* Fill in some information that will be reported to | 1514 | /* Fill in some information that will be reported to |
| 1397 | callers of android_fstat, among others. */ | 1515 | callers of android_fstat, among others. */ |
| 1398 | android_table[fd].statb.st_mode | 1516 | android_table[fd].statb.st_mode |
| 1399 | = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;; | 1517 | = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; |
| 1400 | 1518 | ||
| 1401 | /* Owned by root. */ | 1519 | /* Owned by root. */ |
| 1402 | android_table[fd].statb.st_uid = 0; | 1520 | android_table[fd].statb.st_uid = 0; |
| @@ -1411,6 +1529,64 @@ android_open (const char *filename, int oflag, int mode) | |||
| 1411 | return fd; | 1529 | return fd; |
| 1412 | } | 1530 | } |
| 1413 | 1531 | ||
| 1532 | if (android_init_gui && android_content_name_p (filename)) | ||
| 1533 | { | ||
| 1534 | /* This is a content:// URI. Ask the system for a descriptor to | ||
| 1535 | that file. */ | ||
| 1536 | |||
| 1537 | name = android_get_content_name (filename); | ||
| 1538 | length = strlen (name); | ||
| 1539 | |||
| 1540 | /* Check if the mode is valid. */ | ||
| 1541 | |||
| 1542 | if (oflag & O_DIRECTORY) | ||
| 1543 | { | ||
| 1544 | errno = ENOTSUP; | ||
| 1545 | return -1; | ||
| 1546 | } | ||
| 1547 | |||
| 1548 | /* Allocate a buffer to hold the file name. */ | ||
| 1549 | string = (*android_java_env)->NewByteArray (android_java_env, | ||
| 1550 | length); | ||
| 1551 | if (!string) | ||
| 1552 | { | ||
| 1553 | (*android_java_env)->ExceptionClear (android_java_env); | ||
| 1554 | errno = ENOMEM; | ||
| 1555 | return -1; | ||
| 1556 | } | ||
| 1557 | (*android_java_env)->SetByteArrayRegion (android_java_env, | ||
| 1558 | string, 0, length, | ||
| 1559 | (jbyte *) name); | ||
| 1560 | |||
| 1561 | /* Try to open the file descriptor. */ | ||
| 1562 | |||
| 1563 | fd | ||
| 1564 | = (*android_java_env)->CallIntMethod (android_java_env, | ||
| 1565 | emacs_service, | ||
| 1566 | service_class.open_content_uri, | ||
| 1567 | string, | ||
| 1568 | (jboolean) ((mode & O_WRONLY | ||
| 1569 | || mode & O_RDWR) | ||
| 1570 | != 0), | ||
| 1571 | (jboolean) !(mode & O_WRONLY), | ||
| 1572 | (jboolean) ((mode & O_TRUNC) | ||
| 1573 | != 0)); | ||
| 1574 | |||
| 1575 | if ((*android_java_env)->ExceptionCheck (android_java_env)) | ||
| 1576 | { | ||
| 1577 | (*android_java_env)->ExceptionClear (android_java_env); | ||
| 1578 | errno = ENOMEM; | ||
| 1579 | ANDROID_DELETE_LOCAL_REF (string); | ||
| 1580 | return -1; | ||
| 1581 | } | ||
| 1582 | |||
| 1583 | if (mode & O_CLOEXEC) | ||
| 1584 | android_close_on_exec (fd); | ||
| 1585 | |||
| 1586 | ANDROID_DELETE_LOCAL_REF (string); | ||
| 1587 | return fd; | ||
| 1588 | } | ||
| 1589 | |||
| 1414 | return open (filename, oflag, mode); | 1590 | return open (filename, oflag, mode); |
| 1415 | } | 1591 | } |
| 1416 | 1592 | ||
| @@ -1488,6 +1664,12 @@ android_proc_name (int fd, char *buffer, size_t size) | |||
| 1488 | #pragma GCC diagnostic ignored "-Wmissing-prototypes" | 1664 | #pragma GCC diagnostic ignored "-Wmissing-prototypes" |
| 1489 | #endif | 1665 | #endif |
| 1490 | 1666 | ||
| 1667 | JNIEXPORT jint JNICALL | ||
| 1668 | NATIVE_NAME (dup) (JNIEnv *env, jobject object, jint fd) | ||
| 1669 | { | ||
| 1670 | return dup (fd); | ||
| 1671 | } | ||
| 1672 | |||
| 1491 | JNIEXPORT jstring JNICALL | 1673 | JNIEXPORT jstring JNICALL |
| 1492 | NATIVE_NAME (getFingerprint) (JNIEnv *env, jobject object) | 1674 | NATIVE_NAME (getFingerprint) (JNIEnv *env, jobject object) |
| 1493 | { | 1675 | { |
| @@ -1795,6 +1977,10 @@ android_init_emacs_service (void) | |||
| 1795 | "(Lorg/gnu/emacs/EmacsWindow;IIII)V"); | 1977 | "(Lorg/gnu/emacs/EmacsWindow;IIII)V"); |
| 1796 | FIND_METHOD (reset_ic, "resetIC", | 1978 | FIND_METHOD (reset_ic, "resetIC", |
| 1797 | "(Lorg/gnu/emacs/EmacsWindow;I)V"); | 1979 | "(Lorg/gnu/emacs/EmacsWindow;I)V"); |
| 1980 | FIND_METHOD (open_content_uri, "openContentUri", | ||
| 1981 | "([BZZZ)I"); | ||
| 1982 | FIND_METHOD (check_content_uri, "checkContentUri", | ||
| 1983 | "([BZZ)Z"); | ||
| 1798 | #undef FIND_METHOD | 1984 | #undef FIND_METHOD |
| 1799 | } | 1985 | } |
| 1800 | 1986 | ||
| @@ -4577,7 +4763,28 @@ android_faccessat (int dirfd, const char *pathname, int mode, int flags) | |||
| 4577 | if (dirfd == AT_FDCWD | 4763 | if (dirfd == AT_FDCWD |
| 4578 | && asset_manager | 4764 | && asset_manager |
| 4579 | && (asset = android_get_asset_name (pathname))) | 4765 | && (asset = android_get_asset_name (pathname))) |
| 4580 | return !android_file_access_p (asset, mode); | 4766 | { |
| 4767 | if (android_file_access_p (asset, mode)) | ||
| 4768 | return 0; | ||
| 4769 | |||
| 4770 | /* Set errno to an appropriate value. */ | ||
| 4771 | errno = ENOENT; | ||
| 4772 | return 1; | ||
| 4773 | } | ||
| 4774 | |||
| 4775 | /* Check if pathname is actually a content resolver URI. */ | ||
| 4776 | |||
| 4777 | if (dirfd == AT_FDCWD | ||
| 4778 | && android_init_gui | ||
| 4779 | && android_content_name_p (pathname)) | ||
| 4780 | { | ||
| 4781 | if (android_check_content_access (pathname, mode)) | ||
| 4782 | return 0; | ||
| 4783 | |||
| 4784 | /* Set errno to an appropriate value. */ | ||
| 4785 | errno = ENOENT; | ||
| 4786 | return 1; | ||
| 4787 | } | ||
| 4581 | 4788 | ||
| 4582 | #if __ANDROID_API__ >= 16 | 4789 | #if __ANDROID_API__ >= 16 |
| 4583 | return faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS); | 4790 | return faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS); |