emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 93104cff532: Correctly receive files through Android DND


From: Po Lu
Subject: master 93104cff532: Correctly receive files through Android DND
Date: Sun, 15 Oct 2023 01:11:47 -0400 (EDT)

branch: master
commit 93104cff532f932bcea65d02a59c916767a31645
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    Correctly receive files through Android DND
    
    * java/org/gnu/emacs/EmacsService.java (getUsefulContentResolver)
    (getContentResolverContext): New functions which return a
    content resolver from an EmacsActivity, if at all possible.
    (openContentUri, checkContentUri): Probe or open URIs through
    such content resolvers.  Probe URIs by opening them if merely
    testing permissions fails, for DND URIs do not make
    checkCallingUriPermission return true.
    
    * java/org/gnu/emacs/EmacsWindow.java (onDragEvent): Address
    potential crash.
    
    * src/androidvfs.c (android_check_content_access): Circumvent
    JNI dynamic method dispatch.
    (android_authority_name): Guarantee NAME is never a directory.
---
 java/org/gnu/emacs/EmacsService.java | 89 +++++++++++++++++++++++++++++++++++-
 java/org/gnu/emacs/EmacsWindow.java  |  9 ++--
 src/androidvfs.c                     | 20 ++++++--
 3 files changed, 109 insertions(+), 9 deletions(-)

diff --git a/java/org/gnu/emacs/EmacsService.java 
b/java/org/gnu/emacs/EmacsService.java
index 6fa2ebb3fdb..1325cd85e9b 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -921,6 +921,48 @@ public final class EmacsService extends Service
 
   /* Content provider functions.  */
 
+  /* Return a ContentResolver capable of accessing as many files as
+     possible, namely the content resolver of the last selected
+     activity if available: only they posses the rights to access drag
+     and drop files.  */
+
+  public ContentResolver
+  getUsefulContentResolver ()
+  {
+    EmacsActivity activity;
+
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
+      /* Since the system predates drag and drop, return this resolver
+        to avoid any unforseen difficulties.  */
+      return resolver;
+
+    activity = EmacsActivity.lastFocusedActivity;
+    if (activity == null)
+      return resolver;
+
+    return activity.getContentResolver ();
+  }
+
+  /* Return a context whose ContentResolver is granted access to most
+     files, as in `getUsefulContentResolver'.  */
+
+  public Context
+  getContentResolverContext ()
+  {
+    EmacsActivity activity;
+
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
+      /* Since the system predates drag and drop, return this resolver
+        to avoid any unforseen difficulties.  */
+      return this;
+
+    activity = EmacsActivity.lastFocusedActivity;
+    if (activity == null)
+      return this;
+
+    return activity;
+  }
+
   /* Open a content URI described by the bytes BYTES, a non-terminated
      string; make it writable if WRITABLE, and readable if READABLE.
      Truncate the file if TRUNCATE.
@@ -934,6 +976,9 @@ public final class EmacsService extends Service
     String name, mode;
     ParcelFileDescriptor fd;
     int i;
+    ContentResolver resolver;
+
+    resolver = getUsefulContentResolver ();
 
     /* Figure out the file access mode.  */
 
@@ -978,6 +1023,7 @@ public final class EmacsService extends Service
       }
     catch (Exception exception)
       {
+       exception.printStackTrace ();
        return -1;
       }
   }
@@ -994,6 +1040,11 @@ public final class EmacsService extends Service
     ParcelFileDescriptor fd;
     Uri uri;
     int rc, flags;
+    Context context;
+    ContentResolver resolver;
+    ParcelFileDescriptor descriptor;
+
+    context = getContentResolverContext ();
 
     uri = Uri.parse (name);
     flags = 0;
@@ -1004,8 +1055,42 @@ public final class EmacsService extends Service
     if (writable)
       flags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
 
