| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.base; | 5 package org.chromium.base; |
| 6 | 6 |
| 7 import android.content.ContentResolver; | 7 import android.content.ContentResolver; |
| 8 import android.content.Context; | 8 import android.content.Context; |
| 9 import android.content.res.AssetFileDescriptor; | 9 import android.content.res.AssetFileDescriptor; |
| 10 import android.database.Cursor; | 10 import android.database.Cursor; |
| (...skipping 20 matching lines...) Expand all Loading... |
| 31 // Guards access to sFileProviderUtil. | 31 // Guards access to sFileProviderUtil. |
| 32 private static final Object sLock = new Object(); | 32 private static final Object sLock = new Object(); |
| 33 | 33 |
| 34 /** | 34 /** |
| 35 * Provides functionality to translate a file into a content URI for use | 35 * Provides functionality to translate a file into a content URI for use |
| 36 * with a content provider. | 36 * with a content provider. |
| 37 */ | 37 */ |
| 38 public interface FileProviderUtil { | 38 public interface FileProviderUtil { |
| 39 /** | 39 /** |
| 40 * Generate a content URI from the given file. | 40 * Generate a content URI from the given file. |
| 41 * @param context Application context. | 41 * |
| 42 * @param file The file to be translated. | 42 * @param file The file to be translated. |
| 43 */ | 43 */ |
| 44 Uri getContentUriFromFile(Context context, File file); | 44 Uri getContentUriFromFile(File file); |
| 45 } | 45 } |
| 46 | 46 |
| 47 // Prevent instantiation. | 47 // Prevent instantiation. |
| 48 private ContentUriUtils() {} | 48 private ContentUriUtils() {} |
| 49 | 49 |
| 50 public static void setFileProviderUtil(FileProviderUtil util) { | 50 public static void setFileProviderUtil(FileProviderUtil util) { |
| 51 synchronized (sLock) { | 51 synchronized (sLock) { |
| 52 sFileProviderUtil = util; | 52 sFileProviderUtil = util; |
| 53 } | 53 } |
| 54 } | 54 } |
| 55 | 55 |
| 56 public static Uri getContentUriFromFile(Context context, File file) { | 56 public static Uri getContentUriFromFile(File file) { |
| 57 synchronized (sLock) { | 57 synchronized (sLock) { |
| 58 if (sFileProviderUtil != null) { | 58 if (sFileProviderUtil != null) { |
| 59 return sFileProviderUtil.getContentUriFromFile(context, file); | 59 return sFileProviderUtil.getContentUriFromFile(file); |
| 60 } | 60 } |
| 61 } | 61 } |
| 62 return null; | 62 return null; |
| 63 } | 63 } |
| 64 | 64 |
| 65 /** | 65 /** |
| 66 * Opens the content URI for reading, and returns the file descriptor to | 66 * Opens the content URI for reading, and returns the file descriptor to |
| 67 * the caller. The caller is responsible for closing the file desciptor. | 67 * the caller. The caller is responsible for closing the file descriptor. |
| 68 * | 68 * |
| 69 * @param context {@link Context} in interest | |
| 70 * @param uriString the content URI to open | 69 * @param uriString the content URI to open |
| 71 * @return file desciptor upon success, or -1 otherwise. | 70 * @return file descriptor upon success, or -1 otherwise. |
| 72 */ | 71 */ |
| 73 @CalledByNative | 72 @CalledByNative |
| 74 public static int openContentUriForRead(Context context, String uriString) { | 73 public static int openContentUriForRead(String uriString) { |
| 75 AssetFileDescriptor afd = getAssetFileDescriptor(context, uriString); | 74 AssetFileDescriptor afd = getAssetFileDescriptor(uriString); |
| 76 if (afd != null) { | 75 if (afd != null) { |
| 77 return afd.getParcelFileDescriptor().detachFd(); | 76 return afd.getParcelFileDescriptor().detachFd(); |
| 78 } | 77 } |
| 79 return -1; | 78 return -1; |
| 80 } | 79 } |
| 81 | 80 |
| 82 /** | 81 /** |
| 83 * Check whether a content URI exists. | 82 * Check whether a content URI exists. |
| 84 * | 83 * |
| 85 * @param context {@link Context} in interest. | |
| 86 * @param uriString the content URI to query. | 84 * @param uriString the content URI to query. |
| 87 * @return true if the URI exists, or false otherwise. | 85 * @return true if the URI exists, or false otherwise. |
| 88 */ | 86 */ |
| 89 @CalledByNative | 87 @CalledByNative |
| 90 public static boolean contentUriExists(Context context, String uriString) { | 88 public static boolean contentUriExists(String uriString) { |
| 91 AssetFileDescriptor asf = null; | 89 AssetFileDescriptor asf = null; |
| 92 try { | 90 try { |
| 93 asf = getAssetFileDescriptor(context, uriString); | 91 asf = getAssetFileDescriptor(uriString); |
| 94 return asf != null; | 92 return asf != null; |
| 95 } finally { | 93 } finally { |
| 96 // Do not use StreamUtil.closeQuietly here, as AssetFileDescriptor | 94 // Do not use StreamUtil.closeQuietly here, as AssetFileDescriptor |
| 97 // does not implement Closeable until KitKat. | 95 // does not implement Closeable until KitKat. |
| 98 if (asf != null) { | 96 if (asf != null) { |
| 99 try { | 97 try { |
| 100 asf.close(); | 98 asf.close(); |
| 101 } catch (IOException e) { | 99 } catch (IOException e) { |
| 102 // Closing quietly. | 100 // Closing quietly. |
| 103 } | 101 } |
| 104 } | 102 } |
| 105 } | 103 } |
| 106 } | 104 } |
| 107 | 105 |
| 108 /** | 106 /** |
| 109 * Retrieve the MIME type for the content URI. | 107 * Retrieve the MIME type for the content URI. |
| 110 * | 108 * |
| 111 * @param context {@link Context} in interest. | |
| 112 * @param uriString the content URI to look up. | 109 * @param uriString the content URI to look up. |
| 113 * @return MIME type or null if the input params are empty or invalid. | 110 * @return MIME type or null if the input params are empty or invalid. |
| 114 */ | 111 */ |
| 115 @CalledByNative | 112 @CalledByNative |
| 116 public static String getMimeType(Context context, String uriString) { | 113 public static String getMimeType(String uriString) { |
| 117 ContentResolver resolver = context.getContentResolver(); | 114 ContentResolver resolver = ContextUtils.getApplicationContext().getConte
ntResolver(); |
| 118 Uri uri = Uri.parse(uriString); | 115 Uri uri = Uri.parse(uriString); |
| 119 if (isVirtualDocument(uri, context)) { | 116 if (isVirtualDocument(uri)) { |
| 120 String[] streamTypes = resolver.getStreamTypes(uri, "*/*"); | 117 String[] streamTypes = resolver.getStreamTypes(uri, "*/*"); |
| 121 return (streamTypes != null && streamTypes.length > 0) ? streamTypes
[0] : null; | 118 return (streamTypes != null && streamTypes.length > 0) ? streamTypes
[0] : null; |
| 122 } | 119 } |
| 123 return resolver.getType(uri); | 120 return resolver.getType(uri); |
| 124 } | 121 } |
| 125 | 122 |
| 126 /** | 123 /** |
| 127 * Helper method to open a content URI and returns the ParcelFileDescriptor. | 124 * Helper method to open a content URI and returns the ParcelFileDescriptor. |
| 128 * | 125 * |
| 129 * @param context {@link Context} in interest. | |
| 130 * @param uriString the content URI to open. | 126 * @param uriString the content URI to open. |
| 131 * @return AssetFileDescriptor of the content URI, or NULL if the file does
not exist. | 127 * @return AssetFileDescriptor of the content URI, or NULL if the file does
not exist. |
| 132 */ | 128 */ |
| 133 private static AssetFileDescriptor getAssetFileDescriptor(Context context, S
tring uriString) { | 129 private static AssetFileDescriptor getAssetFileDescriptor(String uriString)
{ |
| 134 ContentResolver resolver = context.getContentResolver(); | 130 ContentResolver resolver = ContextUtils.getApplicationContext().getConte
ntResolver(); |
| 135 Uri uri = Uri.parse(uriString); | 131 Uri uri = Uri.parse(uriString); |
| 136 | 132 |
| 137 try { | 133 try { |
| 138 if (isVirtualDocument(uri, context)) { | 134 if (isVirtualDocument(uri)) { |
| 139 String[] streamTypes = resolver.getStreamTypes(uri, "*/*"); | 135 String[] streamTypes = resolver.getStreamTypes(uri, "*/*"); |
| 140 if (streamTypes != null && streamTypes.length > 0) { | 136 if (streamTypes != null && streamTypes.length > 0) { |
| 141 AssetFileDescriptor afd = | 137 AssetFileDescriptor afd = |
| 142 resolver.openTypedAssetFileDescriptor(uri, streamTyp
es[0], null); | 138 resolver.openTypedAssetFileDescriptor(uri, streamTyp
es[0], null); |
| 143 if (afd.getStartOffset() != 0) { | 139 if (afd != null && afd.getStartOffset() != 0) { |
| 144 // Do not use StreamUtil.closeQuietly here, as AssetFile
Descriptor | 140 // Do not use StreamUtil.closeQuietly here, as AssetFile
Descriptor |
| 145 // does not implement Closeable until KitKat. | 141 // does not implement Closeable until KitKat. |
| 146 try { | 142 try { |
| 147 afd.close(); | 143 afd.close(); |
| 148 } catch (IOException e) { | 144 } catch (IOException e) { |
| 149 // Closing quietly. | 145 // Closing quietly. |
| 150 } | 146 } |
| 151 throw new SecurityException("Cannot open files with non-
zero offset type."); | 147 throw new SecurityException("Cannot open files with non-
zero offset type."); |
| 152 } | 148 } |
| 153 return afd; | 149 return afd; |
| 154 } | 150 } |
| 155 } else { | 151 } else { |
| 156 ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r")
; | 152 ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r")
; |
| 157 if (pfd != null) { | 153 if (pfd != null) { |
| 158 return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.U
NKNOWN_LENGTH); | 154 return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.U
NKNOWN_LENGTH); |
| 159 } | 155 } |
| 160 } | 156 } |
| 161 } catch (FileNotFoundException e) { | 157 } catch (FileNotFoundException e) { |
| 162 Log.w(TAG, "Cannot find content uri: " + uriString, e); | 158 Log.w(TAG, "Cannot find content uri: " + uriString, e); |
| 163 } catch (SecurityException e) { | 159 } catch (SecurityException e) { |
| 164 Log.w(TAG, "Cannot open content uri: " + uriString, e); | 160 Log.w(TAG, "Cannot open content uri: " + uriString, e); |
| 165 } catch (IllegalArgumentException e) { | 161 } catch (IllegalArgumentException | IllegalStateException e) { |
| 166 Log.w(TAG, "Unknown content uri: " + uriString, e); | |
| 167 } catch (IllegalStateException e) { | |
| 168 Log.w(TAG, "Unknown content uri: " + uriString, e); | 162 Log.w(TAG, "Unknown content uri: " + uriString, e); |
| 169 } | 163 } |
| 170 | 164 |
| 171 return null; | 165 return null; |
| 172 } | 166 } |
| 173 | 167 |
| 174 /** | 168 /** |
| 175 * Method to resolve the display name of a content URI. | 169 * Method to resolve the display name of a content URI. |
| 176 * | 170 * |
| 177 * @param uri the content URI to be resolved. | 171 * @param uri the content URI to be resolved. |
| 178 * @param context {@link Context} in interest. | 172 * @param context {@link Context} in interest. |
| 179 * @param columnField the column field to query. | 173 * @param columnField the column field to query. |
| 180 * @return the display name of the @code uri if present in the database | 174 * @return the display name of the @code uri if present in the database |
| 181 * or an empty string otherwise. | 175 * or an empty string otherwise. |
| 182 */ | 176 */ |
| 183 public static String getDisplayName(Uri uri, Context context, String columnF
ield) { | 177 public static String getDisplayName(Uri uri, Context context, String columnF
ield) { |
| 184 if (uri == null) return ""; | 178 if (uri == null) return ""; |
| 185 ContentResolver contentResolver = context.getContentResolver(); | 179 ContentResolver contentResolver = context.getContentResolver(); |
| 186 Cursor cursor = null; | 180 Cursor cursor = null; |
| 187 try { | 181 try { |
| 188 cursor = contentResolver.query(uri, null, null, null, null); | 182 cursor = contentResolver.query(uri, null, null, null, null); |
| 189 | 183 |
| 190 if (cursor != null && cursor.getCount() >= 1) { | 184 if (cursor != null && cursor.getCount() >= 1) { |
| 191 cursor.moveToFirst(); | 185 cursor.moveToFirst(); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 217 } finally { | 211 } finally { |
| 218 StreamUtil.closeQuietly(cursor); | 212 StreamUtil.closeQuietly(cursor); |
| 219 } | 213 } |
| 220 return ""; | 214 return ""; |
| 221 } | 215 } |
| 222 | 216 |
| 223 /** | 217 /** |
| 224 * Checks whether the passed Uri represents a virtual document. | 218 * Checks whether the passed Uri represents a virtual document. |
| 225 * | 219 * |
| 226 * @param uri the content URI to be resolved. | 220 * @param uri the content URI to be resolved. |
| 227 * @param contentResolver the content resolver to query. | |
| 228 * @return True for virtual file, false for any other file. | 221 * @return True for virtual file, false for any other file. |
| 229 */ | 222 */ |
| 230 private static boolean isVirtualDocument(Uri uri, Context context) { | 223 private static boolean isVirtualDocument(Uri uri) { |
| 231 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return false; | 224 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return false; |
| 232 if (uri == null) return false; | 225 if (uri == null) return false; |
| 233 if (!DocumentsContract.isDocumentUri(context, uri)) return false; | 226 if (!DocumentsContract.isDocumentUri(ContextUtils.getApplicationContext(
), uri)) { |
| 234 ContentResolver contentResolver = context.getContentResolver(); | 227 return false; |
| 228 } |
| 229 ContentResolver contentResolver = ContextUtils.getApplicationContext().g
etContentResolver(); |
| 235 Cursor cursor = null; | 230 Cursor cursor = null; |
| 236 try { | 231 try { |
| 237 cursor = contentResolver.query(uri, null, null, null, null); | 232 cursor = contentResolver.query(uri, null, null, null, null); |
| 238 | 233 |
| 239 if (cursor != null && cursor.getCount() >= 1) { | 234 if (cursor != null && cursor.getCount() >= 1) { |
| 240 cursor.moveToFirst(); | 235 cursor.moveToFirst(); |
| 241 return hasVirtualFlag(cursor); | 236 return hasVirtualFlag(cursor); |
| 242 } | 237 } |
| 243 } catch (NullPointerException e) { | 238 } catch (NullPointerException e) { |
| 244 // Some android models don't handle the provider call correctly. | 239 // Some android models don't handle the provider call correctly. |
| 245 // see crbug.com/345393 | 240 // see crbug.com/345393 |
| 246 return false; | 241 return false; |
| 247 } finally { | 242 } finally { |
| 248 StreamUtil.closeQuietly(cursor); | 243 StreamUtil.closeQuietly(cursor); |
| 249 } | 244 } |
| 250 return false; | 245 return false; |
| 251 } | 246 } |
| 252 | 247 |
| 253 /** | 248 /** |
| 254 * Checks whether the passed cursor for a document has a virtual document fl
ag. | 249 * Checks whether the passed cursor for a document has a virtual document fl
ag. |
| 255 * | 250 * |
| 256 * The called must close the passed cursor. | 251 * The called must close the passed cursor. |
| 257 * | 252 * |
| 258 * @param cursor Cursor with COLUMN_FLAGS. | 253 * @param cursor Cursor with COLUMN_FLAGS. |
| 259 * @return True for virtual file, false for any other file. | 254 * @return True for virtual file, false for any other file. |
| 260 */ | 255 */ |
| 261 private static boolean hasVirtualFlag(Cursor cursor) { | 256 private static boolean hasVirtualFlag(Cursor cursor) { |
| 262 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return false; | 257 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return false; |
| 263 int index = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAG
S); | 258 int index = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAG
S); |
| 264 if (index > -1) { | 259 return index > -1 |
| 265 return (cursor.getLong(index) & DocumentsContract.Document.FLAG_VIRT
UAL_DOCUMENT) != 0; | 260 && (cursor.getLong(index) & DocumentsContract.Document.FLAG_VIRT
UAL_DOCUMENT) != 0; |
| 266 } | |
| 267 return false; | |
| 268 } | 261 } |
| 269 } | 262 } |
| OLD | NEW |