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
3 // found in the LICENSE file.
4 
5 package org.chromium.base;
6 
7 import android.content.ContentResolver;
8 import android.content.Context;
9 import android.database.Cursor;
10 import android.net.Uri;
11 import android.os.ParcelFileDescriptor;
12 import android.util.Log;
13 
14 import org.chromium.base.annotations.CalledByNative;
15 
16 import java.io.File;
17 import java.io.FileNotFoundException;
18 
19 /**
20  * This class provides methods to access content URI schemes.
21  */
22 public abstract class ContentUriUtils {
23     private static final String TAG = "ContentUriUtils";
24     private static FileProviderUtil sFileProviderUtil;
25 
26     // Guards access to sFileProviderUtil.
27     private static final Object sLock = new Object();
28 
29     /**
30      * Provides functionality to translate a file into a content URI for use
31      * with a content provider.
32      */
33     public interface FileProviderUtil {
34         /**
35          * Generate a content URI from the given file.
36          * @param context Application context.
37          * @param file The file to be translated.
38          */
getContentUriFromFile(Context context, File file)39         Uri getContentUriFromFile(Context context, File file);
40     }
41 
42     // Prevent instantiation.
ContentUriUtils()43     private ContentUriUtils() {}
44 
setFileProviderUtil(FileProviderUtil util)45     public static void setFileProviderUtil(FileProviderUtil util) {
46         synchronized (sLock) {
47             sFileProviderUtil = util;
48         }
49     }
50 
getContentUriFromFile(Context context, File file)51     public static Uri getContentUriFromFile(Context context, File file) {
52         synchronized (sLock) {
53             if (sFileProviderUtil != null) {
54                 return sFileProviderUtil.getContentUriFromFile(context, file);
55             }
56         }
57         return null;
58     }
59 
60     /**
61      * Opens the content URI for reading, and returns the file descriptor to
62      * the caller. The caller is responsible for closing the file desciptor.
63      *
64      * @param context {@link Context} in interest
65      * @param uriString the content URI to open
66      * @return file desciptor upon success, or -1 otherwise.
67      */
68     @CalledByNative
openContentUriForRead(Context context, String uriString)69     public static int openContentUriForRead(Context context, String uriString) {
70         ParcelFileDescriptor pfd = getParcelFileDescriptor(context, uriString);
71         if (pfd != null) {
72             return pfd.detachFd();
73         }
74         return -1;
75     }
76 
77     /**
78      * Check whether a content URI exists.
79      *
80      * @param context {@link Context} in interest.
81      * @param uriString the content URI to query.
82      * @return true if the URI exists, or false otherwise.
83      */
84     @CalledByNative
contentUriExists(Context context, String uriString)85     public static boolean contentUriExists(Context context, String uriString) {
86         return getParcelFileDescriptor(context, uriString) != null;
87     }
88 
89     /**
90      * Retrieve the MIME type for the content URI.
91      *
92      * @param context {@link Context} in interest.
93      * @param uriString the content URI to look up.
94      * @return MIME type or null if the input params are empty or invalid.
95      */
96     @CalledByNative
getMimeType(Context context, String uriString)97     public static String getMimeType(Context context, String uriString) {
98         ContentResolver resolver = context.getContentResolver();
99         if (resolver == null) return null;
100         Uri uri = Uri.parse(uriString);
101         return resolver.getType(uri);
102     }
103 
104     /**
105      * Helper method to open a content URI and returns the ParcelFileDescriptor.
106      *
107      * @param context {@link Context} in interest.
108      * @param uriString the content URI to open.
109      * @return ParcelFileDescriptor of the content URI, or NULL if the file does not exist.
110      */
getParcelFileDescriptor(Context context, String uriString)111     private static ParcelFileDescriptor getParcelFileDescriptor(Context context, String uriString) {
112         ContentResolver resolver = context.getContentResolver();
113         Uri uri = Uri.parse(uriString);
114 
115         ParcelFileDescriptor pfd = null;
116         try {
117             pfd = resolver.openFileDescriptor(uri, "r");
118         } catch (FileNotFoundException e) {
119             Log.w(TAG, "Cannot find content uri: " + uriString, e);
120         } catch (SecurityException e) {
121             Log.w(TAG, "Cannot open content uri: " + uriString, e);
122         } catch (IllegalArgumentException e) {
123             Log.w(TAG, "Unknown content uri: " + uriString, e);
124         } catch (IllegalStateException e) {
125             Log.w(TAG, "Unknown content uri: " + uriString, e);
126         }
127         return pfd;
128     }
129 
130     /**
131      * Method to resolve the display name of a content URI.
132      *
133      * @param uri the content URI to be resolved.
134      * @param contentResolver the content resolver to query.
135      * @param columnField the column field to query.
136      * @return the display name of the @code uri if present in the database
137      *  or an empty string otherwise.
138      */
getDisplayName( Uri uri, ContentResolver contentResolver, String columnField)139     public static String getDisplayName(
140             Uri uri, ContentResolver contentResolver, String columnField) {
141         if (contentResolver == null || uri == null) return "";
142         Cursor cursor = null;
143         try {
144             cursor = contentResolver.query(uri, null, null, null, null);
145 
146             if (cursor != null && cursor.getCount() >= 1) {
147                 cursor.moveToFirst();
148                 int index = cursor.getColumnIndex(columnField);
149                 if (index > -1) return cursor.getString(index);
150             }
151         } catch (NullPointerException e) {
152             // Some android models don't handle the provider call correctly.
153             // see crbug.com/345393
154             return "";
155         } finally {
156             if (cursor != null) cursor.close();
157         }
158         return "";
159     }
160 }
161