Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a006b472 authored by Bjorn Bringert's avatar Bjorn Bringert
Browse files

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
parent 7eb84256
Loading
Loading
Loading
Loading
+60 −0
Original line number Diff line number Diff line
@@ -62314,6 +62314,38 @@
<parameter name="value" type="java.lang.Object">
</parameter>
</method>
<method name="blobFileDescriptorForQuery"
 return="android.os.ParcelFileDescriptor"
 abstract="false"
 native="false"
 synchronized="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="db" type="android.database.sqlite.SQLiteDatabase">
</parameter>
<parameter name="query" type="java.lang.String">
</parameter>
<parameter name="selectionArgs" type="java.lang.String[]">
</parameter>
</method>
<method name="blobFileDescriptorForQuery"
 return="android.os.ParcelFileDescriptor"
 abstract="false"
 native="false"
 synchronized="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="prog" type="android.database.sqlite.SQLiteStatement">
</parameter>
<parameter name="selectionArgs" type="java.lang.String[]">
</parameter>
</method>
<method name="createDbFromSqlStatements"
 return="void"
 abstract="false"
@@ -66129,6 +66161,17 @@
 visibility="public"
>
</method>
<method name="simpleQueryForBlobFileDescriptor"
 return="android.os.ParcelFileDescriptor"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="simpleQueryForLong"
 return="long"
 abstract="false"
@@ -134578,6 +134621,23 @@
 visibility="public"
>
</method>
<method name="fromData"
 return="android.os.ParcelFileDescriptor"
 abstract="false"
 native="false"
 synchronized="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="data" type="byte[]">
</parameter>
<parameter name="name" type="java.lang.String">
</parameter>
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
<method name="fromSocket"
 return="android.os.ParcelFileDescriptor"
 abstract="false"
+1 −91
Original line number Diff line number Diff line
@@ -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) {
@@ -126,17 +124,6 @@ public class AssetFileDescriptor implements Parcelable {
        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
     * will either return a full asset {@link AutoCloseInputStream}, or
@@ -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);
        }
@@ -281,66 +262,6 @@ public class AssetFileDescriptor implements Parcelable {
        }
    }

    /**
     * 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
     * take care of calling {@link ParcelFileDescriptor#close
@@ -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());
    }

}
+29 −0
Original line number Diff line number Diff line
@@ -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;
@@ -704,6 +705,34 @@ public class DatabaseUtils {
        return prog.simpleQueryForString();
    }

    /**
     * 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.
+0 −92
Original line number Diff line number Diff line
/*
 * 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();
        }
    }

}
+32 −0
Original line number Diff line number Diff line
@@ -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;

@@ -149,6 +156,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:
@@ -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;
}
Loading