1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.gallery3d.filtershow.provider; 18 19 import android.content.ContentProvider; 20 import android.content.ContentValues; 21 import android.database.Cursor; 22 import android.database.MatrixCursor; 23 import android.net.Uri; 24 import android.os.ConditionVariable; 25 import android.os.ParcelFileDescriptor; 26 import android.provider.BaseColumns; 27 import android.provider.MediaStore; 28 import android.provider.OpenableColumns; 29 30 import java.io.File; 31 import java.io.FileNotFoundException; 32 import java.io.IOException; 33 34 public class SharedImageProvider extends ContentProvider { 35 36 private static final String LOGTAG = "SharedImageProvider"; 37 38 public static final String MIME_TYPE = "image/jpeg"; 39 public static final String AUTHORITY = 40 "com.android.gallery3d.filtershow.provider.SharedImageProvider"; 41 public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/image"); 42 public static final String PREPARE = "prepare"; 43 44 public static String LOCAL_PATH = (new File(CONTENT_URI.getPath())).getAbsolutePath(); 45 46 private final String[] mMimeStreamType = { 47 MIME_TYPE 48 }; 49 50 private static ConditionVariable mImageReadyCond = new ConditionVariable(false); 51 52 @Override delete(Uri arg0, String arg1, String[] arg2)53 public int delete(Uri arg0, String arg1, String[] arg2) { 54 return 0; 55 } 56 57 @Override getType(Uri arg0)58 public String getType(Uri arg0) { 59 return MIME_TYPE; 60 } 61 62 @Override getStreamTypes(Uri arg0, String mimeTypeFilter)63 public String[] getStreamTypes(Uri arg0, String mimeTypeFilter) { 64 return mMimeStreamType; 65 } 66 67 @Override insert(Uri uri, ContentValues values)68 public Uri insert(Uri uri, ContentValues values) { 69 if (values.containsKey(PREPARE)) { 70 if (values.getAsBoolean(PREPARE)) { 71 mImageReadyCond.close(); 72 } else { 73 mImageReadyCond.open(); 74 } 75 } 76 return null; 77 } 78 79 @Override update(Uri arg0, ContentValues arg1, String arg2, String[] arg3)80 public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) { 81 return 0; 82 } 83 84 @Override onCreate()85 public boolean onCreate() { 86 return true; 87 } 88 89 @Override query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)90 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 91 String sortOrder) { 92 String uriPath = uri.getLastPathSegment(); 93 if (uriPath == null) { 94 return null; 95 } 96 if (projection == null) { 97 projection = new String[]{ 98 BaseColumns._ID, 99 MediaStore.MediaColumns.DATA, 100 OpenableColumns.DISPLAY_NAME, 101 OpenableColumns.SIZE 102 }; 103 } 104 // If we receive a query on display name or size, 105 // we should block until the image is ready 106 mImageReadyCond.block(); 107 108 File path = new File(uriPath); 109 110 MatrixCursor cursor = new MatrixCursor(projection); 111 Object[] columns = new Object[projection.length]; 112 for (int i = 0; i < projection.length; i++) { 113 if (projection[i].equalsIgnoreCase(BaseColumns._ID)) { 114 columns[i] = 0; 115 } else if (projection[i].equalsIgnoreCase(MediaStore.MediaColumns.DATA)) { 116 columns[i] = uri; 117 } else if (projection[i].equalsIgnoreCase(OpenableColumns.DISPLAY_NAME)) { 118 columns[i] = path.getName(); 119 } else if (projection[i].equalsIgnoreCase(OpenableColumns.SIZE)) { 120 columns[i] = path.length(); 121 } 122 } 123 cursor.addRow(columns); 124 125 return cursor; 126 } 127 128 @Override openFile(Uri uri, String mode)129 public ParcelFileDescriptor openFile(Uri uri, String mode) 130 throws FileNotFoundException { 131 String uriPath = uri.getLastPathSegment(); 132 if (uriPath == null) { 133 return null; 134 } 135 // Here we need to block until the image is ready 136 mImageReadyCond.block(); 137 File path = new File(uriPath); 138 ensureValidImagePath(path); 139 int imode = 0; 140 imode |= ParcelFileDescriptor.MODE_READ_ONLY; 141 return ParcelFileDescriptor.open(path, imode); 142 } 143 144 /** 145 * Ensure that the provided file path is part of the image directory. 146 * Prevent unauthorized access to other directories by path traversal. 147 * Throw security exception for paths outside the directory. 148 * 149 * @param path The path of the file to check. This path is expected to point to the image 150 * directory. 151 * @throws SecurityException Throws SecurityException if the path is not part of the image 152 * directory. 153 * @throws FileNotFoundException Throws FileNotFoundException if there is 154 * no file associated with the given URI. 155 */ ensureValidImagePath(File path)156 private void ensureValidImagePath(File path) throws FileNotFoundException { 157 try { 158 if (!path.getCanonicalPath().startsWith(LOCAL_PATH)) { 159 throw new SecurityException( 160 "The requested file path is not part of the image directory"); 161 } 162 } catch (IOException e) { 163 throw new FileNotFoundException(e.getMessage()); 164 } 165 } 166 } 167