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 /java | |
| 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.
Diffstat (limited to 'java')
| -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 |
4 files changed, 160 insertions, 7 deletions
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 | }; |