Index: base/android/java/src/org/chromium/base/ContentUriUtils.java |
diff --git a/base/android/java/src/org/chromium/base/ContentUriUtils.java b/base/android/java/src/org/chromium/base/ContentUriUtils.java |
index 5448aa0e9ebf4b30cbf138bbb77aa0705e817ef0..f1d0ac586c6a86332dc54b672ffea0606056cf58 100644 |
--- a/base/android/java/src/org/chromium/base/ContentUriUtils.java |
+++ b/base/android/java/src/org/chromium/base/ContentUriUtils.java |
@@ -4,17 +4,22 @@ |
package org.chromium.base; |
+import android.annotation.SuppressLint; |
import android.content.ContentResolver; |
import android.content.Context; |
+import android.content.res.AssetFileDescriptor; |
import android.database.Cursor; |
import android.net.Uri; |
import android.os.ParcelFileDescriptor; |
+import android.provider.DocumentsContract; |
import android.util.Log; |
+import android.webkit.MimeTypeMap; |
import org.chromium.base.annotations.CalledByNative; |
import java.io.File; |
import java.io.FileNotFoundException; |
+import java.io.IOException; |
/** |
* This class provides methods to access content URI schemes. |
@@ -67,9 +72,9 @@ public abstract class ContentUriUtils { |
*/ |
@CalledByNative |
public static int openContentUriForRead(Context context, String uriString) { |
- ParcelFileDescriptor pfd = getParcelFileDescriptor(context, uriString); |
- if (pfd != null) { |
- return pfd.detachFd(); |
+ AssetFileDescriptor afd = getAssetFileDescriptor(context, uriString); |
+ if (afd != null) { |
+ return afd.getParcelFileDescriptor().detachFd(); |
} |
return -1; |
} |
@@ -83,7 +88,19 @@ public abstract class ContentUriUtils { |
*/ |
@CalledByNative |
public static boolean contentUriExists(Context context, String uriString) { |
- return getParcelFileDescriptor(context, uriString) != null; |
+ AssetFileDescriptor asf = null; |
+ try { |
+ asf = getAssetFileDescriptor(context, uriString); |
+ return asf != null; |
+ } finally { |
+ if (asf != null) { |
agrieve
2016/12/07 16:44:54
nit: Use StreamUtil.closeQuietly(asf)
mtomasz
2016/12/08 06:36:37
Done.
|
+ try { |
+ asf.close(); |
+ } catch (IOException e) { |
+ // Closing quietly. |
+ } |
+ } |
+ } |
} |
/** |
@@ -96,8 +113,11 @@ public abstract class ContentUriUtils { |
@CalledByNative |
public static String getMimeType(Context context, String uriString) { |
ContentResolver resolver = context.getContentResolver(); |
- if (resolver == null) return null; |
Uri uri = Uri.parse(uriString); |
+ if (isVirtualDocument(uri, context)) { |
+ String[] streamTypes = resolver.getStreamTypes(uri, "*/*"); |
+ return streamTypes != null ? streamTypes[0] : null; |
+ } |
return resolver.getType(uri); |
} |
@@ -106,15 +126,35 @@ public abstract class ContentUriUtils { |
* |
* @param context {@link Context} in interest. |
* @param uriString the content URI to open. |
- * @return ParcelFileDescriptor of the content URI, or NULL if the file does not exist. |
+ * @return AssetFileDescriptor of the content URI, or NULL if the file does not exist. |
*/ |
- private static ParcelFileDescriptor getParcelFileDescriptor(Context context, String uriString) { |
+ @SuppressLint("NewApi") |
+ private static AssetFileDescriptor getAssetFileDescriptor(Context context, String uriString) { |
ContentResolver resolver = context.getContentResolver(); |
Uri uri = Uri.parse(uriString); |
- ParcelFileDescriptor pfd = null; |
try { |
- pfd = resolver.openFileDescriptor(uri, "r"); |
+ if (isVirtualDocument(uri, context)) { |
+ String[] streamTypes = resolver.getStreamTypes(uri, "*/*"); |
+ if (streamTypes != null) { |
agrieve
2016/12/07 16:44:53
should we check for empty array here as well?
mtomasz
2016/12/08 06:36:37
Done.
|
+ AssetFileDescriptor afd = |
+ resolver.openTypedAssetFileDescriptor(uri, streamTypes[0], null, null); |
+ if (afd.getStartOffset() != 0) { |
+ try { |
+ afd.close(); |
+ } catch (IOException e) { |
+ // Closing quietly. |
agrieve
2016/12/07 16:44:54
StreamUtil.closeQuietly()
mtomasz
2016/12/08 06:36:37
Done.
|
+ } |
+ throw new SecurityException("Cannot open files with non-zero offset type."); |
+ } |
+ return afd; |
+ } |
+ } else { |
+ ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r"); |
+ if (pfd != null) { |
+ return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH); |
+ } |
+ } |
} catch (FileNotFoundException e) { |
Log.w(TAG, "Cannot find content uri: " + uriString, e); |
} catch (SecurityException e) { |
@@ -124,29 +164,57 @@ public abstract class ContentUriUtils { |
} catch (IllegalStateException e) { |
Log.w(TAG, "Unknown content uri: " + uriString, e); |
} |
- return pfd; |
+ |
+ return null; |
} |
/** |
* Method to resolve the display name of a content URI. |
* |
* @param uri the content URI to be resolved. |
- * @param contentResolver the content resolver to query. |
- * @param columnField the column field to query. |
+ * @param context {@link Context} in interest. |
* @return the display name of the @code uri if present in the database |
* or an empty string otherwise. |
*/ |
- public static String getDisplayName( |
- Uri uri, ContentResolver contentResolver, String columnField) { |
- if (contentResolver == null || uri == null) return ""; |
+ @SuppressLint("NewApi") |
+ public static String getDisplayName(Uri uri, Context context, String columnField) { |
+ if (uri == null) return ""; |
+ ContentResolver contentResolver = context.getContentResolver(); |
Cursor cursor = null; |
try { |
cursor = contentResolver.query(uri, null, null, null, null); |
if (cursor != null && cursor.getCount() >= 1) { |
cursor.moveToFirst(); |
- int index = cursor.getColumnIndex(columnField); |
- if (index > -1) return cursor.getString(index); |
+ int displayNameIndex = cursor.getColumnIndex(columnField); |
+ if (displayNameIndex == -1) { |
+ return ""; |
+ } |
+ String displayName = cursor.getString(displayNameIndex); |
+ // For Virtual documents, try to modify the file extension so it's compatible |
+ // with the alternative MIME type. |
+ if (DocumentsContract.isDocumentUri(context, uri)) { |
+ int flagsIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAGS); |
+ if (flagsIndex > -1) { |
+ boolean isVirtual = |
+ (cursor.getLong(flagsIndex) |
+ & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) |
+ != 0; |
+ if (isVirtual) { |
+ String[] mimeTypes = contentResolver.getStreamTypes(uri, "*/*"); |
+ if (mimeTypes != null) { |
agrieve
2016/12/07 16:44:54
check non-empty?
mtomasz
2016/12/08 06:36:37
Done.
|
+ String ext = MimeTypeMap.getSingleton().getExtensionFromMimeType( |
+ mimeTypes[0]); |
+ if (ext != null) { |
+ // Just append, it's simpler and more secure than altering an |
+ // existing extension. |
+ displayName += "." + ext; |
+ } |
+ } |
+ } |
+ } |
+ } |
+ return displayName; |
} |
} catch (NullPointerException e) { |
// Some android models don't handle the provider call correctly. |
@@ -157,4 +225,39 @@ public abstract class ContentUriUtils { |
} |
return ""; |
} |
+ |
+ /** |
+ * Checks whether the passed Uri represents a virtual document. |
+ * |
+ * @param uri the content URI to be resolved. |
+ * @param contentResolver the content resolver to query. |
+ * @return True for virtual file, false for any other file. |
+ */ |
+ @SuppressLint("NewApi") |
agrieve
2016/12/07 16:44:54
Do you need to add Build.VERSION check somewhere b
mtomasz
2016/12/08 06:36:37
I don't think so. All we needed from the newer API
agrieve
2016/12/09 18:23:14
Depending on a compiler optimization for correctne
|
+ public static boolean isVirtualDocument(Uri uri, Context context) { |
agrieve
2016/12/07 16:44:53
Doesn't look like this is used outside of this cla
mtomasz
2016/12/08 06:36:37
Done.
|
+ if (uri == null) return false; |
agrieve
2016/12/07 16:44:53
nit: Make Uri @Nullable and state it can be null i
mtomasz
2016/12/08 06:36:37
I tried to be consistent with the rest of the file
agrieve
2016/12/09 18:23:14
Yes please. Only getDisplayName() seems to check i
|
+ if (!DocumentsContract.isDocumentUri(context, uri)) return false; |
+ ContentResolver contentResolver = context.getContentResolver(); |
+ Cursor cursor = null; |
+ try { |
+ cursor = contentResolver.query(uri, null, null, null, null); |
+ |
+ if (cursor != null && cursor.getCount() >= 1) { |
+ cursor.moveToFirst(); |
+ int index = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAGS); |
+ if (index > -1) { |
+ return (cursor.getLong(index) |
+ & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) |
+ != 0; |
+ } |
+ } |
+ } catch (NullPointerException e) { |
+ // Some android models don't handle the provider call correctly. |
+ // see crbug.com/345393 |
+ return false; |
+ } finally { |
+ if (cursor != null) cursor.close(); |
agrieve
2016/12/07 16:44:53
StreamUtil.closeQuietly()
mtomasz
2016/12/08 06:36:37
Done.
|
+ } |
+ return false; |
+ } |
} |