diff options
| author | Bjorn Bringert <bringert@android.com> | 2010-04-14 14:43:26 +0100 |
|---|---|---|
| committer | Bjorn Bringert <bringert@android.com> | 2010-08-18 15:39:52 +0100 |
| commit | a006b47298539d89dc7a06b54c070cb3e986352a (patch) | |
| tree | 73596cd05521ac6506f5811bc36f1c8ebeeb4315 /core/java | |
| parent | 7eb84256e57ce85bece610e6a01e20fa12e0f3fe (diff) | |
| download | frameworks_base-a006b47298539d89dc7a06b54c070cb3e986352a.zip frameworks_base-a006b47298539d89dc7a06b54c070cb3e986352a.tar.gz frameworks_base-a006b47298539d89dc7a06b54c070cb3e986352a.tar.bz2 | |
New API and implementation of DB and memory-backed FDs
This depends on a kernel patch that implements read(2)
in the ashmem driver.
Bug http://b/issue?id=2595601
Change-Id: Ie3b10aa471aada21812b35e63954c1b2f0a7b042
Diffstat (limited to 'core/java')
| -rw-r--r-- | core/java/android/content/res/AssetFileDescriptor.java | 92 | ||||
| -rw-r--r-- | core/java/android/database/DatabaseUtils.java | 29 | ||||
| -rw-r--r-- | core/java/android/database/sqlite/SQLiteContentHelper.java | 92 | ||||
| -rw-r--r-- | core/java/android/database/sqlite/SQLiteStatement.java | 32 | ||||
| -rw-r--r-- | core/java/android/os/MemoryFile.java | 89 | ||||
| -rw-r--r-- | core/java/android/os/ParcelFileDescriptor.java | 20 |
6 files changed, 88 insertions, 266 deletions
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java index ccb8605..01ae1da 100644 --- a/core/java/android/content/res/AssetFileDescriptor.java +++ b/core/java/android/content/res/AssetFileDescriptor.java @@ -25,8 +25,6 @@ import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.nio.channels.FileChannel; /** * File descriptor of an entry in the AssetManager. This provides your own @@ -51,7 +49,7 @@ public class AssetFileDescriptor implements Parcelable { * @param startOffset The location within the file that the asset starts. * This must be 0 if length is UNKNOWN_LENGTH. * @param length The number of bytes of the asset, or - * {@link #UNKNOWN_LENGTH if it extends to the end of the file. + * {@link #UNKNOWN_LENGTH} if it extends to the end of the file. */ public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, long length) { @@ -125,17 +123,6 @@ public class AssetFileDescriptor implements Parcelable { public void close() throws IOException { mFd.close(); } - - /** - * Checks whether this file descriptor is for a memory file. - */ - private boolean isMemoryFile() { - try { - return MemoryFile.isMemoryFile(mFd.getFileDescriptor()); - } catch (IOException e) { - return false; - } - } /** * Create and return a new auto-close input stream for this asset. This @@ -146,12 +133,6 @@ public class AssetFileDescriptor implements Parcelable { * should only call this once for a particular asset. */ public FileInputStream createInputStream() throws IOException { - if (isMemoryFile()) { - if (mLength > Integer.MAX_VALUE) { - throw new IOException("File length too large for a memory file: " + mLength); - } - return new AutoCloseMemoryFileInputStream(mFd, (int)mLength); - } if (mLength < 0) { return new ParcelFileDescriptor.AutoCloseInputStream(mFd); } @@ -280,66 +261,6 @@ public class AssetFileDescriptor implements Parcelable { super.reset(); } } - - /** - * An input stream that reads from a MemoryFile and closes it when the stream is closed. - * This extends FileInputStream just because {@link #createInputStream} returns - * a FileInputStream. All the FileInputStream methods are - * overridden to use the MemoryFile instead. - */ - private static class AutoCloseMemoryFileInputStream extends FileInputStream { - private ParcelFileDescriptor mParcelFd; - private MemoryFile mFile; - private InputStream mStream; - - public AutoCloseMemoryFileInputStream(ParcelFileDescriptor fd, int length) - throws IOException { - super(fd.getFileDescriptor()); - mParcelFd = fd; - mFile = new MemoryFile(fd.getFileDescriptor(), length, "r"); - mStream = mFile.getInputStream(); - } - - @Override - public int available() throws IOException { - return mStream.available(); - } - - @Override - public void close() throws IOException { - mParcelFd.close(); // must close ParcelFileDescriptor, not just the file descriptor, - // since it could be a subclass of ParcelFileDescriptor. - // E.g. ContentResolver.ParcelFileDescriptorInner.close() releases - // a content provider - mFile.close(); // to unmap the memory file from the address space. - mStream.close(); // doesn't actually do anything - } - - @Override - public FileChannel getChannel() { - return null; - } - - @Override - public int read() throws IOException { - return mStream.read(); - } - - @Override - public int read(byte[] buffer, int offset, int count) throws IOException { - return mStream.read(buffer, offset, count); - } - - @Override - public int read(byte[] buffer) throws IOException { - return mStream.read(buffer); - } - - @Override - public long skip(long count) throws IOException { - return mStream.skip(count); - } - } /** * An OutputStream you can create on a ParcelFileDescriptor, which will @@ -426,15 +347,4 @@ public class AssetFileDescriptor implements Parcelable { } }; - /** - * Creates an AssetFileDescriptor from a memory file. - * - * @hide - */ - public static AssetFileDescriptor fromMemoryFile(MemoryFile memoryFile) - throws IOException { - ParcelFileDescriptor fd = memoryFile.getParcelFileDescriptor(); - return new AssetFileDescriptor(fd, 0, memoryFile.length()); - } - } diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java index c07c3c6..03aa968 100644 --- a/core/java/android/database/DatabaseUtils.java +++ b/core/java/android/database/DatabaseUtils.java @@ -31,6 +31,7 @@ import android.database.sqlite.SQLiteFullException; import android.database.sqlite.SQLiteProgram; import android.database.sqlite.SQLiteStatement; import android.os.Parcel; +import android.os.ParcelFileDescriptor; import android.text.TextUtils; import android.util.Config; import android.util.Log; @@ -705,6 +706,34 @@ public class DatabaseUtils { } /** + * Utility method to run the query on the db and return the blob value in the + * first column of the first row. + * + * @return A read-only file descriptor for a copy of the blob value. + */ + public static ParcelFileDescriptor blobFileDescriptorForQuery(SQLiteDatabase db, + String query, String[] selectionArgs) { + SQLiteStatement prog = db.compileStatement(query); + try { + return blobFileDescriptorForQuery(prog, selectionArgs); + } finally { + prog.close(); + } + } + + /** + * Utility method to run the pre-compiled query and return the blob value in the + * first column of the first row. + * + * @return A read-only file descriptor for a copy of the blob value. + */ + public static ParcelFileDescriptor blobFileDescriptorForQuery(SQLiteStatement prog, + String[] selectionArgs) { + prog.bindAllArgsAsStrings(selectionArgs); + return prog.simpleQueryForBlobFileDescriptor(); + } + + /** * Reads a String out of a column in a Cursor and writes it to a ContentValues. * Adds nothing to the ContentValues if the column isn't present or if its value is null. * diff --git a/core/java/android/database/sqlite/SQLiteContentHelper.java b/core/java/android/database/sqlite/SQLiteContentHelper.java deleted file mode 100644 index 2800d86..0000000 --- a/core/java/android/database/sqlite/SQLiteContentHelper.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.database.sqlite; - -import android.content.res.AssetFileDescriptor; -import android.database.Cursor; -import android.os.MemoryFile; - -import java.io.FileNotFoundException; -import java.io.IOException; - -/** - * Some helper functions for using SQLite database to implement content providers. - * - * @hide - */ -public class SQLiteContentHelper { - - /** - * Runs an SQLite query and returns an AssetFileDescriptor for the - * blob in column 0 of the first row. If the first column does - * not contain a blob, an unspecified exception is thrown. - * - * @param db Handle to a readable database. - * @param sql SQL query, possibly with query arguments. - * @param selectionArgs Query argument values, or {@code null} for no argument. - * @return If no exception is thrown, a non-null AssetFileDescriptor is returned. - * @throws FileNotFoundException If the query returns no results or the - * value of column 0 is NULL, or if there is an error creating the - * asset file descriptor. - */ - public static AssetFileDescriptor getBlobColumnAsAssetFile(SQLiteDatabase db, String sql, - String[] selectionArgs) throws FileNotFoundException { - try { - MemoryFile file = simpleQueryForBlobMemoryFile(db, sql, selectionArgs); - if (file == null) { - throw new FileNotFoundException("No results."); - } - return AssetFileDescriptor.fromMemoryFile(file); - } catch (IOException ex) { - throw new FileNotFoundException(ex.toString()); - } - } - - /** - * Runs an SQLite query and returns a MemoryFile for the - * blob in column 0 of the first row. If the first column does - * not contain a blob, an unspecified exception is thrown. - * - * @return A memory file, or {@code null} if the query returns no results - * or the value column 0 is NULL. - * @throws IOException If there is an error creating the memory file. - */ - // TODO: make this native and use the SQLite blob API to reduce copying - private static MemoryFile simpleQueryForBlobMemoryFile(SQLiteDatabase db, String sql, - String[] selectionArgs) throws IOException { - Cursor cursor = db.rawQuery(sql, selectionArgs); - if (cursor == null) { - return null; - } - try { - if (!cursor.moveToFirst()) { - return null; - } - byte[] bytes = cursor.getBlob(0); - if (bytes == null) { - return null; - } - MemoryFile file = new MemoryFile(null, bytes.length); - file.writeBytes(bytes, 0, 0, bytes.length); - file.deactivate(); - return file; - } finally { - cursor.close(); - } - } - -} diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java index 97e39d6..14de60f 100644 --- a/core/java/android/database/sqlite/SQLiteStatement.java +++ b/core/java/android/database/sqlite/SQLiteStatement.java @@ -17,7 +17,11 @@ package android.database.sqlite; import android.database.DatabaseUtils; +import android.os.ParcelFileDescriptor; import android.os.SystemClock; +import android.util.Log; + +import java.io.IOException; import dalvik.system.BlockGuard; @@ -33,6 +37,9 @@ import dalvik.system.BlockGuard; @SuppressWarnings("deprecation") public class SQLiteStatement extends SQLiteProgram { + + private static final String TAG = "SQLiteStatement"; + private static final boolean READ = true; private static final boolean WRITE = false; @@ -150,6 +157,30 @@ public class SQLiteStatement extends SQLiteProgram } /** + * Executes a statement that returns a 1 by 1 table with a blob value. + * + * @return A read-only file descriptor for a copy of the blob value, or {@code null} + * if the value is null or could not be read for some reason. + * + * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows + */ + public ParcelFileDescriptor simpleQueryForBlobFileDescriptor() { + synchronized(this) { + long timeStart = acquireAndLock(READ); + try { + ParcelFileDescriptor retValue = native_1x1_blob_ashmem(); + mDatabase.logTimeStat(mSql, timeStart); + return retValue; + } catch (IOException ex) { + Log.e(TAG, "simpleQueryForBlobFileDescriptor() failed", ex); + return null; + } finally { + releaseAndUnlock(); + } + } + } + + /** * Called before every method in this class before executing a SQL statement, * this method does the following: * <ul> @@ -244,4 +275,5 @@ public class SQLiteStatement extends SQLiteProgram private final native long native_executeInsert(); private final native long native_1x1_long(); private final native String native_1x1_string(); + private final native ParcelFileDescriptor native_1x1_blob_ashmem() throws IOException; } diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java index a81e16b..f82702a 100644 --- a/core/java/android/os/MemoryFile.java +++ b/core/java/android/os/MemoryFile.java @@ -58,7 +58,6 @@ public class MemoryFile private int mAddress; // address of ashmem memory private int mLength; // total length of our ashmem region private boolean mAllowPurging = false; // true if our ashmem region is unpinned - private final boolean mOwnsRegion; // false if this is a ref to an existing ashmem region /** * Allocates a new ashmem region. The region is initially not purgable. @@ -70,38 +69,11 @@ public class MemoryFile public MemoryFile(String name, int length) throws IOException { mLength = length; mFD = native_open(name, length); - mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE); - mOwnsRegion = true; - } - - /** - * Creates a reference to an existing memory file. Changes to the original file - * will be available through this reference. - * Calls to {@link #allowPurging(boolean)} on the returned MemoryFile will fail. - * - * @param fd File descriptor for an existing memory file, as returned by - * {@link #getFileDescriptor()}. This file descriptor will be closed - * by {@link #close()}. - * @param length Length of the memory file in bytes. - * @param mode File mode. Currently only "r" for read-only access is supported. - * @throws NullPointerException if <code>fd</code> is null. - * @throws IOException If <code>fd</code> does not refer to an existing memory file, - * or if the file mode of the existing memory file is more restrictive - * than <code>mode</code>. - * - * @hide - */ - public MemoryFile(FileDescriptor fd, int length, String mode) throws IOException { - if (fd == null) { - throw new NullPointerException("File descriptor is null."); - } - if (!isMemoryFile(fd)) { - throw new IllegalArgumentException("Not a memory file."); + if (length > 0) { + mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE); + } else { + mAddress = 0; } - mLength = length; - mFD = fd; - mAddress = native_mmap(mFD, length, modeToProt(mode)); - mOwnsRegion = false; } /** @@ -122,7 +94,7 @@ public class MemoryFile * * @hide */ - public void deactivate() { + void deactivate() { if (!isDeactivated()) { try { native_munmap(mAddress, mLength); @@ -181,9 +153,6 @@ public class MemoryFile * @return previous value of allowPurging */ synchronized public boolean allowPurging(boolean allowPurging) throws IOException { - if (!mOwnsRegion) { - throw new IOException("Only the owner can make ashmem regions purgable."); - } boolean oldValue = mAllowPurging; if (oldValue != allowPurging) { native_pin(mFD, !allowPurging); @@ -260,28 +229,7 @@ public class MemoryFile } /** - * Gets a ParcelFileDescriptor for the memory file. See {@link #getFileDescriptor()} - * for caveats. This must be here to allow classes outside <code>android.os</code< to - * make ParcelFileDescriptors from MemoryFiles, as - * {@link ParcelFileDescriptor#ParcelFileDescriptor(FileDescriptor)} is package private. - * - * - * @return The file descriptor owned by this memory file object. - * The file descriptor is not duplicated. - * @throws IOException If the memory file has been closed. - * - * @hide - */ - public ParcelFileDescriptor getParcelFileDescriptor() throws IOException { - FileDescriptor fd = getFileDescriptor(); - return fd != null ? new ParcelFileDescriptor(fd) : null; - } - - /** - * Gets a FileDescriptor for the memory file. Note that this file descriptor - * is only safe to pass to {@link #MemoryFile(FileDescriptor,int)}). It - * should not be used with file descriptor operations that expect a file descriptor - * for a normal file. + * Gets a FileDescriptor for the memory file. * * The returned file descriptor is not duplicated. * @@ -294,17 +242,6 @@ public class MemoryFile } /** - * Checks whether the given file descriptor refers to a memory file. - * - * @throws IOException If <code>fd</code> is not a valid file descriptor. - * - * @hide - */ - public static boolean isMemoryFile(FileDescriptor fd) throws IOException { - return (native_get_size(fd) >= 0); - } - - /** * Returns the size of the memory file that the file descriptor refers to, * or -1 if the file descriptor does not refer to a memory file. * @@ -316,20 +253,6 @@ public class MemoryFile return native_get_size(fd); } - /** - * Converts a file mode string to a <code>prot</code> value as expected by - * native_mmap(). - * - * @throws IllegalArgumentException if the file mode is invalid. - */ - private static int modeToProt(String mode) { - if ("r".equals(mode)) { - return PROT_READ; - } else { - throw new IllegalArgumentException("Unsupported file mode: '" + mode + "'"); - } - } - private class MemoryInputStream extends InputStream { private int mMark = 0; diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index d853f13..c1a1809 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -153,6 +153,26 @@ public class ParcelFileDescriptor implements Parcelable { private static native int createPipeNative(FileDescriptor[] outFds); /** + * Gets a file descriptor for a read-only copy of the given data. + * + * @param data Data to copy. + * @param name Name for the shared memory area that may back the file descriptor. + * This is purely informative and may be {@code null}. + * @return A ParcelFileDescriptor. + * @throws IOException if there is an error while creating the shared memory area. + */ + public static ParcelFileDescriptor fromData(byte[] data, String name) throws IOException { + if (data == null) return null; + MemoryFile file = new MemoryFile(name, data.length); + if (data.length > 0) { + file.writeBytes(data, 0, 0, data.length); + } + file.deactivate(); + FileDescriptor fd = file.getFileDescriptor(); + return fd != null ? new ParcelFileDescriptor(fd) : null; + } + + /** * Retrieve the actual FileDescriptor associated with this object. * * @return Returns the FileDescriptor associated with this object. |
