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.
This commit is contained in:
parent
a3fd382f3f
commit
93104cff53
3 changed files with 109 additions and 9 deletions
|
@ -921,6 +921,48 @@ invocation of app_process (through android-emacs) can
|
|||
|
||||
/* 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 @@ invocation of app_process (through android-emacs) can
|
|||
String name, mode;
|
||||
ParcelFileDescriptor fd;
|
||||
int i;
|
||||
ContentResolver resolver;
|
||||
|
||||
resolver = getUsefulContentResolver ();
|
||||
|
||||
/* Figure out the file access mode. */
|
||||
|
||||
|
@ -978,6 +1023,7 @@ invocation of app_process (through android-emacs) can
|
|||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
exception.printStackTrace ();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -994,6 +1040,11 @@ invocation of app_process (through android-emacs) can
|
|||
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 @@ invocation of app_process (through android-emacs) can
|
|||
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.
|
||||
|
|
|
@ -1601,7 +1601,7 @@ else if (EmacsWindow.this.isMapped)
|
|||
{
|
||||
ClipData data;
|
||||
ClipDescription description;
|
||||
int i, x, y;
|
||||
int i, j, x, y, itemCount;
|
||||
String type;
|
||||
Uri uri;
|
||||
EmacsActivity activity;
|
||||
|
@ -1626,11 +1626,12 @@ else if (EmacsWindow.this.isMapped)
|
|||
|
||||
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 @@ else if (type.equals (ClipDescription.MIMETYPE_TEXT_URILIST))
|
|||
{
|
||||
/* 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)
|
||||
{
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue