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
5package org.chromium.base;
6
7import android.content.ContentResolver;
8import android.content.Context;
9import android.database.Cursor;
10import android.net.Uri;
11import android.os.ParcelFileDescriptor;
12import android.util.Log;
13
14import org.chromium.base.annotations.CalledByNative;
15
16import java.io.File;
17import java.io.FileNotFoundException;
18
19/**
20 * This class provides methods to access content URI schemes.
21 */
22public 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         */
39        Uri getContentUriFromFile(Context context, File file);
40    }
41
42    // Prevent instantiation.
43    private ContentUriUtils() {}
44
45    public static void setFileProviderUtil(FileProviderUtil util) {
46        synchronized (sLock) {
47            sFileProviderUtil = util;
48        }
49    }
50
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
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
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
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     */
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     */
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