-    rc = checkCallingUriPermission (uri, flags);
-    return rc == PackageManager.PERMISSION_GRANTED;
+    rc = context.checkCallingUriPermission (uri, flags);
+
+    if (rc == PackageManager.PERMISSION_GRANTED)
+      return true;
+
+    /* In the event checkCallingUriPermission fails and only read
+       permissions are being verified, attempt to query the URI.  This
+       enables ascertaining whether drag and drop URIs can be
+       accessed, something otherwise not provided for.  */
+
+    descriptor = null;
+
+    try
+      {
+       resolver = context.getContentResolver ();
+        descriptor = resolver.openFileDescriptor (uri, "r");
+       return true;
+      }
+    catch (Exception exception)
+      {
+       /* Ignored.  */
+      }
+    finally
+      {
+       try
+         {
+           if (descriptor != null)
+             descriptor.close ();
+         }
+       catch (IOException exception)
+         {
+           /* Ignored.  */
+         }
+      }
+
+    return false;
   }
 
   /* Build a content file name for URI.
diff --git a/java/org/gnu/emacs/EmacsWindow.java 
b/java/org/gnu/emacs/EmacsWindow.java
index 3d2d86624a7..386eaca8c41 100644
--- a/java/org/gnu/emacs/EmacsWindow.java
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -1601,7 +1601,7 @@ public final class EmacsWindow extends EmacsHandleObject
   {
     ClipData data;
     ClipDescription description;
-    int i, x, y;
+    int i, j, x, y, itemCount;
     String type;
     Uri uri;
     EmacsActivity activity;
@@ -1626,11 +1626,12 @@ public final class EmacsWindow extends EmacsHandleObject
 
        data = event.getClipData ();
        description = data.getDescription ();
+       itemCount = data.getItemCount ();
 
        /* If there are insufficient items within the clip data,
           return false.  */
 
-       if (data.getItemCount () < 1)
+       if (itemCount < 1)
          return false;
 
        /* Search for plain text data within the clipboard.  */
@@ -1662,12 +1663,14 @@ public final class EmacsWindow extends EmacsHandleObject
              {
                /* If the item dropped is a URI, send it to the main
                   thread.  */
+
                uri = data.getItemAt (0).getUri ();
 
                /* Attempt to acquire permissions for this URI;
                   failing which, insert it as text instead.  */
 
-               if (uri.getScheme () != null
+               if (uri != null
+                   && uri.getScheme () != null
                    && uri.getScheme ().equals ("content")
                    && (activity = EmacsActivity.lastFocusedActivity) != null)
                  {
diff --git a/src/androidvfs.c b/src/androidvfs.c
index 94c5d35ed2c..f89a82cfcc6 100644
--- a/src/androidvfs.c
+++ b/src/androidvfs.c
@@ -2898,6 +2898,7 @@ android_check_content_access (const char *uri, int mode)
 {
   jobject string;
   jboolean rc, read, write;
+  jmethodID method;
 
   string = (*android_java_env)->NewStringUTF (android_java_env, uri);
   android_exception_check ();
@@ -2907,11 +2908,13 @@ android_check_content_access (const char *uri, int mode)
 
   read = (bool) (mode & R_OK || (mode == F_OK));
   write = (bool) (mode & W_OK);
+  method = service_class.check_content_uri;
 
-  rc = (*android_java_env)->CallBooleanMethod (android_java_env,
-                                              emacs_service,
-                                              service_class.check_content_uri,
-                                              string, read, write);
+  rc = (*android_java_env)->CallNonvirtualBooleanMethod (android_java_env,
+                                                        emacs_service,
+                                                        service_class.class,
+                                                        method, string, read,
+                                                        write);
   android_exception_check_1 (string);
   ANDROID_DELETE_LOCAL_REF (string);
   return rc;
@@ -3013,6 +3016,15 @@ android_authority_name (struct android_vnode *vnode, 
char *name,
       if (*name == '/')
        name++, length -= 1;
 
+      /* If the provided URI is a directory, return NULL and set errno
+        to ENOTDIR.  Content files are never directories.  */
+
+      if (name[length - 1] == '/')
+       {
+         errno = ENOTDIR;
+         return NULL;
+       }
+
       /* NAME must be a valid JNI string, so that it can be encoded
         properly.  */
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]